IO多路复用-select、poll、epoll

目录

 

IO多路复用-select、poll、epoll

一、select函数

接口说明

使用说明

实现原理

poll函数

epoll函数

接口说明

实现原理

水平触发(LT):

边缘触发(ET):

 


 


 

 


IO多路复用-select、poll、epoll

作用:提供一个进程监听多个描述符(比如socket)的服务,一旦监听到处于就绪状态的描述符,便通知程序。

提示:以下是本篇文章正文内容,下面案例可供参考

一、select函数

include <sys/select.h> 
#include >sys/time.h>
int select(int maxfds,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout);

接口说明

maxfds 表示需要监听的描述符数量+1,即maxfds=n+1表示监听(0,1,2...n)范围的描述字。
其中 0 1 2一般为标准输入、输出和错误标准输出,一个进程可以打开的描述字最大为1024,可以通过修改内核代码头文件宏定义然后重新内核改变这个数值。
fd_set是一个bitmap,每一个bit代表一个描述字(1表示就绪),用于记录那些描述字处于就绪状态。显然readset、writeset和expectset分别用于可读可写和异常状态的描述符。
timeout用于设定超时时间,在这段时间内持续监听进程指定的描述符。timeout==NULL表示一直监听,timeout==0表示立即返回。

函数返回处于就绪状态的描述符数量,-1代表错误返回。

void FD_ZERO(fd_set *fdset);           //清空集合

void FD_SET(int fd, fd_set *fdset);   //将一个给定的文件描述符加入集合之中

void FD_CLR(int fd, fd_set *fdset);   //将一个给定的文件描述符从集合中删除

int FD_ISSET(int fd, fd_set *fdset);   // 检查集合中指定的文件描述符是否可以读写 

使用说明

    socket sct;
    ...
    fd_set readfds;//用于记录就绪状态的描述符
    struct timeval tv;
    tv.tv_sec = 0; 
    tv.tv_usec = 0;
    while(1)
    {
        FD_ZERO(&readfds);//初始化
        FD_SET(fd,&readfds);//添加需要监听的描述字fd
        int res = select(fd+1, &readfds, NULL, NULL, &tv);
        if (res < 0) //返回错误
            continue;
        if(FD_ISSET(fd,&readfds))//对照readfds中的bit位确定是否就绪
        {
            recv();
        }
    }

(1)声明一个fd_set变量,可以看做是一个1024bit长度的内存,0-1023位分别记录对应文件描述符的就绪状态(0-未就绪 1-就绪)。

(2)调用FD_ZERO初始化fd_set变量bit位为0。(readfds = 00000000)

(3)调用FD_SET添加需要监听的fd,fd对应bit位置为1。(fd=3 则 readfds = 00010000)

(4)调用select函数开始监听。(seclect(9,readfds,NULL,NULL,&tv))

(5)select函数返回,判定返回值,-1表示错误返回。此时就绪的bit位为1。(假设没有监听到就绪fd,readfds = 00000000)

(6)调用FD_ISSET(fd,&readfds)对照bitmap中bit位判断fd是否就绪。

(7)进行对应io操作。

 

实现原理

本质上是向中断服务程序注册了回调函数,当发生设备发生读写等中断操作,则会调用该回调函数,达到通知用户进程设备发生了变化。

具体实现是,声明一个fds[]用于存放需要监听描述字(通过FD_SET添加),将用于存放是否就绪的bitmap即fd_set拷贝到内核空间,执行系统调用,遍历fds并调用poll函数将进程加入设备的驱动程序对应的等待队列中,向对应设备注册回调函数用于将结果存入内核空间中fd_set中。随后,当有设备处于就绪状态(此时调用回调函数将状态保存到内核空间的fd_set中)或者等待时间超时,则将内核空间的fd_set拷贝到用户空间,随后调用等待队列中的进程,用户进程被唤醒便可以调用FD_ISSET函数遍历fds对应的fd_set比特位来查询监听的fd是否处于就绪状态。

poll函数

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

select函数支持的最大句柄数一般为1024(内核代码宏定义),poll解决了fds集合大小1024限制的问题。poll使用pollfd结构来存放就绪的描述字,使得poll支持的fds集合限制远大于select的1024。但是两者的原理差不多,都需要遍历完成,都存在随着监控的socket集合的增加性能线性下降的问题。

epoll函数

接口说明

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);

 

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

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

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

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

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

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

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

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

实现原理

------参考 https://baijiahao.baidu.com/s?id=1641172494287388070&wfr=spider&for=pc

epoll_create会在内核的高速cache区中建立一颗红黑树以及就绪链表(该链表存储已经就绪的文件描述符)。接着用户执行的epoll_ctl函数添加文件描述符会在红黑树上增加相应的结点。

采用回调机制。在执行epoll_ctl的add操作时,不仅将文件描述符放到红黑树上,而且也注册了回调函数,内核在检测到某文件描述符可读/可写时会调用回调函数,该回调函数将文件描述符放在就绪链表中。

epoll_wait只用观察就绪链表中有无数据即可,最后将链表的数据返回给数组并返回就绪的数量。内核将就绪的文件描述符放在传入的数组中,所以只用遍历依次处理即可。这里返回的文件描述符是通过mmap让内核和用户空间共享同一块内存实现传递的,减少了不必要的拷贝。

水平触发(LT):

不主动清除上次list里面的就绪fd,下次调用会继续通知。

边缘触发(ET):

主动清除list中就绪的fd,下次调用不会通知。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值