ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

select、poll和epoll

2021-09-29 18:58:17  阅读:154  来源: 互联网

标签:epoll int 描述符 内核 poll select


介绍:

在说select、poll和epoll之前,先说下IO多路复用机制,简单来说,就是服务端通过一个线程(或者进程)来维护多个Socket连接,多个连接共用一个阻塞对象,线程只需要在这个阻塞对象上等待,无需再轮询多个连接。当任何一个连接上有数据可以处理时,操作系统就会通知进程,进程就阻塞的状态返回,开始进行业务处理。

select、poll和epoll都是IO多路复用机制的常见方式,通过这种方式,可以监控多个文件描述符,一旦某个描述符就绪,操作系统就能够通知进程进行相应的读写操作。
select、poll和epoll都是同步IO。

select:

select函数允许内核挂起当前进程,当一个或多个IO事件发生后,再唤醒该进程。
select的定义如下:

int select(int maxfd, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);

参数说明:
maxfd指待测试的文件描述符个数,它的值是待测试的最大文件描述符加 1。(因为文件描述符是从0开始的)
readset是读描述符集合,代表内核可以在这些描述符上检测数据是否可读。
writeset是写描述符集合,代表内核可以在这些描述符上检测数据是否可写。
exceptset是异常描述符集合,代表内核可以在这些描述符上检测数据是否有异常发生。
timeout指的是等待描述符上数据就绪的超时时间,是一个结构体,,定义如下:

struct timeval {
  long   tv_sec; /* seconds */
  long   tv_usec; /* microseconds */
};

如果把该参数设置为null,则表示一直等待,直到有数据就绪。如果把tv_sec和 tv_usec都设置为0,则表示不等待,还可以设置非0的值,表示等待的时间。

该方法的返回值:
如果有就绪的文件描述符,则返回就绪的文件描述符的数目,若超时则为0,若出错则为-1。

select的缺点:
每次调用select,都需要把fd的集合从用户态拷贝到内核态。而且select能够支持的文件描述符数量太小,默认只有1024个。

poll:

poll和select的功能类似,两个接口描述fd的方式不同,poll使用的是pollfd,而select使用的是fd_set。poll和select只是编程接口上的区别,内核的本质实现其实是差不多的。但是克服了select的文件描述符限制的缺陷,比select使用的更广。
poll的定义如下:

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

函数需要三个入参,第一个参数是pollfd 的数组。pollfd的定义如下:

struct pollfd {
    int    fd;       /* file descriptor */
    short  events;   /* events to look for */
    short  revents;  /* events returned */
 };

fd代表文件描述符,events代表描述符上待检测的文件类型,可以表示多个不同的事件,revents存放每次检测完的结果。
nfds描述的是数组 fds 的大小,简单说,就是向 poll 申请的事件检测的个数。
timeout也是代表的等待的超时时间,如果大于0,表示等待指定的毫秒数后返回,等于0表示立即返回,小于0表示永远等待。

函数返回值:
如果有就绪的文件描述符,则返回就绪的文件描述符的数目,若超时则为0,若出错则为-1。

epoll:

epoll是对select和poll的改进,在面对大量文件描述符时,性能表现非常出色。
epoll提供了三个函数,epoll_create,epoll_ctl和epoll_wait。
epoll_create:

int epoll_create(int size);
int epoll_create1(int flags);

epoll_create方法创建一个epoll实例,epoll实例用来调用epoll_ctl和epoll_wait。
返回值: 若创建成功则返回一个大于0的值,表示epoll实例;若返回-1表示出错
epoll_ctl:

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

调用epoll_ctl方法可以往epoll实例中增加或删除监控的事件,入参有四个,epfd是一个epoll实例描述字,就是通过epoll_create创建的epoll实例。op表示增加还是删除一个监控事件,fd是注册的事件的文件描述符。event表示的是注册的事件类型。
返回值: 若成功返回0;若返回-1表示出错。
epoll_wait:

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

epoll_wait和select、poll功能类似,等待事件的就绪,调用后进程会被挂起。
入参有四个,第一个参数epfd是 epoll 实例描述字,events代表返回给用户空间需要处理的 I/O 事件,maxevents表示 epoll_wait 可以返回的最大事件值,timeout也是代表超时时间,如果为-1表示不超时,如果为0则是立即返回。
返回值: 成功返回的是一个大于0的数,表示事件的个数;返回0表示的是超时时间到;若出错返回-1。

select和poll提供的是水平触发(level-triggered)机制,而epoll除了提供水平触发机制外,还提供了性能更强的边缘触发(edge-triggered)机制。
水平触发:当检测到某描述符事件就绪,服务器端需要不断的从epoll_wait中苏醒,直到内核缓冲区的数据被读取完成。水平触发是epoll默认的方式。
边缘触发:当检测到某描述符事件就绪,服务器端只需要从epoll_wait中苏醒一次,即使进程没有调用 read 函数从内核读取数据,也依然只苏醒一次,因此我们程序要保证一次性将内核缓冲区的数据读取完;

如果用的是水平触发方式,当内核通知文件描述符可以读写时,接下来还可以继续检测它的状态,判断是否可以继续读写。
但如果使用的是边缘触发方式,内核只会通知一次可以读写,而且还不知道能读写多少内容,所以收到通知后一般是去循环读取,以免失去读写机会。
一般来说,边缘触发方式能够减少epoll_wait的系统调用,所以效率比水平触发要高。

epoll是Linux内核为处理大批量文件描述符而对poll进行的改进,算是select和poll的增强版本。在大量并发并且少量连接活跃的场景中能显著提高CPU的利用率,被称为解决C10K的利器。

标签:epoll,int,描述符,内核,poll,select
来源: https://blog.csdn.net/chayangdz/article/details/120554315

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有