在linux没有实现epoll事件驱动机制之前,常规的手段是选择select和poll等IO多路复用的方法来实现并发服务程序。但是在大数据、高并发、集群情况下,select和poll的性能瓶颈就出现了,于是epoll就诞生了
Select
select函数监视的文件描述符分三类:writefds、readfds和exceptfds
调用select后会产生阻塞,直到文件描述符就绪或超时,函数才会返回。
select函数返回后,可以通过遍历fd_set,来找到就绪的文件描述符
int select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict errorfds, struct timeval *restrict timeout);
select执行步骤如下:
-
使用copy_from_user从用户空间拷贝fd_set到内核空间
-
注册回调函数_pollwait();
-
遍历所有FD,调用其对应的poll方法
-
以tcp_poll为例,其核心实现就是_pollwait(),也就是注册回调函数
-
_pollwait()的主要工作就是把当前进程挂到设备的等待队列中,不同的设备有不同的等待队列,对于tcp_poll来说,其等待队列是sk→sk_sleep。在设备收到一条消息或填写完文件数据后会唤醒设备等待队列上睡眠的进程,这时当前进程便被唤醒了
-
poll方法返回时会返回一个描述读写操作是否就绪的mask掩码,根据这个mask掩码给fd_set赋值
-
如果遍历完所有的fd,还没有返回一个可读写的mask掩码,则会调用schedule_timeout让调用select的当前进程进入睡眠
-
当设备驱动发生自身资源可读写后,会唤醒其等待队列上睡眠的进程。如果超过设定的超时时间,还是没被唤醒,则调用select的进程重新被唤醒,从而获得CPU资源,然后重新遍历fd,判断有没有就绪的fd
-
把fd_set从内核空间拷贝到用户空间
-
select的触发方式是水平触发,应用程序如果没有完全对一个已经就绪的文件扫描夫进行IO操作,那么之后每次selec