网络:高级IO之IO多路转接

select

系统由select函数来实现多路复用。

这个函数用来让我们的程序监控多个文件描述符的状态变化。

程序会停在select这里等待,直到被监视的文件描述符一个或者多个发生变化。

函数原型如下:

#include <sys/select.h>

int select(nfds, readfds, writefds, exceptfds, timeout)

nfds:需要监视的文件描述符+1

readfds:可读文件描述符集合

writefds:可写文件描述符集合

exceptfds:异常文件描述符集合

timeout:用来设置select等待的时间

timeout的取值有三种:

1.NULL,表示select没有timeout,select将一直被阻塞,直到某个文件描述符上发生事件

2.0,仅仅检测描述符集合的状态

3.特定的其他时间,在这个时间没有事件发生,select将会超时

fd_set相关:

FD_ZERO(fd_set *fdset):清空fdset与所有文件句柄的联系。

FD_SET(int fd, fd_set *fdset):建立文件句柄fd与fdset的联系。

FD_CLR(int fd, fd_set *fdset):清除文件句柄fd与fdset的联系。

FD_ISSET(int fd, fd_set *fdset):检查fdset联系的文件句柄fd是否可读写,当>0表示可读写。

 

select的过程:(8个比特位)

1.fd_set set;  FD_ZERO(&set);置零                              00000000

2.需要监视的文件描述符为6,FD_SET(fd,&set);          00100000

3.再加入1,2                                                                 00100011

4.执行select(7,&set,0,0,0)         

5.若1,2有事件发生  返回00000011,没事件发生的6就会被置零

 

select的两个特点:

1.可监控的文件描述符取决于sizeof(fd_set)的大小,由于fd_set有固定值,所以可监控的文件描述符是上限的

2.将fd加入select的同时,需要用一个数组来进行保存select中监控的fd

    一是用于select返回后,数组和fd_set进行判断

    二是select每次返回后都要清空并未发生的fd,每次开始select前都要从数组中获取fd然后加入到fd_set中,并且获得fd的最大值,用于select的第一个参数。

 

select的缺点:

1.每次select,都需要设置fd集合

2.每次select,都需要把fd集合从用户态拷贝到内核态

3.每次select,都需要再内核遍历所有的fd

4.select支持的文件描述符太小了

 

 

epoll

epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。epoll除了提供select/poll那种IO事件的水平触发(Level Triggered)外,还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。

epoll的三个函数调用:

int epoll_create

int epoll_create(int size)

创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

自从Linux 2.6.8开始,size参数被忽略,但是依然要大于0。

int epoll_ctl

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

epoll的事件注册函数,它不同与select()是在监听事件时告诉 内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来表示:

EPOLL_CTL_ADD:注册新的fd到epfd中;

EPOLL_CTL_MOD:修改已经注册的fd的监听事件;

EPOLL_CTL_DEL:从epfd中删除一个fd;

第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:

struct epoll_event {

__uint32_t events; /* Epoll events */

epoll_data_t data; /* User data variable */

};

events可以是以下几个宏的集合:

EPOLLIN :表示对应的 文件描述符可以读(包括对端SOCKET正常关闭);

EPOLLOUT:表示对应的 文件描述符可以写;

EPOLLPRI:表示对应的 文件描述符有紧急的数据可读(这里应该表示有 带外数据到来);

EPOLLERR:表示对应的 文件描述符发生错误;

EPOLLHUP:表示对应的 文件描述符被挂断;

EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。

EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里。

int epoll_wait

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)

等待事件的产生,类似于select()调用。参数events用来从 内核得到事件的集合,maxevents表示每次能处理的最大事件数,告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。

epoll的工作原理:

1.每一个epoll对象都有一个eventpoll结构体,用来存放epoll_ctl存放的事件

 

2.这些事件会被挂载到红黑树当中,红黑树可以识别出重复的事件

 

3.所有添加到epoll中的事件都会和设备驱动程序建立回调关系,也就是说,当需要响应的事件发生时,就会调用这个回调函数

 

4.通过这个回调方法,就会把将发生的事件挂载到双向链表中

 

5.在epoll中,每一个事件,都会对应建立一个epitem结构体

 

6.在调用epoll_wait的时候,只需要判断epitem结构体是否在这个双向链表当中

 

7.当这个双向链表不为空,说明有事件就绪,将发生的事件复制到用户态,并且就绪事件的数量返回给用户

 

 

epoll的使用过程:

1.epoll_create创建句柄

2.epoll_ctl,将需要监控的文件描述符注册

3.epoll_wait等待事件就绪

 

epoll相比于select的优点:

1.函数变成三个,调用的过程变得方便高效,不需要每次的设置需要关注的文件描述符

2.只在某个阶段需要将文件描述符结构拷贝到内核态,这个操作不频繁

3.采用事件回调机制,不需要每次都遍历整个文件描述符集合,只需要将就绪的事件添加到就绪队列,然后再epoll_wait返回

4.文件描述符没有上限

 

 

epoll的工作方式:

1.水平触发工作方式(LT)epoll默认的工作方式

epoll检测socket事件就绪的时候,不会立刻进行处理,或者可以处理一部分

比如,有10k的数据,可以先读5k,在第二次调用epoll_wait将剩下的5k读完,这时候epoll_wait任然可以直接立刻返回并且通知socket读事件就绪

直到缓冲区没有任何数据epoll_wait才不会立刻返回

支持阻塞读写和非阻塞读写

2.边缘触发工作方式(ET)

epoll检测socket事件就绪的时候,必须立刻作出处理

比如,有10k的数据,可以先读5k,在第二次调用epoll_wait读剩下5k的时候,epoll_wait就不会再返回了

所以在ET模式下,文件描述符就绪后,只有一次处理机会

ET比LT的调用epoll_wait次数少的多,所以ET比LT更加高效

只支持非阻塞的读写

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值