select
#incl_ude <sys/select. h>
#include <sys/time.h>
int select(int maxfd, fd_set * readset, fd_set * writeset,
fd_set * exceptset, const struct timeval * timeout);
//成功时返回大千0 的值,失败时返回-1 。
#maxfd 是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错。
#readset 将所有关注“是否存在待读取数据”的文件描述符注册到fd_set型变量,并传递其地址值。
#writeset 将所有关注“是否可传输无阻塞数据”的文件描述符注册到fd_set型变量,并传递其地址值。
#exceptset 将所有关注”是否发生异常"的文件描述符注册到fd_set型变量,并传递其地址值。
#timeout 调用select 函数后,为防止陷入无限阻塞的状态,传递超时( time-out ) 信息。
#返回值 发生错误时返回-1, 超时返回时返回0。因发生关注的事件返回时,返回大千0的值,该值是发生事件的
文件描述符数。
优点
- 服务器端接入者少。
- 程序应具有兼容性。
缺点:
- 调用select函数后,需要对所有文件描述符执行循环检查语句。
- 每次调用select函数时都需要向该函数传递监视对象信息。
- 监视的fd数量被限制,32位机默认是1024个,64位机默认是2048。
参考:https://blog.csdn.net/qq_40732350/article/details/88978584
pselect
int pselect(int maxfdp1,
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
const struct timespec *tsptr,
const sigset_t *sigmask);
优点:
- pselect的超出时间能指定到纳秒级(旧结构只能指定到微秒级)
- pselect增加了指向信号集的指针sigmask(此时的信号集表示信号掩码)
pselect函数是一个 防止信号干扰的增强型 select函数(重点啊小哥们)
对于pselect可使用一可选择的信号屏蔽字。若sigmask为空,那么在与信号有关的方面,pselect的运行状况和select相同。否则,sigmask指向一信号屏蔽字,在调用pselect时,以原子操作的方式安装该信号屏蔽字。在返回时恢复以前的信号屏蔽字。
poll
int poll(struct pollfd fds[], nfds_t nfds, int timeout);
#fds是一个指向结构pollfd数组的指针,监视的文件描述符和条件放在里面。
#nfds是比监视的最大描述符的值大1的值。
#timeout是超时时间,单位为毫秒,当为负值时,表示永远等待。
大于0:表示满足条件的文件描述符数量
0:超时
-1:发生错误
缺点:
- 大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。
- poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。
poll和select的区别
poll库与 select库的主要区别在于, select库需要为读事件、写事件和异常事件分别创建一个描述符集合,因此在最后轮询的时候,需要分别轮询这三个集合,而poll库只需要创建一个集合,在每个描述符对应的结构上分别设置读事件,写事件或异常事件,最后轮询的时候可以同时检查这三种事件是否发生,poll是select的优化实现。
epoll
#include <sys/epoll. h>
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
//成功时返回发生事件的文件描述符数,失败时返回-1。
#epfd 表示事件发生监视范围的epoll例程的文件描述符。
#events 保存发生事件的文件描述符集合的结构体地址值。
#maxevents 第二个参数中可以保存的最大事件数。
#timeout 以1/1000秒为单位的等待时间,传递-1 时, 一直等待直到发生事件。
优点:
- 同时可以返回多个文件描述符变化的事件。
- 以事件的形式返回文件描述符的变化。
- 内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。
缺点:
- 是Linux所特有的,所以移植性不够好。
边缘触发比条件触发的好处
- 条件触发关心的是事件状态:只要处在某个状态下就会一直触发事件
- 条件触发关心的是事件状态改变:只要状态发生改变就会触发事件
参考:https://blog.csdn.net/qq_40732350/article/details/89021711
select、poll、epoll 区别总结:
进程的最大连接数 | FD剧增后带来的IO效率问题 | 消息传递方式 | |
select | 会受到宏的限制 | 因为每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度慢的“线性下降性能问题”。 | 内核需要将消息传递到用户空间,都需要内核拷贝动作 |
poll | 系统运行打开的最大数目 | 同上 | 同上 |
epoll | 系统运行打开的最大数目 | 因为epoll内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epoll没有前面两者的线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题。 | epoll通过内核和用户空间共享一块内存来实现的。 |
epoll不是采用轮询,所以时间复杂度是O(1);
select() 提供了更高的超时精度:select() 支持微秒级,poll() 支持毫秒级。ppoll() 和 pselect() 理论上都提供了纳秒级的超时精度,但是实际上,这两个调用的毫秒级精度都不可靠。
select | poll | epoll | |
监控单元 | 文件描述符集合 | 结构体数组 | 需要监控的结构体 |
工作模式 | 条件触发 | 条件触发 | 条件触发/边缘触发 |
实现方式 | 轮询 | 轮询 | 回调机制 |
数据传递 | mmap内存映射 | ||
应用 | 平台无关,监控数量少 | 只使用于Linux,监控数量少 | 只使用于Linux,可以监控大量的文件描述符 |
参考:https://www.cnblogs.com/zhaodahai/p/6831456.html