Unix网络编程之IO模型

2020-03-10 14:15:06 浏览数 (1)

点击上方疾风先生可以订阅哦

首先,我们要了解IO模型先要知道在底层操作系统是通过哪些设备来实现数据的传输,其次要了解IO模型中哪些是发生阻塞调用操作,然后有了上述的基本认知之后,开始来了解IO模型是如何演进,最后通过IO模型的演进我们要辨别IO模型中的关键术语联系与区分,上述的思维导图囊括以下要分享的知识点!

数据传输交换媒介

Unix操作系统结构图

概要说明

  • 用户空间: 将上述用户级别或者是unix编程的应用程序的部分称为用户空间,我们可以通过启动进程来调用内核命令完成从硬件设备读取或写入等操作
  • 系统内核: 是直接与计算机硬件打交道的应用程序级别,在计算机相关的书籍中也称为操作系统,可以通过操作系统级别提供的一些组件来帮助用户进程与计算机硬件完成通信交互的操作,可以称为一个中转站
  • 硬件: 对于网络编程而言,与关联的硬件设备就是网络接口控制器等设备,通过网络接口控制器将字节流数据传输到互联网再根据IP地址等信息传输到其他计算机系统应用程序,实现多台计算机系统之间的通信
  • 文件描述符(File descriptor):在linux/unix系统中,文件进程存储着一份文件描述表(数组存储),数组存储file结构指针类型,文件进程通过数组索引来完成文件的操作,这里的数组索引也就是文件描述符,简称为fd

至此,通过上述的图解以及简要说明可知,用户进程无法直接操作硬件设备,必须通过向内核发起请求命令,然后由内核向硬件设备解析并执行相应的命令操作,于是跨计算机的进程网络数据传输需要将数据发送到系统内核再到网络接口控制器,通过网络接口控制器实现计算机之间的通信,如下图所示:

网络IO阻塞操作

IO读取操作核心步骤

  • 用户进程向系统内核发起数据读取操作请求,必须等待内核从硬件设备中获取数据直到数据报准备完成
  • 当内核从设备中准备好数据的时候,需要将数据报从内核复制到用户空间中

IO阻塞操作

  • recvfrom函数:属于IO操作的系统调用,这里区分系统内核和用户进程,调用函数的时候需要从用户进程的上下文空间切换到内核空间中运行,一段时间之后才能切换回来
  • select/poll函数: 允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或者多个事件发生抑或是超过指定时间段便唤醒用户进程,告知当前事件是否就绪状态

IO操作的对比

  • recvfrom函数: 调用当前函数的时候,此时用户进程受阻于系统内核,这个时候其他进程发起请求调用的时候须等待系统内核完成工作之外才能处理其他的进程请求,也就是1:1模型
  • select/poll函数:调用当前函数的时候,此时用户进程受阻于当前select函数,不断轮询向内核select就绪状态的描述符或者是超过指定时间段被唤醒,由于select系统函数调用是携带描述符集合到内核,因此可以对N个描述符进行监听(姑且称描述符的就绪状态为事件可读状态,后面论述均以事件论述),即N:1模型,如下图:

IO模型演进

基于上述的数据传输以及IO阻塞操作可知,网络编程需要读取网络传输过来的数据需要先经过系统内核再到用户空间,期间需要系统内核等待数据准备完成以及数据复制到用户空间两个步骤,同时为了简化概念,将TCP的低套接字等信息隐藏不做说明,用简单且易于理解的UDP传输方式来演示Unix下的5种IO模型

阻塞式IO模型

  • 自应用进程发起recvfrom系统调用,在此期间一直处于被阻塞,因为这个时候需要等待内核获取数据报信息复制到用户空间中,除非被中断异常返回,否则将一直处于阻塞状态
  • 以下是时序图展示阻塞式IO

非阻塞式IO模型

  • 非阻塞式主要体现在用户进程发起recvfrom系统调用的时候,这个时候系统内核还没有接收到数据报,直接返回错误给用户进程,告诉用户进程“当前还没有数据报可达,晚点再来
  • 用户进程接收到信息,但是用户进程不知道什么时候数据报可达,于是就开始不断轮询(polling)向系统内核发起recvfrom的系统调用“询问数据来了没”,如果没有则继续返回错误
  • 用户进程轮询发起recvfrom系统调用直至数据报可达,这个时候需要等待系统内核复制数据报到用户进程的缓冲区,复制完成之后将返回成功提示
  • 以下时序图展示NIO的方式

IO复用模型

  • IO复用模型是使用select或者poll函数向系统内核发起调用,阻塞于这两个之一的系统函数调用,而不是真正阻塞于实际的IO操作(recvfrom调用才是实际阻塞IO操作的系统调用)
  • 阻塞于select函数的调用,等待系统内核准备数据并通知当前事件为可读状态
  • 当select接收到系统内核通知事件为可读状态时,就可以向系统内核发起recvfrom调用将数据复制到用户空间的缓冲区
  • IO复用模式时序图如下

信号驱动式IO模型

  • 先开启套接字的信号IO驱动功能,并通过一个内置安装信号处理函数的signaction系统调用,当发起调用之后将会直接返回
  • 其次,等待内核从网络中接收数据报之后,向用户进程发送当前数据可达的信号给到信号处理函数
  • 信号处理函数接收到信息就发起recvfrom系统调用等待内核复制数据报到用户空间的缓冲区
  • 接收到复制完成的返回成功提示之后,应用进程就可以开始从网络中读取数据
  • 上述是基于信号驱动式IO模型,当系统内核描述符就绪时将会发送SIGNO给到用户进程,这个时候再发起recvfrom的系统调用等待返回成功提示,流程如下:

异步IO模型

  • 由POSIX规范定义,告知系统内核启动某个操作,并让内核在整个操作包括数据等待以及数据复制过程的完成之后通知用户进程数据已经准备完成,可以进行读取数据
  • 与上述的信号IO模型区分在于通过异步方式直接通知我们何时IO操作完成,而信号IO是通知我们何时可以启动一个IO操作
  • 时序图如下

5种IO模型分析

  • 对于阻塞式IO模型,属于1:1模型,也就是系统内核只能处理一个请求,其他请求过来的时候必须等待当前请求处理完成才能进行,性能相当差,可以通过多线程变更为N:1模型,与非阻塞式IO类似,区分在于前者多线程,后者单线程
  • 对于驱动式信号IO模型,虽然是非阻塞式IO模型,但是基于内核通知回调的实现机制比较复杂(在信号函数异步处理IO与读取数据操作要保持先后顺序,个人认为信号函数正确设计是不处理业务的,即后续的数据读取等操作)
  • 而对于非阻塞式IO模型,相比阻塞式IO而言,而是通过不断轮询的方式发起recvfrom系统调用,由于存在内核与用户空间的切换,会损耗性能
  • 对于IO复用模型而言,阻塞于select函数的调用(本质上是基于文件描述符集合的遍历),向内核注册对应的事件并等待事件可读或者超时通知到select函数
  • 而对于AIO模型而言,是一种实现真正的非阻塞异步IO方式,但是在linux/unix系统支持此IO模型设计并不确定
  • 目前大多数Unix/Linux服务器都是基于IO复用模型进行优化改进,即对select/poll方法进行增强优化

IO关键术语

同步与异步的定义

  • 同步:发起一个fn的调用,需要等待调用结果返回,该调用结果要么是期望的结果要么是异常抛出的结果,可以说是原子性操作(要么成功要么失败返回)
  • 异步: 发起一个fn调用,无需等待结果就直接返回,只有当被调用者执行处理程序之后通过“唤醒”手段通知调用方获取结果(唤醒的方式有回调,事件通知等)
  • 小结: 同步和异步关注的是程序之间的通信

阻塞与非阻塞的定义

  • 阻塞: 类比线程阻塞来说明,在并发多线程争抢资源的竞态条件下,如果有一个线程已持有锁,那么当前线程将无法获取锁而被挂起,处于等待状态
  • 非阻塞: 一旦线程释放锁,其他线程将会进入就绪状态,具备争抢锁的资格
  • 小结: 阻塞与非阻塞更关注是程序等待结果的状态
  • 由此可知,同步异步与阻塞非阻塞之间不存在关联,关注的目标是不一样的

同步IO与异步IO(基于POSIX规范)

  • 同步IO: 表示应用进程发起真实的IO操作请求(recvfrom)导致进程一直处于等待状态,这时候进程被阻塞,直到IO操作完成返回成功提示
  • 异步IO: 表示应用进程发起真实的IO操作请求(recvfrom),这个时候系统内核向用户进程将直接返回一个错误信息,“相当于告诉进程还没有处理好,好了会通知你”
  • 阻塞IO: 主要是体现发起IO操作请求通知内核并且内核接收到到请求之后如果让进程等待,那么就是阻塞
  • 非阻塞IO: 发起IO操作请求的时候不论结果如何直接告诉进程“不用等待,晚点再来”,那就是非阻塞

IO模型对比

根据上述的同步与异步IO定义并结合上述的IO模型可知,只有异步IO模型符合POSIX规范的异步IO,其他IO模型都存在recvfrom系统调用被内核阻塞,属于同步IO操作,由此可总结如下:

  • 也就是说,要么称为同步IO和异步IO,要么称为上述的IO模型名称
  • 大部分操作系统都是基于同步IO的方式实现,对于支持异步IO模型的操作系统还不确定,在实际工作中接触到的IO模型,从严格意义上来说应该称为Blocking-IO(阻塞IO)和Non-Blocking-IO(非阻塞IO),而不是同步IO和异步IO
  • 小结: 同步与异步针对通信机制,阻塞与非阻塞针对程序调用等待结果的状态

你好,我是疾风先生,先后从事外企和互联网大厂的java和python工作, 记录并分享个人技术栈,欢迎关注我的公众号,致力于做一个有深度,有广度,有故事的工程师,欢迎成长的路上有你陪伴,关注后回复greek可添加私人微信,欢迎技术互动和交流,谢谢!

0 人点赞