Linux网络IO模型

前4个为同步,后面一个为异步

阻塞I/O

IO复用模型 

非阻塞IO模型(了解即可) 

非阻塞IO通过进程反复调用IO函数(多次系统调用, 并马上返回) ; 在数据拷贝的过程中, 进程是阻塞的

信号驱动IO (了解即可) 

套接口进行信号驱动I/O,并安装一个信号处理函数, 进程继续运行并不阻塞。
当数据准备好时, 进程会收到一个SIGIO信号, 可以在信号处理函数中调用I/O操作函数处理数据。 

异步IO模型(了解即可)

当一个异步过程调用发出后, 调用者不能立刻得到结果。
实际处理这个调用的部件在完成后, 通过状态、 通知和回调来通知调用者的输入输出操作。 
 

epoll高效原理和底层机制分析

进程阻塞。调用recv后会把进程a放入其他进程中处理。进程a阻塞,不会往下执行代码, 也不会占用 cpu 资源。当 socket 接收到数据后, 操作系统将该 socket 等待队列上的进程重新放回
到工作队列​​​​​​​,recv 就可以返回接收到的数据。

 内核接收网络数据全过程

进程在 recv 阻塞期间, 计算机收到了对端传送的数据(步骤①) 。 数据经
由网卡传送到内存(步骤②) , 然后网卡通过中断信号通知 cpu 有数据到达, cpu
执行中断程序(步骤③) 。 此处的中断程序主要有两项功能, 先将网络数据写入
到对应 socket 的接收缓冲区里面(步骤④) , 再唤醒进程 A(步骤⑤) , 重新将
进程 A 放入工作队列中。为了提高处理速度, 操作系统会维护端口号到 socket 的索引结构, 以快速读取​​​​​​​

select 的用法

int fds[] = 存放需要监听的 socket
while(1){
int n = select(..., fds, ...)
for(int i=0; i < fds.count; i++){
if(FD_ISSET(fds[i], ...)){
//fds[i]的数据处理
}}} 
其一, 每次调用 select 都需要将进程加入到所有被监视 socket 的等待队列,每次唤醒都需要从每个队列中移除, 都必须要进行遍历。 而且每次都要将整个 fds列表传递给内核, 有一定的开销。 正是因为遍历操作开销大, 出于效率的考量,才会规定 select 的最大监视数量, 默认只能监视 1024 个 socket。
其二, 进程被唤醒后, 程序并不知道哪些 socket 收到数据, 还需要遍历一次。

 epoll 的设计思路

措施一: 功能分离
select 低效的原因之一是将“维护等待队列” 和“阻塞进程” 两个步骤合二为一。 每次调用 select 都需要这两步操作, 然而大多数应用场景中, 需要监视的socket 相对固定, 并不需要每次都修改。 epoll 将这两个操作分开, 先用 epoll_ctl维护等待队列, 再调用 epoll_wait 阻塞进程。 显而易见的, 效率就能得到提升。
相比 select, epoll 拆分了功能

int epfd = epoll_create(...);
epoll_ctl(epfd, ...); //将所有需要监听的 socket 添加到 epfd 中
while(1){
int n = epoll_wait(...)
for(接收到数据的 socket){
//处理
}}

措施二: 就绪列表
select 低效的另一个原因在于程序不知道哪些 socket 收到数据, 只能一个个遍历。 如果内核维护一个“就绪列表” , 引用收到数据的 socket, 就能避免遍历。

epoll 的原理和流程

当某个进程调用 epoll_create 方法时, 内核会创建一个 eventpoll 对象(也就是程序中 epfd 所代表的对象) 。 eventpoll 对象也是文件系统中的一员, 和 socket一样, 它也会有等待队列。
创建 epoll 对象后, 可以用 epoll_ctl 添加或删除所要监听的 socket。 以添加socket 为例, 如下图, 如果通过 epoll_ctl 添加 sock1、 sock2 和 sock3 的监视, 内核会将 eventpoll 添加到这三个 socket 的等待队列中。

 当 socket 收到数据后, 中断程序会操作 eventpoll 对象, 而不是直接操作进程。 中断程序会给 eventpoll 的“就绪列表” 添加 socket 引用。 如下图展示的是sock2 和 sock3 收到数据后, 中断程序让 rdlist 引用这两个 socket。

eventpoll 对象相当于是 socket 和进程之间的中介, socket 的数据接收并不直接影响进程, 而是通过改变 eventpoll 的就绪列表来改变进程状态。
当程序执行到 epoll_wait 时, 如果 rdlist 已经引用了 socket, 那么 epoll_wait直接返回, 如果 rdlist 为空, 阻塞进程。
假设计算机中正在运行进程 A 和进程 B, 在某时刻进程 A 运行到了 epoll_wait语句。 如下图所示, 内核会将进程 A 放入 eventpoll 的等待队列中, 阻塞进程。 

当 socket 接收到数据, 中断程序一方面修改 rdlist, 另一方面唤醒 eventpoll等待队列中的进程, 进程 A 再次进入运行状态。 也因为 rdlist 的存在, 进程 A 可以知道哪些 socket 发生了变化。 

红黑树是一种自平衡二叉查找树, 搜索、 插入和删除时间复杂度都是O(log(N)), 效率较好。 epoll 使用了红黑树作为索引结构维护socket列表。rdlist双向链表。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux内核的IO模型主要包括阻塞IO、非阻塞IO、多路复用IO和异步IO。下面我将逐个介绍这些模型的特点。 1. 阻塞IO(Blocking IO):当应用程序发起一个IO操作后,内核会一直阻塞等待,直到IO操作完成才返回结果给应用程序。在这期间,应用程序是被阻塞的,无法进行其他操作。阻塞IO模型适用于对实时性要求不高的场景,简单易用,但会导致资源浪费。 2. 非阻塞IO(Non-Blocking IO):当应用程序发起一个IO操作后,内核会立即返回一个结果给应用程序,无论IO操作是否完成。如果IO操作还未完成,应用程序可以继续做其他事情,而不需要一直等待。应用程序可以通过轮询来检查IO操作的状态,直到操作完成。非阻塞IO模型可以提高系统的并发性能,但需要应用程序自己处理轮询逻辑。 3. 多路复用IO(Multiplexing IO):多路复用IO模型通过一个系统调用(如select、poll、epoll等)来同时监听多个IO事件,当有任意一个IO事件就绪时,内核会通知应用程序进行处理。这种模型避免了阻塞和轮询的问题,可以同时处理多个IO操作,提高系统的并发性能。 4. 异步IO(Asynchronous IO):异步IO模型中,应用程序发起一个IO操作后,可以立即返回继续执行其他操作,而不需要等待IO操作完成。当IO操作完成后,内核会通知应用程序,并返回结果。异步IO模型通过回调函数来处理IO完成的通知,相比于其他模型,可以更高效地处理大量的IO操作。 这些IO模型在不同的场景下有各自的优劣,选择合适的IO模型可以提高系统的性能和响应能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值