IO多路复用

IO多路复用

1、前言

IO多路复用指的是在单个线程中以阻塞的方式同时监控多个文件描述符上的IO事件。Linux中常用的IO多路复用方法主要为:selectpollepoll,三者各有优缺点。

2、原理

2.1 select

select的基本原理是通过将要监控的fd集合以fd_set的方式传递给内核,其中fd_set可以理解为内核中描述fd的位图,被置位的位意味着要监控的fd。这样的句柄位图,它可以向内核传递三个,分别是用来监控读、写、异常三个事件的,原型如下所示:

int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);
  • nfds:要监控的fd集合中,最大的fd+1。内核会从0开始,扫描这个位图,直到nfds,从而避免不必要的扫描
  • readfds:当句柄集合中的句柄可读时(有数据到来了),进程将被唤醒
  • writefds:监控可写事件,与readfds类似
  • exceptfds:监控异常事件,与readfds类似

标准库提供了一些辅助宏定义,包括FD_ZEROFD_SETFD_ISSETFD_CLR,分别用于位图的清零、fd的置位,fd是否置位检测、fd取消置位。

  • 优点:使用简单、方便,跨平台性良好
  • 缺点:监控的fd数量有上限(一般为1024个);需要遍历位图来确定哪个句柄上事件到来了,效率低。

2.2 poll

poll的原理与select类似,不同的是poll传递给内核的是一个struct pollfd类型的数组,该结构体定义如下:

struct pollfd {
	int fd;			//要监控的fd
	short events;	//要监控的事件,如POLLIN(read)、POLLOUT(write)等
	short revents;	//返回(命中)的事件
};

poll函数的原型如下:

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
  • fdsstruct pollfd类型的数组

  • nfds:数组的长度,即要监控的fd的个数

  • timeout:超时时间

  • 优点:相比于select,能监控更多的事件,且没有长度限制

  • 缺点:没有摆脱需要遍历所有的fd来检查谁触发了事件,效率低

在内核实现方面,pollselect底层实现基本相同。以UDP套接字为例,每个sock都有一个等待队列,poll会将当前进程添加到要监控的等待队列上,一旦有数据到达,等待队列上的进程就都会被唤醒。然后,内核会根据被监控sock的状态,判断自己所关注的事件是否触发,并将所触发的事件返回给用户态。其流程如下图所示:

在这里插入图片描述

参考链接:linux socket poll io处理-udp

2.3 epoll

epoll在机制上与pollselect差距比较大,后面的两种方式属于“即时型”的,即每次调用的时候都需要把我们要监控的句柄和事件信息传递给内核。这部分的工作看起来是多余的,因为一般来说我们关注的fd和事件都不会每次都不一样,因此这种数据拷贝实际上是不必要的,特别是在fd数量比较多的时候,会产生大量的无用拷贝。

epoll机制改变了这种状况,其实现机制为:创建一个新的类型为epoll的句柄,并将要监控的句柄和事件绑定到这个句柄上,然后在这个句柄上进行等待事件触发。从这里可以看出,要监控的句柄只需要传递给内核一次,而且内核会把触发了事件的句柄传递给用户态,从而用户态不需要遍历所有句柄。该机制提供了三个接口,包括:

  1. epoll句柄创建。下面的函数会返回一个文件句柄,代表epoll的句柄。参数size会被忽略,但是不能为0。

    int epoll_create(int size);
    
  2. 句柄和事件的管理。

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

    这个函数用于管理epoll上的句柄和事件,各个参数含义为:

    • epfd:使用epoll_create创建的句柄
    • op:要进行的操作,包括EPOLL_CTL_ADDEPOLL_CTL_MODEPOLL_CTL_DEL,意思很明显
    • fd:要进行操作的fd
    • event:要操作的事件
  3. 事件的等待。下面的函数会在epfd上等待,直到有事件触发,与poll类似。不同的是,它会将被触发了的fd通过events传递给用户态,其中maxeventsevents数组的长度。

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

优点selectpoll的增强版,摆脱了前两者效率低、需要重复传参数的问题,没有句柄数量的限制。

缺点:需要调用额外的接口,操作比较繁琐。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值