IO多路复用
IO多路复用是一种同步IO模型,一个线程可以监听多个文件句柄,如果某个文件句柄就绪,会通常应用程序进行读写操作。没有文件句柄就绪会阻塞程序。
Select
程序把文件描述符fd从用户拷贝到内核空间,内核不断遍历所有fd,检查其状态,查看是否有fd就绪。就绪则返回。程序依次遍历所有的fd,检查其状态,如就绪则进行读写操作。哪个文件句柄就绪未知
优点:由内核来轮询fd状态,提升了效率
缺点:使用一个bitmap结构,一位对应一个fd,同时监听的fd数量有限(1024),用户态到内核态切换一定开销。需要遍历所有的fd依次判断是否就绪。
Poll
poll中声明了一种结构体
struct pollfd{
int fd;
short events; // 监听事件类型
short revents; // 实际发生的事件,初始为0,当fd就绪置为events
}
基于链表来存储信息,没有最大链接数的限制,其它和select类似。依然是把fd拷贝到内核,由内核轮询fd。
epoll:event pool
epoll是事件驱动,不再轮询。
- 使用mmap把内核空间和用户空间映射到同一块物理内存中,减少了用户空间和内核空间的数据交换。
- 使用红黑树保存要监听的套接字。红黑树的插入和删除性能较好O(logN)
- 使用一个双向链表保存就绪的套接字,返回时,只把这个双向链表拷贝到用户空间。只遍历就绪的套接字,进一步提升了效率。
创建一个epoll对象时,会创建一个eventpool实例,包含一个红黑树保存监听的套接字,一个双向链表保存就绪的套接字
struct eventpoll {
...
/* List of ready file descriptors */
struct list_head rdllist;
/* RB tree root used to store monitored fd structs */
struct rb_root_cached rbr;
...
};
socket在添加到红黑树上时,会再socket的等待队列(存放关注这个socket上的事件的进程)中注册一个回调函数。
socket就绪后,回调用这个回调函数,这个回调函数回把这个socket添加到就绪链表中,然后唤醒epoll_wait,通知epool由socket就绪。
程序会遍历这个就绪链表,并拷贝到用户空间,进行读写操作。
参考:
https://rebootcat.com/2020/09/26/epoll_cookbook/
惊群效应
惊群效应就是多个进程(线程)阻塞等待同一件事(资源)上,当事件发生时,操作系统可能唤醒所有等待这个事件的进程(线程),但最后只能由一个进程(线程)成功获取该事件(资源),其它进程(线程)继续阻塞,这回造成额外的性能损失
accept惊群的解决办法
通过一个WQ_FLAG_EXCLUSIVE标志位,当socket事件触发时,对于等待队列中的进程,如果这些进程没有这个标志位,则通通唤醒,有则只唤醒第一个由这个标志位的进程则结束。
epoll惊群
设置SO_REUSEPORT,使端口复用,多个进程(线程)绑定同一个端口号,每个进程(线程)都监视一个listensock,交给内核解决,内核只会交给一个进程处理。

参考:https://blog.csdn.net/weixin_53695360/article/details/123463057#t11

浙公网安备 33010602011771号