一、混乱的 IO 概念
IO是Input和Output的缩写,即输入和输出。广义上的围绕计算机的输入输出有很多:鼠标、键盘、扫描仪等等。而我们今天要探讨的是在计算机里面,主要是作用在内存、网卡、硬盘等硬件设备上的输入输出操作。
谈起IO的模型,大多数人脑子里肯定是一坨混乱的概念,“阻塞”、“非阻塞”,“同步”、“异步”有什么区别?很多同学傻傻分不清,有尝试去搜索相关资料去探究真相,结果又被淹没在茫茫的概念之中。
这里尝试简单地去解释下为啥会出现这种现象,其中一个很重要的原因就是大家看到的资料对概念的解释都站在了不同的角度,有的站在了底层内核的视角,有的直接在java层面或者Netty框架层面给大家介绍API,所以给大家造成了一定程度的困扰。
所以在开篇之前,还是要说下本文所站的视角,我们将会从底层内核的层面给大家讲解下IO。因为万变不离其宗,只有了解了底层原理,不管语言层面如何花里胡哨,我们都能以不变应万变。
文章相关视频讲解:
C/C++ Linux服务器开发高级架构学习视频点击:C/C++Linux服务器开发高级架构/Linux后台架构师-学习视频
90分钟搞定底层网络IO模型,必须要懂得10种模型
epoll原理剖析以及reactor模型应用
linux epoll网络编程细节处理
二、用户空间和内核空间
为了便于大家理解复杂的IO以及零拷贝相关的技术,我们还是得花点时间在回顾下操作系统相关的知识。这一节我们重点看下用户空间和内核空间,基于此后面我们才能更好地聊聊多路复用和零拷贝。
硬 件 层(Hardware)
包括和我们熟知的和IO相关的CPU、内存、磁盘和网卡几个硬件;
内核空间(Kernel Space)
计算机开机后首先会运行内核程序,内核程序占用的一块私有的空间就是内核空间,并且可支持访问CPU所有的指令集(ring0 - ring3)以及所有的内存空间、IO及硬件设备;
用户空间(User Space)
每个普通的用户进程都有一个单独的用户空间,用户空间只能访问受限的资源(CPU的“保护模式”)也就是说用户空间是无法直接操作像内存、网卡和磁盘等硬件的;
如上所述,那我们可能会有疑问,用户空间的进程想要去访问或操作磁盘和网卡该怎么办呢?
为此,操作系统在内核中开辟了一块唯一且合法的调用入口“System Call Interface”,也就是我们常说的系统调用,系统调用为上层用户提供了一组能够操作底层硬件的API。这样一来,用户进程就可以通过系统调用访问到操作系统内核,进而就能够间接地完成对底层硬件的操作。这个访问的过程也即用户态到内核态的切换。常见的系统调用有很多,比如:内存映射mmap()、文件操作类的open()、IO读写read()、write()等等。