Linux系统I/O模型和网络I/O模型教程

2021-06-12 19:32:07

计算机I/O

  • I/O在计算机中指Input/Output,

Linux服务器 I/O 原理和流程

Linux系统I/O模型和网络I/O模型

  • 基于Linux服务器I/O原理提出的数据交换模式架构
  • 说人话:IO模型就是一种数据传递的设计方案

太长不看版本

同步和异步

  • 同步:使用天然气老水壶烧水,得盯着
  • 异步:使用物联网电水壶烧水,水烧开了自动停了还会发个短信提示水烧开了

阻塞和非阻塞

  • 阻塞:烧水时得在旁边站着
  • 非阻塞:烧水时候,可以再取接几壶水拿过来都烧上

信号驱动模式I/O

  • 去饭店吃饭,服务员点菜,等着菜端上来

异步I/O模型

  • 点外卖,点完餐干别的去就行了

五种IO模式比较

  • 第一种全程阻塞,大部分时间都在等待
  • 在第一阶段不断地轮询等待,阻塞了
  • I/O复用,全程阻塞,但是让代理程序去阻塞,但还要盯着代理
  • 信号驱动,后半段阻塞
  • 异步I/O,全程不阻塞
          • -
          • -

以下为细心详解

理解Linux系统I/O模型和网络I/O模型的意义

C10K问题

  • 每一个用户都必须与服务器保持TCP连接才能进行实时的数据交互。
  • 最初的服务器都是基于进程/线程模型的,新到来一个TCP连接,就需要分配1个进程(或者线程)。
  • 当用户数超过1万(10K)时,很多设计不良的网络服务程序性能都将急剧下降. 甚至瘫痪
  • C10K问题根源基于操作系统内核IO产生的,并不能通过升级计算机硬件解决
  • 解决C10K的根本还是要从操作系统IO入手

解决C10K问题操作系统提供的功能

  • FreeBSD推出了kqueue,
  • Linux推出了Epoll,
  • Windows推出了IOCP,
  • Solaris推出了/dev/poll

Linux的Epoll

  • Linux是互联网企业中使用率最高的操作系统
  • Epoll就成为C10K killer. 高并发. 高性能. 异步非阻塞这些技术的代名词
  • Epoll技术的编程模型就是异步非阻塞回调,也可以叫做Reactor,事件驱动,事件轮循(EventLoop)。
  • Nginx,libevent,node.js这些就是Epoll时代的产物。
  • Epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll。
  • Epoll是Linux网络I/O模型中IO多路复用模型(或称作事件驱动I/O模型)的一种实现

Linux系统I/O模型

  • 简单理解为用户进程调用系统内核的方式
  • 这些个都是理论模型啊!

同步和异步

  • 关注事件处理的消息通信机制,
  • 在等待事件处理结果时,被调用者是否提供完成通知。

同步 :synchronous

  • 调用者等待被调用者返回消息后才能继续执⾏,如果被调⽤用者不提供消息返回则为同步,同步需要调⽤者主动询问事情是否处理完成。
  • 进程发出调用请求后,内核不提供通知机制,即文件IO处理完成后不通知进程,需要进程⾃自⼰去问内核是否处理完成。
  • 说人话版:鸡娃写作业,娃啥时候写完也不知道,一会瞅一眼,一会瞅一眼,眼睁睁看看娃写完!

异步 :asynchronous

  • 被调用者通过状态. 通知或回调机制主动通知调用者,即异步会主动返回被调用者的状态给调⽤者。
  • 进程发出调用请求后,内核会在调用处理理完成后主动返回调用结果给进程。
  • 说人话版:鸡娃写作业,娃是自鸡娃,写完作业会主动出来告诉一声说写完了!

阻塞和非阻塞

  • 关注调用者在等待结果返回之前所处的状态
  • 在等待事件处理结果时,调用者是否可以接受新的请求。

阻塞 :blocking

  • 指IO操作需要彻底完成后才返回到用户空间,调用结果返回之前,调用者被挂起,干不了别的事情。
  • 进程发出调用请求后,进程被挂起,直到内核返回结果进程才被释放
  • 说人话版:鸡娃写作业,坐在娃旁边啥也不干直到娃写完作业!

非阻塞:nonblocking

  • 指IO操作被调用后立即返回给用户一个状态值,而无需等到IO操作彻底完成,在最终的调用结果返回之前,调用者不会被挂起,可以去做别的事情。
  • 进程发出调用请求后,进程被释放,进程可以接受新的请求,并将请求再次递交给内核空间进行处理,周而复始
  • 说人话版:鸡娃写作业,娃是牛蛙,先给他一本奥数,他就开始做了,娃妈出门买了一套FCE真题回来给牛蛙,牛蛙也接了,娃妈又在某宝上拍了一套5+3,娃又默默的接下了,娃妈做好了晚饭送进去,娃边吃边坐,娃妈看电视,娃做,娃妈睡觉,娃做,娃妈起床啦,娃还在做……

同异阻非排列组合

  • 同步阻塞 :渣娃写作业写不写完也不知道吭声,必须坐旁边紧盯着
  • 同步非阻塞 :普娃写作业写不写完也不知道吭声,但能自己写,娃写作业的时候还能扫扫地. 做做饭啥的
  • 异步阻塞 : 鸡娃,写完作业知道告诉一声,但是必须在娃写作业的时候在旁边呆着,啥也不能干。
  • 异步非阻塞 : 牛蛙,不但写完作业知道告诉一声,而写写作业也不用看着,并且你给他啥他都能给搞定,还不耽误干家务

Linux网络I/O模型

  • Linux网络模型即在系统I/O模型和I/O流程的基础上添加了网络部分的内容
  • 阻塞型. 非阻塞型. 复用型. 信号驱动型. 异步龙晶杀鬼型

阻塞型 I/O 模型(blocking IO) 属于同步阻塞

  • 阻塞IO模型是最简单的I/O模型,用户线程在内核进行IO操作时被阻塞
  • 用户线程通过系统调用read发起I/O读操作,由用户空间转到内核空间。内核等到数据包到达后,然后将接收的数据拷贝到用户空间,完成read操作
  • 用户需要等待read将数据读取到buffer后,才继续处理接收的数据。整个I/O请求的过程中,用户线程是被阻塞的,这导致用户在发起IO请求时,不能做任何事情,对CPU的资源利用率不够
阻塞型特点:
  • 优点:程序简单,在阻塞等待数据期间进程/线程挂起,基本不会占用 CPU 资源
  • 缺点:每个连接需要独立的进程/线程单独处理,当并发请求量大时为了维护程序,内存. 线程切换开销较大,
同步阻塞模型应用举例:
  • apache HTTPD早期的preforck模式使用的是这种模式。
  • 程序向内核发送IO请求后⼀直等待内核响应,
  • 如果内核处理请求的IO操作不能立即返回,则进程将一直等待并不再接受新的用户请求,
  • 并由进程轮训查看IO是否完成,完成后进程将IO结果返回给Client,
  • 在IO没有返回期间进程不能接受其他客户的请求,必须进程自己去查看IO是否完成,
  • 这种方式简单,但是比较慢,用的比较少。

非阻塞型 I/O 模型 (nonblocking IO) 属于同步非阻塞

  • 用户请求进程向内核发起IO请求时⽴立即返回,但并未读取到任何数据,
  • 进程需要不断地发起IO请求,直到数据到达进程空间的内存后,才真正读取到数据并继续执行,即前期需要一次 “轮询”去查看请求是否数据是否准备报,
非阻塞型存在两个问题:
  • 1.如果有大量文件描述符都要等,那么就得一个一个的read,这会带来⼤量的Context Switch(read是系统调用,每调用一次就得在用户态和核心态切换一次),
  • 2.轮询的时间不好把握,这⾥是要猜多久之后数据才能到,等待时间设的太⻓,程序响应延迟就过⼤,但是设的太短,就会造成过于频繁的重试,⼲耗CPU而已,所以是⽐较浪费CPU的方式,因此一般很少直接使用这种模型,而是在其他IO模型中使用非阻塞IO这一特性。
同步非阻塞模型应用举例:
  • 应⽤用进程向内核发送请IO求后一直等待内核响应,
  • 如果内核处理理请求的IO操作不不能⽴立即返回IO结果,进程将不不再等待,
  • 而且继续处理理其他请求,但是仍然需要进程隔一段时间就要查看内核IO是否完成。
  • 每一个文件描述符对应的IO是由一个线程监控和处理,
  • 当一个应用进程循环调用recvfrom时,称之为轮询polling。这么做往往会耗费大量CPU时间,实际使用很少。

IO多路复用型I/O 模型(IO multiplexing) 属于加代理的同步阻塞型

  • 也叫做事件驱动型I/O 模型(Event Driven IO)
  • 多路复用IO指一个线程可以同时(实际是交替实现,即并发完成)监控和处理多个文件描述符对应各自的IO,即复用同一个线程
  • 一个线程之所以能实现同时处理多个IO,是因为这个线程调用了内核中的SELECT,POLL或EPOLL等系统调用,从而实现多路复用IO
  • 说人话版:添加了一个事件代理线程,帮着询问内核干没干完
IO多路复用型代理人机制:
  • I/O multiplexing 主要包括:select,poll,epoll三种系统调用,select/poll/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。
  • I/O multiplexing 基本原理就是select/poll/epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。
  • 当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,
  • 当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。
IO多路复用型特点:
  • 优点:可以基于一个阻塞对象,同时在多个描述符上等待就绪,而不是使用多个线程(每个文件描述符一个线程),这样可以大大节省系统资源
  • 缺点:当连接数较少时效率相比多线程+阻塞 I/O 模型效率较低,可能延迟更大,因为单个连接处理需要 2 次系统调用,占用时间会有增加
IO多路复用模型适用场景:
  • 当客户端处理多个描述符时(一般是交互式输入和网络套接口),必须使用I/O复用
  • 当一个客户端同时处理多个套接字时,此情况可能的但很少出现
  • 当一个服务器既要处理监听套接字,又要处理已连接套接字,一般也要用到I/O复用
  • 当一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用
  • 当一个服务器要处理多个服务或多个协议,一般要使用I/O复用
IO多路复用模型应用举例:
  • Apache prefork- 主进程+多进程/单线程+select,
  • Apache work- 主进程+多进程/多线程+poll模式,
  • Apache event- 主进程+多进程/监听线程+多线程+epoll模式,

信号驱动式 I/O 模型 (signal-driven IO) 属于异步半阻塞

  • 信号驱动是内核通知用户进程何时开始一个I/O操作
  • 信号驱动I/O的意思就是进程现在不用傻等着,也不用去轮询,可以继续接受其他请求,不用等待内核响应。
  • 内核收到进程请求后进行的IO如果不能立即返回,就由内核等待结果,内核在数据就绪时,发送信号通知进程。
  • 此模型的优势在于等待数据报到达期间进程不被阻塞。用户主程序可以继续执行,只要等待来自信号处理函数的通知。
信号驱动式型特点:
  • 当数据准备好时,进程会收到一个 SIGIO 信号,可以在信号处理函数中调用 I/O 操作函数处理数据。
  • 优点:线程并没有在等待数据时被阻塞,内核直接返回调用接收信号,不影响进程继续处理其他请求因此可以提高资源的利用率
  • 缺点:信号 I/O 在大量 IO 操作时可能会因为信号队列溢出导致没法通知
信号驱动式型应用举例:
  • 主程序进程通过系统调用sigaction ,并注册一个信号处理的回调函数,
  • 该调用会立即返回,然后主程序可以继续向下执行,
  • 当有I/O操作准备就绪,即内核数据就绪时,内核会为该进程产生一个SIGIO信号,并回调注册的信号回调函数,
  • 这样就可以在信号回调函数中系统调用recvfrom 获取数据,将用户进程所需要的数据从内核空间拷贝到用户空间。
  • 在信号驱动式 I/O 模型中,应用程序使用套接口进行信号驱动 I/O,并安装一个信号处理函数,进程继续运行并不阻塞

异步 I/O 模型 (asynchronous IO) 属于异步非阻塞

  • 异步I/O是由内核通知用户进程I/O操作何时完
  • 异步I/O不是顺序执行。
  • 异步IO两个阶段,进程都是非阻塞的。
异步 I/O 模型机制
  • 用户进程进行aio\_read系统调用之后,无论内核数据是否准备好,都会直接返回给用户进程,然后用户态进程可以去做别的事情。
  • 等到socket数据准备好了,内核直接复制数据给进程,然后从内核向进程发送通知。
  • 异步IO当内核通知触发信号处理程序时,应用程序进程在从内核空间缓冲区拷贝数据到用户空间缓冲区这个阶段不需要阻塞,内核直接通知用户线程可以进行后续操作了
  • 说人话版:鸡娃时把作业丢给娃就完事了,娃昨晚了会喊一声‘妈,我做完了’,还会把做完的作业放老娘桌上!
异步 I/O 模型特点
  • 优点:异步 I/O 能够充分利用 DMA 特性,让 I/O 操作与计算重叠。
  • 缺点:要实现真正的异步 I/O,操作系统需要做大量的工作。
异步 I/O模型应用举例
  • 目前 Windows 下通过 IOCP 实现了真正的异步 I/O,
  • Linux 系统下,Linux 2.6才引入AIO 并不完善,因此
  • Linux提供了AIO库函数实现异步,但是用的很少。目前有很多开源的异步IO库,例如libevent. libev. libuv。
  • 在 Linux 下实现高并发网络编程时以 IO 复用模型模式+多线程任务的架构基本可以满足需求。

五种网络IO模型比较

  • 这五种 I/O 模型中,越往后,阻塞越少,理论上效率也是最优
  • 前四种属于同步 I/O,因为其中真正的 I/O操作(recvfrom)将阻塞进程/线程,
  • 异步 I/O 模型才与 POSIX 定义的异步 I/O 相匹配

IO模型实现举例

Nginx 中的IO模型实现

  • Nginx支持在多种不同的操作系统实现不同的事件驱动模型,
  • Nginx在不同的操作系统甚至是不同的系统版本上面的实现方式不尽相同,主要有以下实现方式:
  1. select:
    select库是在linux和windows平台都基本支持的 事件驱动模型库,并且在接口的定义也基本相同,只是部分参数的含义略有差异,最大并发限制1024,是最早期的事件驱动模型。
  2. poll:
    在Linux 的基本驱动模型,windows不支持此驱动模型,是select的升级版,取消了最大的并发限制,在编译nginx的时候可以使用–with-poll\_module和–without-poll\_module这两个指定是否编译select库。
  3. epoll:
    epoll是库是Nginx服务器支持的最高性能的事件驱动库之一,是公认的非常优秀的事件驱动模型,它和select和poll有很大的区别,epoll是poll的升级版,但是与poll有很大的区别.
    epoll的处理方式是创建一个待处理的事件列表,然后把这个列表发给内核,返回的时候在去轮询检查这个表,以判断事件是否发生,epoll支持一个进程打开的最大事件描述符的上限是系统可以打开的文件的最大数,同时epoll库的I/O效率不随描述符数目增加而线性下降,因为它只会对内核上报的“活跃”的描述符进行操作。
  4. kqueue:
    用于支持BSD系列平台的高校事件驱动模型,主要用在FreeBSD 4.1及以上版本. OpenBSD 2.0级以上版本,NetBSD级以上版本及Mac OS X 平台上,该模型也是poll库的变种,因此和epoll没有本质上的区别,都是通过避免轮询操作提供效率。
  5. Iocp:
    Windows系统上的实现方式,对应第5种(异步I/O)模型。
  6. rtsig:
    不是一个常用事件驱动,最大队列1024,不是很常用
  7. /dev/poll:
    用于支持unix衍生平台的高效事件驱动模型,主要在Solaris 平台. HP/UX,该模型是sun公司在开发Solaris系列平台的时候提出的用于完成事件驱动机制的方案,它使用了虚拟的/dev/poll设备,开发人员将要见识的文件描述符加入这个设备,然后通过ioctl()调用来获取事件通知,因此运行在以上系列平台的时候请使用/dev/poll事件驱动机制。
  8. eventport:
    该方案也是sun公司在开发Solaris的时候提出的事件驱动库,只是Solaris 10以上的版本,该驱动库看防止内核崩溃等情况的发生。
当前页面是本站的「Baidu MIP」版。发表评论请点击:完整版 »