IO 是什么?
在 Linux 系统中一切皆文件,而文件指的就是一些二进制的流,这些流包括输入流和输出流,比如我们之前谈到的,进程间通信,socket 套接字,管道,read 读取数据,write 写数据等等,在信息交换的过程中,我们都是对这些流进行操作,简称 IO 操作,那计算机里面这些流,我们是通过文件描述符来进行操作的.
通常用户进程的一个完整 IO 分为两个阶段
从用户空间到内核空间,从内核空间到设备空间
内核空间存放的是内核代码和数据,用户空间存放的是用户程序代码和数据,不管是内核还是用户,他们都处于虚拟地址空间,Linux 使用两级保护机制,0 级供内核使用,3 级供用户程序使用,也就是说,用户无法直接操作内核数据,必须通过系统调用请求内核完成 IO 操作
所以对于一个输入操作来说,进程 IO 系统调用后,内核会先看缓冲区有没有数据,如果没有到设备中读取,因为设备 IO 速度比较慢,对于用户来说,这是一个等待的过程,当内核缓冲区有数据则复制到进程的空间
所以 IO 操作的流程可以分为两步,等待 IO 的操作条件具备,然后进行数据拷贝
IO 一般有三种,内存 IO 、网络 IO、磁盘 IO ,我们一般说的是后两者
例子:网络输入操作
先来了解四个概念
同步通信和异步通信
这个同步和线程中的同步不是一个概念,这里指的是:如果发出一个调用时,在没有得到结果之前,该调用就不返回,换句话说,就是由调用者主动等待这个调用的结果
当一个异步过程调用发出后,直接返回,所以调用者不会立刻得到结果; 而是在调用发出后,被调用者通过状态来通知调用者,或者通过回调函数处理这个调用
所以同步和异步的区别是请求发出后,是否需要等待结果,才能继续执行其他操作,同步是需要等待,而异步不需要
阻塞和非阻塞
阻塞和非阻塞关注的是程序在等待调用结果时的状态.
阻塞调用是指: 调用结果返回之前,当前线程会被挂起. 调用线程只有在得到结果之后才会返回
非阻塞调用指: 在不能立刻得到结果之前,该调用不会阻塞当前线程
阻塞线程意思就是当前线程被挂起,让出 CPU 资源,进入等待队列,等待下一次被调度
重要五种的 IO 模型
1、阻塞 IO : 在内核将数据准备好之前, 系统调用会一直阻塞等待,什么事都不做,直到内核将数据准备好然后拷贝的用户空间,返回一个成功的指示,这种方式效率很低,无法处理并发
//伪代码
while(1){
// accept 调用发生在三次握手之后,从等待连接队列中拿出第一个
// 可设置套接字为 accept 阻塞接收
client_fd = accept(listen_fd)
fds.append(client_fd)
for(auto fd : fds){
//程序会阻塞在这一步,影响 accept 调用
// 因为如果客户端没有发送消息,这里会一直阻塞
if(recv