Linux IO模型

一 . 前言

每次说nginx为什么快时,都总会提到“nginx因为使用‘异步、非阻塞、IO多路复用’,所以nginx很快且能扛住高并发……”。那么什么是IO模型呢?有分同步和异步、阻塞和非阻塞,这其中有什么区别呢?

二. Linux IO模型是什么?

在了解Linux IO 之前先记住下面这点

Linux文件系统一切都是文件

2.1文件描述符(fd)

文件分为普通文件,目录文件,设备文件,连接文件等,Linux在操作文件时,都是通过文件描述符(文件描述符是相当于Linux Kernel对文件创建的索引,是一个非负整数。所以对IO的操作调用,实际上都是操作的文件描述符。)。比如常见的文件描述符:0表示标准输入,1表示标准输出,2表示标准错误。
如:

#如下&符号表示的文件描述符。表示将“标准错误”文件描述符重定向到“标准输出”文件描述符
sh test.sh 2>&1

2.2文件IO流程

常说的文件IO指文件数据写入磁盘和从磁盘读出数据的过程。
操作系统层如何定义的一次IO的呢?数据从磁盘读取到内核空间,再从内核空间复制到用户空间,最后进程从用户空间去到数据。这个过程就成为一次OS 层IO。这个过程简化为如下:

  • 读:磁盘–>内核空间–>用户空间–>用户进程(写的过程是反过来)

Linux IO过程有多种模式,不同模式实现不同,性能等也有差异。

2.3 五种 IO模型

1) 同步IO模型(synchronous IO)

  • 阻塞IO(bloking IO)
  • 非阻塞IO(non-blocking IO)
  • 信号驱动式IO(signal-driven IO)
  • 多路复用IO(multiplexing IO)

2) 异步IO(asynchronous IO)

其联系与区别分别如下:
①.阻塞IO模型
在这里插入图片描述
说明:应用进程通过系统调用 recvfrom 接收数据,但由于内核还未准备好数据包,应用进程就会被阻塞住,一直等到内核将数据准备好并从内核空间拷贝到用户空间,recvfrom 才能完成数据包复制工作,应用进程才能结束阻塞状态。

  • 优点:应用进程实现简单
  • 缺点:存在阻塞,比较适合并发小,时效低的场景。

②非阻塞IO模型
在这里插入图片描述
说明:应用进程与内核交互时,在目的达到之前,不在一味地等待,而是直接反馈(即“非阻塞”),而是不断的轮询检查内核是否将数据准备好。如果在某一次轮询发现数据准备好了,就将数据从内核空间拷贝到用户空间。

  • 优点:用户进程能立即拿到响应,不阻塞请求
  • 缺点:需要不断轮询,系统资源浪费

③信号驱动式IO模型
在这里插入图片描述
说明:用户进程在读取文件时通知内核,如果某个socket的某个事件发生时,请向我发送一个信号。在收到信号后,信号对应的处理函数会进行后续的处理。
也就是,
第1步,应用进程预先向内核注册一个信号处理函数,然后用户进程直接返回(即“不阻塞”),当内核数据准备就绪时会发送一个信号给应用进程;
第2步,应用进程收到信号后,用户进程便在信号处理函数中有内核开始把数据从内核空间拷贝到用户空间。(数据拷贝时,应用基础是阻塞的。)

  • 优点:高效,阻塞时间段、系统压力小(不需要不断轮询)
  • 缺点:实现起来要复杂一些

④多路复用IO
在这里插入图片描述
说明:多个进程的IO可以注册到同一个管道上,监听这个管道中的“信号”,这个管道会统一和内核进行交互。当管道中的某一个请求需要的数据准备好之后,进程在把数据从内核空间拷贝到用户空间。
IO多路复用就是多了个select函数(poll,epoll函数),多个进程的IO可以注册到同一个select上,当用户进程调用该select,select会监听所有注册好的IO,如果所有被监听的IO数据都没有准备好,那select调用进程会阻塞(所以它也是“阻塞式的IO”。当任意一个IO所需的数据准备好,才会有select的返回,才会再发送recvfrom调用触发从内核空间到用户空间的数据拷贝。
多路开关
多路复用IO就像上图拨开关一样,哪条连接数据,就拨向哪条。这里中间一条链路就类似于IO管道,两边分别代表不同的用户进程和内核进程。
以上几种都是同步IO,因为数据从内核空间拷贝到用户空间时都是同步的。

⑤异步IO
在这里插入图片描述
说明:用户进程发起aio_read操作滞后,给内核传递一些关键信息,如文件描述符、缓冲区指针、缓冲区大小等,告诉内核当整个操作完成时,如何通知进程,然后用户进程就去干其他事情了(即“异步”)。内核在收到用户进程的aio_read之后,会like返回,然后内核开始等待数据准备,数据准备好后,直接将数据从内核空间拷贝到用户空间(“用户进程感受不到,所以不阻塞用户进程”)。然后在通知进程本次IO完成。

横向对比集中IO模型

在这里插入图片描述

三.Linux中的select、poll、epoll有什么区别?

三个都是 IO 多路复用的机制,可以监视多个描述符的读 / 写等事件,一旦某个描述符就绪(一般是读或者写事件发生了),就能够将发生的事件通知给关心的应用程序去处理该事件。

select
因为poll 和 select **仅仅是函数接口有所不同,本质一样。**不过相比较 select 而言,poll 可以支持大于 1024 个文件描述符
在这里插入图片描述

#include<sys/select.h>
int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);

参数:

  • ndfds參数指定被监听文件描写叙述符个数,它通常被设为select监听的全部文件描写叙述符加1。
  • readfds,writefds,exceptfds參数分别指向可读,可写和异常事件,应用程序通过将自己感兴趣的文件描写叙述符增加到相应的集合中去,select调用返回时。内核将改动他们来通知应用程序哪些文件描写叙述符已经就绪
  • timeout为超时时间,select调用成功返回就绪的文件描写叙述符个数

select的特点
select的内部实现调用了poll(以下会写道,所以读者能够先跳过这里,去读poll,然后在回头一起看这个)。全部它和poll的特点同样。
poll的特点例如以下
(1)将用户传入的pollfd数据(相应select的描写叙述符集合)复制到内核空间,这个拷贝过程时间复杂度为O(N)
(2)挨个查询每个文件描写叙述符的状态,假设无就绪的文件描写叙述符,则进程就会挂起等待,知道发生超时或设备驱动再次唤醒它。然后它再次遍历全部的文件描写叙述符,找出发生事件的文件描写叙述符。因为其共遍历2次文件文件描写叙述符,所以其时间复杂度为O(N)
(3)将获得的数据拷贝至用户空间。时间复杂度又是O(N)

小结
select & poll:

  • 每次调用 select,都需要把 fd 集合从用户态拷贝到内核态,这个开销在 fd 很多时会很大
  • 同时每次调用 select 都需要在内核遍历传递进来的所有 fd,这个开销在 fd 很多时也很大
  • select 支持的文件描述符数量只有 1024,非常小;而poll支持的fd数量超过1024

epoll

epoll是在Linux 2.6内核中提出的,是之前的select和poll的增强版本.

  • 相对于select和poll来说,epoll更加灵活,没有描述符限制。(文件描述符没有限制)
  • epoll使用一个文件描述符管理多个描述符,将用户关心的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。(时间复杂度低,更高效)
  • epoll在内核实现中是依据每个fd上的callback函数来实现的,仅仅有活跃的fd才会主动调用callback,其它的fd则不会。(更节省资源也就更高效)。

参考:
https://mp.weixin.qq.com/s?__biz=Mzg3MjA4MTExMw==&mid=2247484746&idx=1&sn=c0a7f9129d780786cabfcac0a8aa6bb7&source=41#wechat_redirect

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值