在所有POSIX兼容的平台上,select函数使我们可以执行I/O多路转接。传给select的参数告诉内核:
从select返回时,内核告诉我们:
使用这种返回信息,就可调用响应的I/O函数(一般是read或write),并且确知该函数不会阻塞。
#include <sys/select.h>
int select(int maxfdpl,
fd_set* readfds,
fd_set* writefds,
fd_set* exceptfds,
structtimeval * tvptr);
select函数的特点:
include <sys/select.h>
int pselect(intmaxfdpl,
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
const struct timespec *tsptr,
const sigset_t *sigmask);
除了以下几点外,pselect与select相同。
include <poll.h>
int poll(structpollfd fdarray[], nfds_t nfds, int timeout);
struct pollfd {
int fd; /* file descriptior to check, or < 0 to ignore*/
short events; /* events of interest on fd */
short revents; /* events that occurred on fd */
};
poll函数可用于任何类型的文件描述符。与select不同,poll不是为每个条件(可读性,可写性和异常条件)构造一个描述符集,而是构造一个pollfd结构的数组,每个元素指定一个描述符编号以及我们对该描述符感兴趣的条件。
应将每个数组元素的events成员设置为图14-17中所示值的一个或几个,通过这些值告诉内核我们关心的是每个描述符的哪些事件。返回时,revents成员由内核设置,用于说明每个描述符发生了哪些事件。
直到Linux2.6才出现了由内核直接支持的实现方法,那就是epoll,它几乎具备了之前所说的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知方法。
epoll可以同时支持水平触发和边缘触发(EdgeTriggered,只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发),理论上边缘触发的性能要更高一些,但是代码实现相当复杂。
epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。
另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。
#include<sys/epoll.h>
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);