多路复用
1|0多路复用
- 概念
- 事先把需要监听的文件描述符加入到集合中,然后在规定的时间内或者无限时间进行等待,如果在规定时间内,集合内的文件描述符没有数据变化,则超时接受,就会进入下一个规定时间等待,一旦集合中的文件描述符有数据变化,则其它没有发生数据变化的文件描述符会被踢出到集合外
阻塞IO:只能同时监听一个套接字
非阻塞IO:一直询问,问IO口有没有数据到达。非常浪费cpu资源
信号驱动:主要用于UDP通信
- 事先把需要监听的文件描述符加入到集合中,然后在规定的时间内或者无限时间进行等待,如果在规定时间内,集合内的文件描述符没有数据变化,则超时接受,就会进入下一个规定时间等待,一旦集合中的文件描述符有数据变化,则其它没有发生数据变化的文件描述符会被踢出到集合外
所谓多路复用,指的是通过某个特定的接口(select()、poll()等),来同时监听多路套接字,就达到不需要多进程与多线程,又可以处理多个阻塞套接字的目的。
对于套接字sockfd,其就绪状态有三种:
读就绪,写就绪,异常就绪
-
特点
- 不再由应用程序自己去监听客户端连接。取而代之的是由内核去代替应用程序监视文件,并且可以同时对多个IO口进行监听。由多路复用实现的TCP服务器就叫多路IO转接服务器,也叫做多任务IO服务器
-
函数接口
注意:
- select能监听的文件描述符个数受限于FD_SETSIZE,一般是1024,单纯改变进程打开的文件描述符个数,不能改变select监听文件个数
- 解决1024以下的客户端时用select是合适的,但如果连接的客户端过多,select采用大的事轮询,会大大降低服务器响应效率
- 可以进行跨平台
2|0信号驱动
- 信号驱动原理
- 所谓信号驱动,即用信号来驱使服务器妥善处理多个远端套接字,信号方式的思路比较简单;每当远端有数据到达,那么就在本端触发信号SIGIO,然后利用信号的异步特性来处理远端消息。
套接字sockfd------>只要是远端有数据到来,这个信号就会自动产生,捕捉信号就去执行函数读取数据出来
-
适用场景
- 由于不管套接字接受何种数据,内核一律触发SIGIO,因此这种看似理想的方式,却不适合TCP,因为在TCP中,当客户端发来连接请求,普通数据,数据回执等情况就会触发信号,这就使得服务器端仅凭此信号无法知道下一步要做什么,因此信号驱动模型的服务器模型,一般只适合用于UDP协议
-
信号驱动的实现步骤
- 设置SIGIO的响应函数,信号SIGIO默认会杀死进程,因此必须要设其响应函数,当进程收到信号的时候,说明有数据到达,则在响应函数中接收数据即可
- 设置SIGIO的属主进程,信号SIGIO由内核针对套接字产生,而内核套接字可以在多个应用程序中有效(例如父子进程将套接字遗传给子进程),因此必定指定该信号属主。
- 给套接字设置信号触发模式,也就是让套接字工作在信号模式下。因此在默认情况下,套接字收到数据就不会触发SIGIO,必须将套接字文件描述符设定为异步工作模式,它才会触发该信号
- 设置套接字属主:
fcntl()
案例:
3|0多路复用epoll
-
概念
- epoll是linux下多路复用IO接口,select/poll增强版本,它能显著提高程序在带量并发连接中只有少量活跃的情况下的cpu利用率,因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一个原因就是获取事件的时候,它无需遍历轮询整个被监听的文件描述符集合,只要遍历那些被内核IO事件异步唤醒而加入到ready队列的文件描述符集合即可。
目前epoll是linux大规模并发网络程序中的热门模型。epoll除了提供select/poll那种IO事件的电平触发外,还提供了边沿触发,这就使得用户空间有可能缓存IO的状态,减少epoll_wait, epoll_pwait
- epoll是linux下多路复用IO接口,select/poll增强版本,它能显著提高程序在带量并发连接中只有少量活跃的情况下的cpu利用率,因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一个原因就是获取事件的时候,它无需遍历轮询整个被监听的文件描述符集合,只要遍历那些被内核IO事件异步唤醒而加入到ready队列的文件描述符集合即可。
-
epoll函数介绍
句柄:这种标识叫做句柄(handle), 是数一数二的翻译非常烂的名词. 但看其英文handle, 可以理解, 这就是一种资源的"把柄", 你只需要拥有这个"把柄", 就可以任意支配, 使用这个资源.
所以函数句柄, 可以认为就是函数指针, 它指示了这个了函数在内存中的位置, 通过这个句柄, 便可以轻松调用该函数.
EPOLL_CTL_ADD
向epoll文件描述符epfd的兴趣列表添加一个条目。该条目包括文件描述符fd,一个引用‐
EPOLL_CTL_MOD
将兴趣列表中与fd相关的设置更改为event中指定的新设置。
EPOLL_CTL_DEL
从兴趣列表中删除(注销)目标文件描述符fd。event参数被忽略,可以为NULL
- EPOLLIN:表示对应的文件描述符可以读
- EPOLLOUT:表示对应的文件描述符可以写
- EPOLLRDHUP:表示对应的文件描述符读挂断
- EPOLLPRI:表示对应的文件描述符有紧急的数据要读(带外数据)
- EPOLLERR:表示对应的文件描述符发生错误
- EPOLLHUP:表示对应的文件描述符被挂断
- EPOLLET:将EPOLL设为边缘触发模式
- EPOLLONESHOT:只监听一次事件,当监听完一次事件,如果还需要监听,要重新把socket加入到epoll队列
EPOLLRDHUP是从Linux内核2.6.17开始由GNU引入的事件。
当socket接收到对方关闭连接时的请求之后触发,有可能是TCP连接被对方关闭,也有可能是对方关闭了写操作。
如果不使用EPOLLRDHUP事件,我们也可以单纯的使用EPOLLIN事件然后根据recv函数的返回值来判断socket上收到的是有效数据还是对方关闭连接的请求。
- epoll的用法
如下的代码中,先用epoll_create 创建一个 epol l对象 epfd,再通过 epoll_ctl 将需要监视的 socket 添加到epfd中,最后调用 epoll_wait 阻塞等待数据。
- 原理
epoll 通过两个方面,很好解决了 select/poll 的问题。
第一点,epoll 在内核里
使用红黑树来跟踪进程所有待检测的文件描述符,红黑树是一种高效率的二叉查找树,在内核中使用红黑树来维护待检测的fd集,增删改查的效率都很高。把需要监控的 socket 通过 epoll_ctl() 函数加入内核中的红黑树里,红黑树是个高效的数据结构,增删改一般时间复杂度是 O(logn)。而 select/poll 内核里没有类似 epoll 红黑树这种保存所有待检测的 socket 的数据结构,所以 select/poll 每次操作时都传入整个 socket 集合给内核,而 epoll 因为在内核维护了红黑树,可以保存所有待检测的 socket ,所以只需要传入一个待检测的 socket,减少了内核和用户空间大量的数据拷贝和内存分配。
1.
第二点, epoll 使用
事件驱动的机制,内核里维护了一个链表来记录就绪事件,当某个 socket 有事件发生时,通过回调函数内核会将其加入到这个就绪事件列表中,当用户调用 epoll_wait() 函数时,只会返回有事件发生的文件描述符的个数,不需要像 select/poll 那样轮询扫描整个 socket 集合,大大提高了检测的效率。
从下图你可以看到 epoll 相关的接口作用:
epoll 的方式即使监听的 Socket 数量越多的时候,效率不会大幅度降低,能够同时监听的 Socket 的数目也非常的多了,上限就为系统定义的进程打开的最大文件描述符个数。
- 触发模式——水平触发和边缘触发
epoll 支持两种事件触发模式,分别是水平触发和边缘触发。select/poll 只有水平触发模式。
-
水平触发模式
-
边缘触发模式
一般来说,边缘触发的效率比水平触发的效率要高,因为边缘触发可以减少 epoll_wait 的系统调用次数。
如果使用边缘触发模式,I/O 事件发生时只会通知一次,而且我们不知道到底能读写多少数据,所以在收到通知后应尽可能地读写数据,以免错失读写的机会。因此,我们会循环从文件描述符读写数据,那么如果文件描述符是阻塞的,没有数据可读写时,进程会阻塞在读写函数那里,程序就没办法继续往下执行。所以,边缘触发模式一般和非阻塞 I/O 搭配使用,程序会一直执行 I/O 操作,直到系统调用(如 read 和 write)返回错误。
__EOF__

本文链接:https://www.cnblogs.com/bcc0729/p/17693438.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」