Linux驱动IO篇——IO多路复用

简介: Linux驱动IO篇——IO多路复用

什么是IO多路复用

简单来说,就是解决能够同时操作多个设备的方法,及时处理多个设备的数据。

具体的,是指单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力。

IO多路复用的三种方法

Linux的IO多路复用有三种方法:selectpollepoll,且这三种方法都是系统调用

这里介绍嵌入式中常用的两种selectpoll

select

相关接口

/* According to POSIX.1-2001, POSIX.1-2008 */
    #include <sys/select.h>
    /* According to earlier standards */
    #include <sys/time.h>
    #include <sys/types.h>
    #include <unistd.h>
    int select(int nfds, fd_set *readfds, fd_set *writefds,
                fd_set *exceptfds, struct timeval *timeout);
    int pselect(int nfds, fd_set *readfds, fd_set *writefds,
                fd_set *exceptfds, const struct timespec *timeout,
                const sigset_t *sigmask);
    int  FD_ISSET(int fd, fd_set *set);
    void FD_SET(int fd, fd_set *set);
 void FD_CLR(int fd, fd_set *set);
    void FD_ZERO(fd_set *set);

select把要监视的文件描述符分成三类:readfdswritefdsexceptfds`。

readfds是需要进行读操作的文件描述符,writefds是需要进行写操作的文件描述符,exceptfds是需要进行异常处理的文件描述符。对于不需要监听的,填NULL即可。

第一次对n个文件进行select()的时候,若任何一个文件满足要求,select()就直接返回;第2次再进行select()的时候,没有文件满足读写要求,select()的进程阻塞且睡眠。示意图如下:

fd_set是文件描述符组,需要通过FD_XX系列的函数来操作。例如:

FD_ZERO:清空文件描述符组

fd_set writefds;
FD_ZERO(&writefds)

FD_SET:添加一个文件描述符到组中

FD_CLR:删除一个组中的文件描述符

FD_SET(fd, &writefds);//将fd加进组中
FD_CLR(fd, &writefds);//将fd从组中删除

FD_ISSET:检测一个文件描述符是否在组中,我们用这个来检测一次select调用之后有哪些文件描述符可以进行IO操作

if (FD_ISSET(fd, &readfds)){
  /* fd可读 */
}

poll

#include <poll.h>
int poll(struct pollfd fds[], nfds_t nfds, int timeout);

poll()函数也是一个系统调用,可实现类似select()的效果。与select()不同的是,select将要监听的文件描述符分为读、写、异常三种,而poll则使用一个文件描述符集struct pollfd fds[]来管理。

struct pollfd用来描述一个需要监听的文件描述符,传参时传入struct pollfd类型数组fds

参数说明

参数 说明
fds 一个struct pollfd结构类型的数组,用于存放需要检测其状态的文件描述符集;
nfds 用于标记数组fds中的结构体元素的总数量
timeout 阻塞的时间,单位:毫秒;如果timeout==0,那么poll() 函数立即返回而不阻塞,如果设置为负数,那么poll() 函数会一直阻塞下去,直到所检测的文件描述符上的感兴趣的事件发生是才返回。

返回值

  • >0:数组fds中准备好读、写或出错状态的那些文件描述符的总数量
  • ==0:代表poll超时
  • -1:代表poll函数调用失败,同时会自动设置全局变量errno

struct pollfd结构体

struct pollfd{
    int fd; /*文件描述符*/
    short events; /* 等待的需要测试事件 */
    short revents; /* 实际发生了的事件,也就是返回结果 */
}

events和revents事件值

常量 说明
POLLIN 普通或优先级带数据可读
POLLRDNORM 普通数据可读
POLLRDBAND 优先级带数据可读
POLLPRI 高优先级数据可读
POLLOUT 普通数据可写
POLLWRNORM 普通数据可写
POLLWRBAND 优先级带数据可写
POLLERR 发生错误
POLLHUP 发生挂起
POLLNVAL 描述字不是一个打开的文件

poll系统调用过程

poll系统调用在内核中的入口函数是sys_poll()sys_poll()的系统调用关系如下:

关键点是在vfs_poll()函数:

//include/linux/poll.h
static inline __poll_t vfs_poll(struct file *file, struct poll_table_struct *pt)
{
 if (unlikely(!file->f_op->poll))
  return DEFAULT_POLLMASK;
 return file->f_op->poll(file, pt);//调用到驱动中的poll函数
}

file->f_op->poll(file,pt)会调用到驱动中的poll函数.

驱动中poll机制实现

驱动中只需要实现struct file_operations结构体里的poll函数,代码如下:

static unsigned int my_poll(struct file *fp, struct poll_table_struct *wait)
{
 unsigned int mask = 0;
    poll_wait(fp, &btn_waitq, wait);//将驱动中的等待队列btn_waitq添加进等待队列表table中
    if(ev_press)
         mask |= POLLIN|POLLRDNORM;
 return mask;
}
static struct file_operations my_fops = {
    .owner = THIS_MODULE,
    ......
    .poll = my_poll,
};

应用程序调用poll()时,内核中会调用每个设备驱动中的poll函数,这些底层函数都会调用poll_wait()将本设备驱动中的等待队列添加到一个等待队列表中(table)

如果监控设备没有事件发生,在调用完所有要监控设备驱动的poll函数后,进程会休眠(table)

如果任何一个监控的设备有事件发生,内核中的poll又会重新带调用每个设备驱动中的poll方法。看看有多少个设备有事件发生,然后才返回到应用层。

end

猜你喜欢

Linux驱动IO篇——阻塞/非阻塞IO

Linux驱动IO篇——ioctl设备操作

一个Linux驱动工程师必知的内核模块知识

Linux内核中常用的数据结构和算法

Linux内核中常用的C语言技巧

Linux内核基础篇——常用调试技巧汇总

Linux内核基础篇——动态输出调试

Linux内核基础篇——printk调试

Linux内核基础篇——initcall

相关文章
|
7月前
|
监控 应用服务中间件 Linux
掌握并发模型:深度揭露网络IO复用并发模型的原理。
总结,网络 I/O 复用并发模型通过实现非阻塞 I/O、引入 I/O 复用技术如 select、poll 和 epoll,以及采用 Reactor 模式等技巧,为多任务并发提供了有效的解决方案。这样的模型有效提高了系统资源利用率,以及保证了并发任务的高效执行。在现实中,这种模型在许多网络应用程序和分布式系统中都取得了很好的应用成果。
213 35
|
5月前
|
监控 Linux 开发者
理解Linux操作系统内核中物理设备驱动(phy driver)的功能。
综合来看,物理设备驱动在Linux系统中的作用是至关重要的,它通过与硬件设备的紧密配合,为上层应用提供稳定可靠的通信基础设施。开发一款优秀的物理设备驱动需要开发者具备深厚的硬件知识、熟练的编程技能以及对Linux内核架构的深入理解,以确保驱动程序能在不同的硬件平台和网络条件下都能提供最优的性能。
278 0
|
存储 Linux C语言
Linux C/C++之IO多路复用(aio)
这篇文章介绍了Linux中IO多路复用技术epoll和异步IO技术aio的区别、执行过程、编程模型以及具体的编程实现方式。
625 1
Linux C/C++之IO多路复用(aio)
|
6月前
|
Linux C语言 网络架构
Linux的基础IO内容补充-FILE
而当我们将运行结果重定向到log.txt文件时,数据的刷新策略就变为了全缓冲,此时我们使用printf和fwrite函数打印的数据都打印到了C语言自带的缓冲区当中,之后当我们使用fork函数创建子进程时,由于进程间具有独立性,而之后当父进程或是子进程对要刷新缓冲区内容时,本质就是对父子进程共享的数据进行了修改,此时就需要对数据进行写时拷贝,至此缓冲区当中的数据就变成了两份,一份父进程的,一份子进程的,所以重定向到log.txt文件当中printf和fwrite函数打印的数据就有两份。此时我们就可以知道,
103 0
|
6月前
|
存储 Linux Shell
Linux的基础IO
那么,这里我们温习一下操作系统的概念我们在Linux平台下运行C代码时,C库函数就是对Linux系统调用接口进行的封装,在Windows平台下运行C代码时,C库函数就是对Windows系统调用接口进行的封装,这样做使得语言有了跨平台性,也方便进行二次开发。这就是因为在根本上操作系统确实像银行一样,并不完全信任用户程序,因为直接开放底层资源(如内存、磁盘、硬件访问权限)给用户程序会带来巨大的风险。所以就向银行一样他的服务是由工作人员隔着一层玻璃,然后对顾客进行服务的。
86 0
|
10月前
|
存储 网络协议 Linux
【Linux】进程IO|系统调用|open|write|文件描述符fd|封装|理解一切皆文件
本文详细介绍了Linux中的进程IO与系统调用,包括 `open`、`write`、`read`和 `close`函数及其用法,解释了文件描述符(fd)的概念,并深入探讨了Linux中的“一切皆文件”思想。这种设计极大地简化了系统编程,使得处理不同类型的IO设备变得更加一致和简单。通过本文的学习,您应该能够更好地理解和应用Linux中的进程IO操作,提高系统编程的效率和能力。
426 34
|
9月前
|
SQL druid Java
【YashanDB知识库】YashanDB JDBC驱动查询时抛出io fail:Read timed out异常
【YashanDB知识库】YashanDB JDBC驱动查询时抛出io fail:Read timed out异常
|
9月前
|
SQL druid Java
【YashanDB知识库】YashanDB JDBC驱动查询时抛出io fail:Read timed out异常
【YashanDB知识库】YashanDB JDBC驱动查询时抛出io fail:Read timed out异常
|
12月前
|
Linux API C语言
Linux基础IO
Linux基础IO操作是系统管理和开发的基本技能。通过掌握文件描述符、重定向与管道、性能分析工具、文件系统操作以及网络IO命令等内容,可以更高效地进行系统操作和脚本编写。希望本文提供的知识和示例能帮助读者更深入地理解和运用Linux IO操作。
238 14