参考
原理分析
select原理
图片来源: 游戏研究院
select将进程信息保存在每个需要监视的fd的等待列表中,当任何一个fd有读写事件发生,都会触发中断处理程序进行处理,包括将网卡中数据拷贝到内核空间,以及进行进程调度,让等待的进程优先执行
这里存在几个问题:
- select函数每次执行前,需要修改FD_SET结构体,更新监视的句柄,每次都要把进程信息加到所有句柄的等待队列中,涉及一次遍历
- 当select从阻塞状态唤醒,也需要遍历确认是哪个句柄上有动静
- 要传递所有的fd给内核
epoll_create
1 |
|
epoll_create() creates a new epoll(7) instance. Since Linux 2.6.8,
the size argument is ignored, but must be greater than zero; see
NOTES below.
epoll_create() returns a file descriptor referring to the new epoll
instance. This file descriptor is used for all the subsequent calls
to the epoll interface. When no longer required, the file descriptor
returned by epoll_create() should be closed by using close(2). When
all file descriptors referring to an epoll instance have been closed,
the kernel destroys the instance and releases the associated
resources for reuse.
epoll函数
epoll_ctl
1 |
|
This system call is used to add, modify, or remove entries in the
interest list of the epoll(7) instance referred to by the file
descriptor epfd. It requests that the operation op be performed for
the target file descriptor, fd.
Valid values for the op argument are:
EPOLL_CTL_ADD
Add fd to the interest list and associate the settings
specified in event with the internal file linked to fd.
EPOLL_CTL_MOD
Change the settings associated with fd in the interest list to
the new settings specified in event.
EPOLL_CTL_DEL
Remove (deregister) the target file descriptor fd from the
interest list. The event argument is ignored and can be NULL
(but see BUGS below).
1 |
|
events这个参数是一个字节的掩码构成的。下面是可以用的事件:
- EPOLLIN - 当关联的文件可以执行 read ()操作时。
- EPOLLOUT - 当关联的文件可以执行 write ()操作时。
- EPOLLRDHUP - (从 linux 2.6.17 开始)当socket关闭的时候,或者半关闭写段的(当使用边缘触发的时候,这个标识在写一些测试代码去检测关闭的* 时候特别好用)
- EPOLLPRI - 当 read ()能够读取紧急数据的时候。
- EPOLLERR - 当关联的文件发生错误的时候,epoll_wait() 总是会等待这个事件,并不是需要必须设置的标识。
- EPOLLHUP - 当指定的文件描述符被挂起的时候。epoll_wait() 总是会等待这个事件,并不是需要必须设置的标识。当socket从某一个地方读取数据的时候(管道或者socket),这个事件只是标识出这个已经读取到最后了(EOF)。所有的有效数据已经被读取完毕了,之后任何的读取都会返回0(EOF)。
- EPOLLET - 设置指定的文件描述符模式为边缘触发,默认的模式是水平触发。
- EPOLLONESHOT - (从 linux 2.6.17 开始)设置指定文件描述符为单次模式。这意味着,在设置后只会有一次从epoll_wait() 中捕获到事件,之后你必须要重新调用 epoll_ctl() 重新设置。
返回值:如果成功,返回0。如果失败,会返回-1, errno将会被设置
有以下几种错误:
- EBADF - epfd 或者 fd 是无效的文件描述符。
- EEXIST - op是EPOLL_CTL_ADD,同时 fd 在之前,已经被注册到epoll中了。
- EINVAL - epfd不是一个epoll描述符。或者fd和epfd相同,或者op参数非法。
- ENOENT - op是EPOLL_CTL_MOD或者EPOLL_CTL_DEL,但是fd还没有被注册到epoll上。
- ENOMEM - 内存不足。
- EPERM - 目标的fd不支持epoll。
epoll_wait
1 | int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout); |
The epoll_wait() system call waits for events on the epoll(7)
instance referred to by the file descriptor epfd. The memory area
pointed to by events will contain the events that will be available
for the caller. Up to maxevents are returned by epoll_wait(). The
maxevents argument must be greater than zero.
The timeout argument specifies the number of milliseconds that
epoll_wait() will block. Time is measured against the
CLOCK_MONOTONIC clock. The call will block until either:
* a file descriptor delivers an event;
* the call is interrupted by a signal handler; or
* the timeout expires.
epoll_create的过程主要是创建并初始化数据结构eventpoll,以及创建file实例
1 |
|
就绪队列
rdllist是用于存储就绪的fd信息,使用双向链表删除,插入效率高
索引结构
红黑树是自平衡二叉查找树,搜索,插入,删除效率高。在epoll_ctrl添加监听fd的时候能够快速判断是否已经存在,以及快速插入,或者移除
ep_poll_callback():目标fd的就绪事件到来时,将epi->rdllink加入ep->rdllist的队列,导致rdlist不空,从而进程被唤醒,epoll_wait得以继续执行。
到epoll_wait(),从队列中移除wait,再将传输就绪事件到用户空间。
epoll比select更高效的一点是:epoll监控的每一个文件fd就绪事件触发,导致相应fd上的回调函数ep_poll_callback()被调用
select poll epoll这三个都是对poll机制的封装