IO复用

IO复用:使得程序能同时监听多个文件描述符

select:

select在一段指定的时间内,监听用户感兴趣的文件描述符的 读、写、异常事件。

select(int nfds,fd_set* readfds ,fd_set* writefds,fd_set* exceptfds,struct timeval* timeout);

当调用了select之后整个进程就会被block。同时kernel会监听select负责的所有的socket.如果
相关的时间就绪就会返回。

但是select只能处理 读、写、异常事件,不能处理更多的事件。
而且内核是对fd_set集合在线修改,下次调用不得不重置相应的集合。。。


poll:

int poll(struct pollfd* fds,....);

与select不同的是poll将 文件描述符和时间集合都定义在了 结构体中。并且注册事件和实际发生事件
被分开了。

struct pollfd
{ 
    int fd;
    short events;   //注册事件
    short revents;   //实际发生事件
}

于是每次内核修改的是revents,无需重置pollfd了。


epoll:

epoll将用户关心的文件描述符放在内核的一个事件表中,从而无须每次重复传递。但需要一个额外的文件描述符
来标志内核中的时间表

epoll_create(int size)             //创建一个事件表
epoll_ctl()                          //给文件符指定操作类型(注册xxx上的xxx事件...)
epoll_wait()                         //等待事件

三种方法差异:

select和poll都只能工作在相对低效的LT模式中,epoll则能在ET模式中并支持EPOLLONESHOT。
select和poll采用的轮询的方式,每次调用都要扫描整个事件表O(n),而epoll采用的是回调,如果检测到
就绪的,回调函数就将对应时间插入内核就绪队列中,最后直接拷贝出来O(1)。
如果触发频繁,epoll的效率不一定高。所以epoll适合连接数量多,活动少的情况。


LT和ET:

1.LT(电平模式):
当epoll_wait检测到事件发生,程序可以不立即处理,再次调用还会出现此事件,直到事件被处理。

2.ET(边沿触发):
当epoll_wait检测到事件发生,程序必需立即处理,因为再次调用不会出现。


EPOLLONESHOT事件:

即便使用ET,一个事件还是可能被多次触发。
比如一个线程读取完某个socket上的数据并进行处理时,socket又出现新的数据可读。。。
此时另一个线程会被唤醒来处理这个。
如果希望任意时刻一个socket只被一个线程处理,可以使用epoll的EPOLLONESHOT

参考:

linux高性能服务器编程

posted @ 2017-03-20 18:11  Przz  阅读(184)  评论(0编辑  收藏  举报