彻底理解 IO 多路复用实现机制


彻底理解 IO 多路复用实现机制文章插图
本文作者:何建辉(公众号:org_yijiaoqian)
点赞再看 , 养成习惯 , 微信搜一搜【一角钱技术】关注更多原创技术文章 。 本文 GitHub org_hejianhui/JavaStudy已收录 , 有我的系列文章 。
前言

  • BIO 、NIO 、AIO 总结
  • Unix网络编程中的五种IO模型
【彻底理解 IO 多路复用实现机制】为了加深对 I/O多路复用机制 的理解 , 以及了解到多路复用也有局限性 , 本着打破砂锅问到底的精神 , 前面我们讲了BIO、NIO、AIO的基本概念以及一些常见问题 , 同时也回顾了Unix网络编程中的五种IO模型 。 本篇重点学习理解IO多路复用的底层实现机制 。
概念说明IO 多路复用有三种实现 , 在介绍select、poll、epoll之前 , 首先介绍一下Linux操作系统中基础的概念:
  • 用户空间和内核空间
  • 进程切换
  • 进程的阻塞
  • 文件描述符
  • 缓存 I/O
用户空间 / 内核空间现在操作系统都是采用虚拟存储器 , 那么对32位操作系统而言 , 它的寻址空间(虚拟存储空间)为4G(2的32次方) 。操作系统的核心是内核 , 独立于普通的应用程序 , 可以访问受保护的内存空间 , 也有访问底层硬件设备的所有权限 。 为了保证用户进程不能直接操作内核(kernel) , 保证内核的安全 , 操作系统将虚拟空间划分为两部分 , 一部分为内核空间 , 一部分为用户空间 。
针对linux操作系统而言 , 将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF) , 供内核使用 , 称为内核空间 , 而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF) , 供各个进程使用 , 称为用户空间 。
进程切换为了控制进程的执行 , 内核必须有能力挂起正在CPU上运行的进程 , 并恢复以前挂起的某个进程的执行 。 这种行为被称为进程切换 。 因此可以说 , 任何进程都是在操作系统内核的支持下运行的 , 是与内核紧密相关的 , 并且进程切换是非常耗费资源的 。
从一个进程的运行转到另一个进程上运行 , 这个过程中经过下面这些变化:
  1. 保存处理机上下文 , 包括程序计数器和其他寄存器 。
  2. 更新PCB信息 。
  3. 把进程的PCB移入相应的队列 , 如就绪、在某事件阻塞等队列 。
  4. 选择另一个进程执行 , 并更新其PCB 。
  5. 更新内存管理的数据结构 。
  6. 恢复处理机上下文 。
进程阻塞正在执行的进程 , 由于期待的某些事件未发生 , 如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新工作做等 , 则由系统自动执行阻塞原语(Block) , 使自己由运行状态变为阻塞状态 。 可见 , 进程的阻塞是进程自身的一种主动行为 , 也因此只有处于运行态的进程(获得了CPU资源) , 才可能将其转为阻塞状态 。 当进程进入阻塞状态 , 是不占用CPU资源的 。
文件描述符文件描述符(File descriptor)是计算机科学中的一个术语 , 是一个用于表述指向文件的引用的抽象化概念 。文件描述符在形式上是一个非负整数 。 实际上 , 它是一个索引值 , 指向内核为每一个进程所维护的该进程打开文件的记录表 。 当程序打开一个现有文件或者创建一个新文件时 , 内核向进程返回一个文件描述符 。 在程序设计中 , 一些涉及底层的程序编写往往会围绕着文件描述符展开 。 但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统 。
缓存I/O缓存I/O又称为标准I/O , 大多数文件系统的默认I/O操作都是缓存I/O 。 在Linux的缓存I/O机制中 , 操作系统会将I/O的数据缓存在文件系统的页缓存中 , 即数据会先被拷贝到操作系统内核的缓冲区中 , 然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间 。


推荐阅读