select、poll、epoll相关

select、poll、epoll都是IO多路复用机制中的一种处理方式,由单个线程处理多个文件描述符,利用内核帮助我们监控文件事件是否准备就绪(一般是读写就绪),就绪后可以通知用户线程去进行相应读写操作。

  • 多路: 指的是多个socket网络连接;
  • 复用: 指的是复用一个线程、使用一个线程来检查多个文件描述符(Socket)的就绪状态
  • 内核空间:操作系统内核运行的区域,它包括了操作系统内核代码、数据结构和设备驱动程序等。内核空间通常是操作系统中的一块保护内存区域,只有操作系统内核才能够访问这个区域。
  • 用户空间:用户空间是指用户应用程序运行的区域,包括用户应用程序代码、数据和堆栈等

可以举个简单的例子(可能不是很恰当),在饭馆,有客人(文件描述fd)、服务员(内核)、老板(用户程序),在select、poll模式下,当客人准备结账时,服务员会告诉老板有人要结账,但没告诉老板是哪一桌,需要再轮询一遍;epoll模式下,服务员告诉老板1、2号桌结账,老板就可以去直接收钱。

1.select

int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout)
有文件描述符就绪时,返回就绪数量>1,超时时返回0,异常返回-1

在select模式下它有一个select方法,有5个参数,首先是待监测文件描述的数量;中间三个参数是我们想让内核监测的读、写、异常事件的文件描述集合fd_set,如果不想监测该类事件直接传null即可;最后是一个超时时间的结构体,可以设置等待的秒数和微秒数,告知内核如果没有文件描述准备就绪的话,等待多久再去重新复制fd_set(即需要监测的文件描述集合)进行扫描,它可以指定三种模式:

(1) 设为null,如果没有文件描述IO准备就绪的话,那么一直等待

(2) 设置固定的时间,等待一段时间后如果没有文件描述IO准备就绪,立即返回,再次将全量文件事件从用户空间复制到内核空间,重新开始扫描

(3) 根本不等待,扫描一遍fd_set后,立即返回,为此超时时间结构体不能为空,需要把等待时间参数设为0

当有文件描述符fd准备就绪时会对fd_set中的文件描述做标记,并返回就绪fd个数,用户线程根据标记寻找就绪的文件描述。

缺点:

(1)select每次执行时,都需要把文件描述从用户空间复制到内核空间,当文件描述集合比较大时这一过程会比较消耗资源;

(2)每次调用select都需要在内核遍历所有的fd_set,当fd_set很大时也很耗时

(3)select是以fd_set存储的待监测fd,内核对它的大小做了限制,默认为1024;

(4)同时发生文件描述IO事件准备就绪时,虽然能够返回就绪事件数量,但是用户线程并不知道是哪个文件描述就绪,需要进行查找,如果监测的fd很多的话会比较耗时。

最后就是select是水平触发的方式,如果用户线程没有处理准备就绪的文件事件,那么下次select调用还会将这个事件进行返回

2.poll

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

poll方法比较简单,由一个结构体pollfd存储待监测文件描述,nfds表示待监测fd数量,最后有一个超时时间,当timeout=0时表示轮询遍历;大于0时,表示当有fd IO事件准备就绪或到达等待事件时返回;小于0时表示,如果没有fd准备就绪那么就一直等待。

poll与select运行模式比较类似,方法每次工作时都需要将文件描述从用户空间复制到内核空间,并轮询待监测fd集合,当没用fd就绪时进入等待,当期间有文件描述IO事件准备就绪或者超时时返回,用户线程根据标记查询就绪fd。与select不同的是poll使用pollfd结构体存储存储待监测fd,select使用的是fd_set,pollfd内部是使用链表实现的,不会再限制监测fd的数量。

select与poll在等待的过程中如果发生了fd IO就绪事件会立即返回,但是并不知道是哪个fd准备就绪,用户线程需要根据fd_set和pollfd集合中的标记再次轮询fd集合寻找就绪fd。

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

epoll中有三个方法,

(1)create会创建epoll句柄即监听集合,size表示它监听的描述符数量,能打开的监听数量远超select,单cpu可打开连接数量超过10W;

(2)ctl方法为fd注册函数,向epfd中注册待监听fd,epfd表示create方法的返回值,将要注册到哪个句柄,它实际是由内核维护的一个fd类型的红黑树;op表示动作,是向epfd中添加fd,删除或修改已注册fd的监测事件;fd为要被监测的文件描述;event为该fd需要被监测的事件类型,在这一步时会将fd从用户空间复制到内核空间;

(3)最终通过调用wait方法等待就绪fd。events为就绪事件类型,maxevents为事件最大数量,最后为超时时间。

优点:

(1)相比select,epoll并没有打开fd数量的限制,它打开fd数量与可以打开文件数量有关,在1GB内存的机器上大约是10万左右

(2)与select、poll相比调用epoll_wait时并不需要重新将fd从用户空间复制到内核空间,在epoll_ctl这一步,已经将fd复制完毕了

(3)select、poll执行时都需要遍历文件描述集合fd_set或pollfd,在epll中epoll_ctl方法fd加入句柄时会注册一个回调函数,当fd就绪时,会进入到一个就绪队列中,在调用wait时不再需要遍历fd集合,只需要就绪队列是否为空。当有就绪fd时会返回就绪fd数量,并且将就绪队列fd复制到用户空间的一个events数组中,用户线程只需要根据就绪fd数量遍历events即可,不用轮询全部fd集合了

此外epoll除了支持select、poll的水平触发之外还支持边缘触发,当epoll_wait检测到某描述符事件就绪并通知应用程序时,应用程序必须立即处理该事件,如果不处理,下次调用epoll_wait时,不会再次通知此事件。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值