5种IO模型和IO多路复用详解

I/O模型概述

  • 同步I/O
    • 阻塞式I/O
    • 非阻塞式I/O
    • I/O复用
    • 信号驱动I/O
  • 异步I/O
    • POSIX的异步 I/O 函数

输入操作流程

输入操作一般分为两个流程

  1. 等待数据准备好
    • 对于在套接字上的操作,就是等待数据在网络中到达内核缓冲区
  2. 从内核向进程中复制数据
    • 就是把数据从内核缓冲区中复制到应用进程缓冲区

同步I/O和异步I/O的区别

同步 I/O 操作导致请求进程阻塞

异步 I/O 操作不导致请求进程阻塞

阻塞式 I/O 、非阻塞式 I/O 、I/O 复用、信号驱动 I/O 都是同步 I/O,因为在输入流程的第二步上都会将进程阻塞,

而异步 I/O 模型只有 POSIX 定义的异步 I/O 与之相匹配

5种I/O模型

阻塞式I/O

在这里插入图片描述

调用recvfrom函数后用户进程会阻塞等待数据到达内核缓冲区并且拷贝到应用进程缓冲区后返回

非阻塞式I/O

1483366389700024047.png

循环调用recvfrom函数,如果数据没到达内核缓冲区就立即返回一个错误,如果数据到达了就复制数据到应用进程缓冲区(循环调用的过程叫轮询

I/O复用

1483366448898081321.png
I/O 复用又叫 I/O 多路复用

I/O 复用模型下应用进程会被阻塞两次,分别对应输入流程的两个步骤,

  1. 第一部分调用 select 函数阻塞直到数据在内核中准备好,函数返回;(I/O 多路复用中不仅仅只有 select,还有 poll 和 epoll)
  2. 第二部分调用 recvfrom 函数把数据复制到应用进程的缓冲区

I/O 多路复用和其他的 I/O 模型不同在他可以一次追踪多个 I/O 流,I/O 复用是在单线程下追踪多个 I/O 流,与他相似的是在多线程下,每个线程追踪一个 I/O 流

信号驱动I/O

1483366503112041652

首先开启套接字的信号驱动 I/O 功能,相当于调用函数告诉内核接收到数据时传递信号给应用进程,然后应用进程就可以在信号处理函数中调用recvfrom从内核中读取数据,或者通知主循环读取,和 I/O 复用的是在第一部分也就是建立信号处理程序是直接返回的,是不会阻塞应用进程的

异步I/O

异步 I/O 由POSIX规范定义,工作方式是直接将输入流程的两个部分都交给内核完成,通过调用 aio_read 函数(传递文件描述符,缓冲区指针,缓冲区大小等),调用后直接返回,让内核接收到数据并且把数据复制到应用程序的缓冲区(传入的缓冲区指针)后发送信号给应用进程,进程在对应的信号处理程序中处理数据

图片引用自:http://www.tianshouzhi.com/api/tutorials/netty/221

I/O 多路复用的 select/poll/epoll 详解

以下内容需要再考证

select

select 函数参数主要有文件描述符的数组,以及一个32个整数大小的bitmap(1024位)

调用 select 后,内核会先把文件描述符数组复制到内核中,内核会查看每一个文件描述符是否有数据到达,到了就会把bitmap置位,完成后然后返回,(这期间阻塞应用进程),应用进程遍历文件描述符,通过bitmap就可以知道数据是否到达并调用 recvfrom 来读取数据到进程的缓冲区中

复制到内核中交给内核遍历判断的好处是避免频繁在用户态和内核态间切换,速度更快,因为如果是应用进程直接遍历判断还是需要切换到内核态的,切换是需要额外开销的
select

缺点:

  1. bitmap大小1024位,最多支持追踪1024个 I/O 流
  2. bitmap置位后导致下一次循环中不能重用,需要下一次循环开始时重新遍历设置
  3. 文件描述符拷贝到内核态会额外开销
  4. select 返回后还是需要遍历每个文件描述符判断一遍,O(n)

poll

struct pollfd{
	int fd;//文件描述符
    short events;//读写事件
    short revents;//用于置位
}

调用 poll 函数,参数主要是 pollfd 数组(充当 select 函数中的文件描述符数组和位图功能),同样会复制文件描述符数组到内核,但是使用了 revent 置位,函数返回后,用户进程遍历文件描述符时,发现 revent 被置位了,进行读取数据并将 revent 字段复位
poll.png

优点:

  1. 解决了文件描述符上限问题
  2. 解决了bitmap被置位后不可重用的问题

缺点:

  1. 文件描述符数组拷贝到内核态会额外开销
  2. poll 返回后还是需要遍历每个文件描述符判断一遍,O(n)

epoll


epfd 类似 pollfd 有 fd 和 events 字段(似乎是一个双向链表(待考究))

不需要拷贝 epfd 到内核态,并且 epoll 会把 epfd 重新排序,把有数据的文件描述符排到头部,返回一个整数代表有数据的文件描述符数量,应用进程遍历时就不需要用 O(n) 的复杂度遍历了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J3huT5pn-1619186298656)(D:\Desktop\note\linux与操作系统\epoll.png)]

优点:

  1. 解决拷贝文件描述符的开销
  2. 解决了函数返回后 O(n) 的遍历复杂度,复杂度O(1)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值