IO 网络模型的本质
前置概念
(1)用户空间和内核空间
学习 Linux 或者 IO 网络模型时,经常可以看到两个词:User space(用户空间)和 Kernel space(内核空间)。
简单说,Kernel space 是 Linux 内核的运行空间,User space 是用户程序的运行空间。具体来说,虚拟内存被操作系统划分成两块:内核空间和用户空间,内核空间是内核代码运行的地方,用户空间是用户程序代码运行的地方。当进程运行在内核空间时就处于内核态,当进程运行在用户空间时就处于用户态。
为了安全,它们是隔离的,即使用户的程序崩溃了,内核也不受影响。Kernel space 可以执行任意命令,调用系统的一切资源;User space 只能执行简单的运算,不能直接调用系统资源,必须通过调用系统接口(又称 system call),才能向内核发出指令。
(2)进程切换
为了控制进程的执行,内核必须有能力挂起正在 CPU 上运行的进程,并恢复以前挂起的某个进程的执行。这种行为被称为进程切换。因此可以说,任何进程都是在操作系统内核的支持下运行的,是与内核紧密相关的。
(3)进程的阻塞
正在执行的进程,由于期待的某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新工作做等,则由系统自动执行阻塞原语(Block),使该进程由运行状态变为阻塞状态。
可见,进程的阻塞是进程自身的一种主动行为,也因此只有处于运行态的进程(获得 CPU),才可能将其转为阻塞状态。当进程进入阻塞状态的进程是不占用 CPU 资源的。
(4)文件描述符
Linux 系统中,把一切都看做是文件,当进程打开现有文件或创建新文件时,内核向进程返回一个文件描述符,文件描述符就是内核为了高效管理已被打开的文件所创建的索引,用来指向被打开的文件,所有执行I/O操作的系统调用都会通过文件描述符。文件描述符在形式上是一个非负整数。
(5)内存与缓冲区
缓冲区是内存空间的一部分,也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区。缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。
**为什么要引入缓冲区?**比如我们从磁盘里取信息,我们先把读出的数据放在缓冲区,计算机再直接从缓冲区中取数据,等缓冲区的数据取完后再去磁盘中读取,这样就可以减少磁盘的读写次数,再加上计算机对缓冲区的操作速度远高于对磁盘的操作速度,故应用缓冲区可大大提高计算机的运行速度。
网络 IO 本质
什么是 IO?
I/O 其实就是 input 和 output 的缩写,即输入/输出。那输入输出啥呢?比如我们用键盘来敲代码其实就是输入,那显示器显示图案就是输出,这其实就是 I/O。
大多数文件系统的默认 IO 操作都是缓存 IO,又被称作标准I/O。其读写过程如下:
-
读操作:操作系统检查内核的缓冲区有没有需要的数据,如果已经缓存了,那么就直接从缓存中返回;否则从磁盘、网卡等中读取,然后缓存在操作系统的缓存中;
-
写操作:将数据从用户空间复制到内核空间的缓存中。这时对用户程序来说写操作就已经完成,至于什么时候再写到磁盘、网卡等中由操作系统决定,除非显示地调用了 sync 同步命令。
假设内核空间缓存无需要的数据,用户进程从磁盘或网络读数据分两个阶段:
-
阶段一: 内核程序从磁盘、网卡等读取数据到内核空间缓存区(内存);
-
阶段二: 用户程序从内核空间缓存(内存)拷贝数据到用户空间。
缓存 IO 的缺点:数据在传输过程中需要在应用程序地址空间和内核空间进行多次数据拷贝操作,这些数据拷贝操作所带来的CPU以及内存开销非常大。
什么是网络 IO?
IO 有内存 IO、网络 IO和磁盘 IO(又称磁盘 I/O)三种,通常我们说的 IO 指的是后两者:
那为什么都要跟内存交互呢?,因此都是和内存交互。当然假设没有内存,让 CPU 直接和外部设备交互,那也算 I/O,只是交互速度会慢很多。
总结下:I/O 就是指内存与外部设备之间的交互(数据拷贝),网络 IO 就是指内存与网卡之间的交互。
什么是网络 IO 模型?
(1)阻塞与非阻塞
阻塞与非阻塞,用于描述调用者在等待返回结果时的状态。
- 阻塞:调用者发起请求后,会一直等待返回结果,这期间当前线程会被挂起(阻塞)。
- 非阻塞:调用者发起请求后,会立刻返回,当前线程也不会阻塞。该调用不会立刻得到结果,调用者需要定时轮询查看处理状态。
(2)同步与异步
同步与异步,用于描述调用结果的返回机制(或者叫通信机制)。
- 同步:调用者发起请求后,会一直等待返回结果,即由调用者主动等待这个调用结果。
- 异步:调用者发起请求后,会立刻返回,但不会立刻得到这个结果,而是由被调者在执行结束后主动通知(如 Callback)调用者。
(3)网络 IO 模型
网络 IO 模型:用什么样的通道或者说是通信模式进行数据的传输,这很大程序上决定了程序通信的性能。
网络 IO 会涉及到两个系统对象,一个是用户空间调用 IO 的进程或者线程,另一个是内核空间:
- 第一阶段:等待数据。即应用进程发起 IO 系统调用后,会一直等待数据;当有数据传入服务器,会将数据放入内核空间,此时数据准备好。
- 第二阶段:将数据从内核空间复制到用户空间,并返回给应用程序成功标识。
因为在以上两个阶段上各有不同的情况,所以出现了多种网络 IO 模型。Linux 系统为我们提供五种可用的 IO 模型:
其中同步阻塞 IO、同步非阻塞 IO、多路复用 IO、信号驱动 IO 模型的第二阶段是相同的,都是处于阻塞状态,所以这四种 IO 都属于同步IO,它们主要区别在第一阶段。而异步 IO 模型则不同,应用进程在这两个阶段是完全不阻塞的。
IO 模型 |
第一阶段 |
第二阶段 |
阻塞式IO |
阻塞 |
阻塞 |
非阻塞式IO |
非阻塞 |
阻塞 |
IO多路程复用 |
阻塞(Select、Poll、Epoll) |
阻塞 |
信号驱动式IO |
异步 |
阻塞 |
异步IO |
异步 |
异步 |