【UNIX网络编程】5种I/O模型

目录

阻塞 I/O 模型

非阻塞 I/O 模型

I/O 复用模型

信号驱动式 I/O 模型

异步 I/O 模型

五种 I/O 对比


在阅读本文之前,你应该先了解一些基础概念。什么是 I/O? 一次 I/O 的过程是怎么样的?

I/O 简单来说就是在一端进行数据输入,然后通过内核后在另一端进行数据输出

一次 I/O 的过程中通常会涉及如下的操作:

  • 在进程A进行数据输入,数据从应用层拷贝到内核层。
  • 内核层通过通信机制,将数据拷贝到进程B的内核层。
  • 进程B将数据从内核层再拷贝到自己的应用层进行数据输出。

UNIX 下五种 I/O 模型:

  1. 阻塞 I/O 模型
  2. 非阻塞 I/O 模型
  3. I/O 复用模型
  4. 信号驱动式 I/O 模型
  5. 异步 I/O 模型

阻塞 I/O 模型

在默认情况下,所有的套接字都是阻塞的。下面是一个阻塞 I/ O 的例子。

进程调用 recvfrom 会发起系统调用,阻塞等待内核将数据准备好并返回。直到数据被复制到应用缓冲区或者发生错误的时候才会返回。最常见的错误就是系统调用被信号中断。

所以从上面这个例子能看出,进程调用 recvfrom 开始到数据返回这个过程中,进程会一直阻塞等待,直到成功返回后才能去处理其他任务。

非阻塞 I/O 模型

如果大家能够理解阻塞 I/O 模型的话,那么非阻塞 I/O 估计也能想到了。

非阻塞 I/O 就是进程发起系统调用后去检查内核有没有数据准备好,如果数据没有准备好,那么我们就不等待了,直接返回。如果哪次系统调用时,发现内核准备好数据了,才会发起数据的拷贝。

那调用失败不就返回了?那我们怎么能确定数据哪次来呢?

正是因为这种非阻塞的特性,调用后就会立马返回,所以非阻塞 I/O 是要搭配轮询来使用的,也就是我们要在循环里面调用非阻塞 I/O,让其不断地去检测内核,这种模型要消耗大量CPU的。

下面举一个经典的老王烧水的例子来深入理解一下阻塞 I/O 模型 和 非阻塞 I/O 模型:

  • 阻塞 I/O 模型 实际上就是老王去烧水,他一直在旁边等待,期间什么也不做,直到水开了拿走水。
  • 非阻塞 I/O 模型 就是老王去烧水,他把水放上面就不管了,去干别的事情,每隔一段时间回来看一眼水开没开,直到水开了拿走水。

I/O 复用模型

常见的 I/O 复用模型有 select、poll 和 epoll。这里使用 select 来举例子。使用 I/O 复用的时候,系统调用会阻塞在 select 上,而不是真正的 I/O 的系统调用。

我们调用 select 会阻塞在 select 的系统调用上,去等待内核将数据准备好。数据准备好后会返回给进程通知可读了,这时候再去调用 recvfrom 去直接复制数据到应用层缓冲区返回。

大家可能感觉相比之下这不是进行了两次系统调用吗,那效率不更差了?但 select 的优势在于它可以同时去监听多个文件描述符。

信号驱动式 I/O 模型

利用信号,在内核文件描述符就绪后发送 SIGIO 通知我们。

首先通过sigaction系统调用启用信号驱动功能并设置信号处理函数,该调用会立即返回,不会阻塞当前进程。当数据报就绪时,内核会向进程发送SIGIO信号。此时可以选择两种处理方式:一是在信号处理函数中直接调用recvfrom读取数据,然后通知主程序进行后续处理;二是简单地通知主程序有数据可读。这种模式的主要优点是避免了进程在等待数据时的阻塞,主程序可以持续运行,只需等待信号处理程序的通知即可,无论是数据已准备好处理还是数据报已就绪可读。 

异步 I/O 模型

异步 I/O 模型是 POSIX规范定义的。这个模型的工作机制是通知内核去启动某个操作(可以指定让其将数据从内核复制到我们自己的缓冲区),然后等待操作完成之后,内核会去通知我们 I/O 操作何时完成了。

与信号驱动式 I/O 模型对比 ,异步 I/O 会在内核完成任务后通知我们任务完成了,而信号驱动式 I/O 是通知我们数据准备好了,让应用去处理数据。

我们使用 POSIX 的异步 I/O 函数(函数名以 aio_ 或 lio_ 开头),通过调用 aio_read 函数向内核提交 I/O 请求。在调用时,我们需要提供与常规 read 函数相同的三个参数:文件描述符、数据缓冲区指针和缓冲区大小,同时还需要指定文件偏移量(类似于lseek的功能)。此外,我们还需要设置 I/O 完成时的通知方式。调用后,系统会立即返回,而进程可以继续执行其他任务,不会被I/O 操作阻塞。

五种 I/O 对比

阻塞I/O模型:老王把水壶放上炉子后,搬个凳子坐在旁边盯着水壶,全程不离开,直到水烧开才去做其他事。

特点:全程等待,CPU利用率低

    非阻塞I/O模型:老王把水壶放上炉子后,每隔5分钟过来看一眼(轮询),期间可以看电视/扫地,但每次检查都消耗精力,发现水开才停止检查。

    特点:主动轮询消耗CPU资源

    I/O复用模型:老王在厨房装了智能监控器(select),同时监控水壶、微波炉和烤箱。当任何一个设备完成工作时,监控器会提醒老王,这时他才去处理对应的设备。

    特点:单线程监控多任务,效率更高

    信号驱动I/O模型:老王给水壶装上报警器(SIGIO信号),设置好后就去浇花。当水开时报警器会响,老王听到后过来关火。

    特点:由内核主动通知,但数据仍需自己取

    异步I/O模型:老王使用全自动水壶(aio_read),设置好水量后完全不管。水壶会自己烧水、自动断电,并通过手机推送通知老王"水已烧好并倒入保温杯"。

    特点:内核完成所有工作后通知结果

    评论
    成就一亿技术人!
    拼手气红包6.0元
    还能输入1000个字符
     
    红包 添加红包
    表情包 插入表情
     条评论被折叠 查看
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值