20-IO多路复用 epoll

从内核中最简单的驱动程序入手,描述Linux驱动开发,主要文章目录如下(持续更新中):
01 - 第一个内核模块程序
02 - 注册字符设备驱动
03 - open & close 函数的应用
04 - read & write 函数的应用
05 - ioctl 的应用
06 - ioctl LED灯硬件分析
07 - ioctl 控制LED软件实现(寄存器操作)
08 - ioctl 控制LED软件实现(库函数操作)
09 - 注册字符设备的另一种方法(常用)
10 - 一个cdev实现对多个设备的支持
11 - 四个cdev控制四个LED设备
12 - 虚拟串口驱动
13 - I2C驱动
14 - SPI协议及驱动讲解
15 - SPI Linux驱动代码实现
16 - 非阻塞型I/O
17 - 阻塞型I/O
18 - I/O多路复用之 select
19 - I/O多路复用之 poll
20 - I/O多路复用之 epoll
21 - 异步通知

1. epoll简介

 阻塞型IO相对于非阻塞型IO来说,最大的优点是资源不可用是进程主动放弃CPU让其他的进程运行,而不用不停的轮询,提高系统的效率,但是缺点也是比较明显的就是进程阻塞之后不能做其他的事情,这在一个进程要同时对多个设备进行操作时非常不便。解决这个问题的方法有很多,比如多进程、多线程和I/O多路复用,I/O复用有select、poll和Linux所持有的epoll三种方式,select、poll和epoll可以用于处理轮询,应用程序通过 select、epoll 或 poll 函数来查询设备是否可以操作,如果可以操作的话就从设备读取或者向设备写入数据。当应用程序调用 select、epoll 或 poll 函数的时候设备驱动程序中的 poll 函数就会执行,因此需要在设备驱动程序中编写 poll 函数。本节来说明epoll的用法。
 在传统的 selcet 和 poll 函数都会随着所监听的 fd 数量的增加,出现效率低下的问题,而且poll 函数每次必须遍历所有的描述符来检查就绪的描述符,这个过程很浪费时间。为此,epoll因运而生,epoll 就是为处理大并发而准备的,一般常常在网络编程中使用 epoll 函数。

2. epoll接口函数

2.1 应用层接口函数

 应用程序需要先使用 epoll_create 函数创建一个 epoll 句柄,epoll_create 函数原型如下:

原    型: int epoll_create(int size)
功    能: 创建一个 epoll 句柄
@param1: 从 Linux2.6.8 开始此参数已经没有意义了,随便填写一个大于 0 的值就可以
		需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽
@return: epoll句柄,如果为-1的话表示创建失败

 epoll 句柄创建成功以后使用 epoll_ctl 函数向其中添加要监视的文件描述符以及监视的事件,epoll_ctl 函数原型如下所示:

原    型: int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功    能: 用于控制某个epoll文件描述符上的事件,可以注册事件,修改事件,删除事件
@param1: 由epoll_create生成的epoll句柄
@param2: 要对epfd(epoll句柄)进行的操作,可以设置为
			EPOLL_CTL_ADD  		// 向epfd添加文件参数fd表示的描述符
			EPOLL_CTL_MOD  		// 修改参数fd的event事件
			EPOLL_CTL_DEL 		// 从epfd中删除fd描述符
@param3: 要监视的文件描述符
@param4: 要监视的事件类型
			struct epoll_event {
				__u32 events;	// epoll事件
				__u64 data;		// 用户数据
			} EPOLL_PACKED;
		 可选的事件如下所示:
			EPOLLIN  			// 触发该事件,表示对应的文件描述符上有可读数据
			EPOLLOUT 			// 触发该事件,表示对应的文件描述符上可以写数据
			EPOLLPRI 			// 表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来)
			EPOLLERR 			// 指定的文件描述符发生错误
			EPOLLHUP 			// 指定的文件描述符挂起
			EPOLLET  			// 设置 epoll 为边沿触发,默认触发模式为水平触发。
			EPOLLONESHOT 		// 一次性的监视,当监视完成以后还需要再次监视某个 fd,那么就需要将fd 重新添加到 epoll 里面。
		注:上面这些事件可以进行“或”操作,也就是说可以设置监视多个事件。
@return: 成功返回0,失败返回-1

2.2 驱动中接口函数

 一切都设置好以后应用程序就可以通过 epoll_wait 函数来等待事件的发生,类似 select 函数。epoll_wait 函数原型如下所示:

原    型: int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
功    能: 用于轮询I/O事件的发生
@param1: 要等待的epoll,由epoll_create生成的epoll句柄
@param2: 指向 epoll_event 结构体的数组,当有事件发生的时候 Linux 内核会填写 events,调用者可以根据 events 判断发生了哪些事件
@param3: 每次能处理的事件数,events数组大小,必须大于0
@param4: 超时时间,单位为 ms
@return: 0表示超时;-1表示错误;其他值表示准备就绪的文件描述符数量

epoll 更多的是用在大规模的并发服务器上,因为在这种场合下 select 和 poll 并不适合。当设计到的文件描述符(fd)比较少的时候就适合用 selcet 和 poll

发布了57 篇原创文章 · 获赞 64 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_36310253/article/details/103146125