阻塞与非阻塞
阻塞与非阻塞区别在于,前者必须等待数据后才可继续执行。后者则是若没有数据,则立即返回继续执行。
下面为一个简单的描述阻塞IO的时序图:
缺点显而易见,每个请求都需要对应一个线程。而线程数也是存有上限的,因为内存空间有限。所以当并发量大时,可以处理请求量等价于可建线程数。
轮询检索与回调通知
-
轮询检索:对元素列表顺序迭代检索,从而得到需要的元素,复杂度为 O(n)。
-
回调通知:在元素列表中添加元素时,同时注册一个回调函数,并将就绪元素列表作为参数传递进去。当元素转为就绪态时,调用该回调方法将自身加入到就绪元素列表。元素主动性的添加,使得获取就绪元素时的复杂度降为了O(1)。
selec / poll / epoll 概述
select、poll 和 epoll 是 Linux 操作系统中用于异步 I/O 操作的三种不同机制。
select() 是一个系统调用,用于监视多个文件描述符,等待其中一个或多个变为特定类型的 I/O 操作。它操作的是一个由 fd_set 结构组成的数组,其中每个结构表示要监视的一组文件描述符。Select 是这三种机制中最基本和最便携的,但在可扩展性方面还有一些限制。
poll() 与 select() 类似,但它使用的是一个 struct pollfd 结构的数组,而不是 fd_set 结构。此外,poll() 还提供了一些额外的功能,如为轮询操作设置超时和监视文件描述符上的错误。
epoll() 在 Linux 2.5.44 中作为对 poll() 和 select() 的更可扩展替代方案引入。epoll 是一种事件驱动的I/O模型,它是Linux内核中实现高效的I/O多路复用的机制之一。使用epoll可以监控大量的文件描述符,当其中任意一个文件描述符上有读、写、异常等事件发生时,就会触发相应的回调函数来处理这些事件,从而实现高效的I/O操作。
另外,epoll还支持边沿触发(ET)和水平触发(LT)两种工作模式。在LT模式下,只要该文件描述符上有数据可读或可写,就会立即返回通知应用程序进行处理;而在ET模式下,仅当文件描述符状态发生变化时才会返回通知,这可以减少 epoll_wait 调用次数,提高效率。例如对于读事件 EPOLLIN:ET模式下,一定要及时处理干净本次的数据,否则后面不一定会继续通知导致数据丢失。而LT模式下,只要 Socket 上有未读完的数据,EPOLLIN 就会一直触发。
总结:select() 和 poll() 适用于需要监视少量文件描述符的应用程序,而 epoll() 则更适合具有高并发性和可扩展性要求的应用程序。所以,epoll 也是我们下面要详细介绍的内容。
select / poll
select / poll 对监控的Socket文件描述符采用的为轮询检索,大体流程如下:
- 应用程序向内核注册一组文件描述符以及感兴趣的事件类型(如可读、可写等)。
- 应用程序调用 select 或 poll 等待其中任意一个文件描述符上发生感兴趣的事件。
- 内核对每个注册的文件描述符都进行轮询,判断是否有相应的事件发生。如果有,则将该文件描述符标记为就绪状态,并将其返回给应用程序。
- 应用程序根据返回的就绪文件描述符进行相应的操作。
- 回到步骤 2,重复执行以上过程。
轮询的缺点很明显:当需要管理的文件描述符数量很大时,轮询的开销会非常大,导致性能降低。
关于 select / poll 的其他内容,如最大容量等、二者差异就不过多介绍,不是核心点。
epoll
epoll 对监控的Socket文件描述符采用的是回调通知的方式。epoll 是 select / poll 的增强。分为三个函数:epoll_create、epoll_ctl、epoll_wait。函数功能如下:
- epoll_create:创建一个 epoll 实例。该调用返回一个文件描述符,可以用于后续的 epoll 操作。
- epoll_ctl:将要监听的 socket 注册到 epoll 实例上。需要指定监听的socket文件描述符、监听事件类型以及对应的回调函数等信息。
- epoll_wait:等待监听的事件发生。如果有事件发生,epoll_wait() 将返回一个包含事件信息的事件列表。
epoll 在内核源码中的定义
结构体:
// fs/eventpoll.c
struct eventpoll {
// 阻塞在epoll_wait函数的用户进程等待队列
wait_queue_head_t wq;
// 就绪的 socket 文件描述符的列表 (被包装为epitem)
struct list_head rdllist;
// 存放添加的需要监听的 socket 文件描述符的红黑树 (被包装为epitem)
struct rb_root rbr;
......
}
示意图: