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是事件驱动,不再轮询。

  1. 使用mmap把内核空间和用户空间映射到同一块物理内存中,减少了用户空间和内核空间的数据交换。
  2. 使用红黑树保存要监听的套接字。红黑树的插入和删除性能较好O(logN)
  3. 使用一个双向链表保存就绪的套接字,返回时,只把这个双向链表拷贝到用户空间。只遍历就绪的套接字,进一步提升了效率。

创建一个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

posted @ 2022-07-21 20:46  店里最会撒谎白玉汤  阅读(607)  评论(0)    收藏  举报