IO - 阻塞/非阻塞/同步/异步 区别
下文是基于Linux网络IO展开的描述
操作系统用户态/内核态
- 用户态:也称为用户空间,即上层应用程序的活动空间,应用程序的执行必须依赖于内核提供的资源。
- 内核态:也称为内核空间,控制计算机硬件资源,并提供上层应用程序运行的环境
用户态可以通过系统调用切换到内核态,这是主动进入内核态的形式;
在出现异常或者外设中断时也会进入到内核态,这个是被动响应的形式。
IO模型可以区分为两个阶段
- 第一阶段:等待数据准备就绪
- 第二阶段:将数据从内核态拷贝到用户态
各种IO模型其实是根据以上两个阶段的不同表现来区分的(用户进程或线程是否阻塞)
- 第一个阶段用户进程是否阻塞来区分 阻塞/非阻塞
- 第二个阶段用户进程是否阻塞来区分 同步/异步
阻塞IO
当用户进程发起系统调用 recvform
时,
在第一阶段,内核首先会等待数据准备就绪,这个过程中用户进程是阻塞的;
在第二阶段,当数据准备就绪之后,数据会从内核空间拷贝到用户空间(应用程序空间),这个过程中用户进程同样是阻塞的,知道数据拷贝完成才恢复。
应用程序阻塞期间被挂起,进程从运行状态变为阻塞状态,让出CPU,当数据拷贝完成时,会通知进程,进程再进入到就绪状态,等待CPU的调度。
因此,阻塞IO在两个阶段都是阻塞的
非阻塞IO
非阻塞IO与阻塞IO相似,区别在于:
第一个阶段,在等待数据准备的过程中,用户进程不会阻塞,而是在数据没有准备就绪的时候得到一个error返回,这时用户进程就会知道数据还没有准备好,然后就继续发起 recvfrom
操作知道数据准备就绪(一直占用CPU,减少了进程/线程切换的开销)
第二个阶段,数据从内核空间拷贝到用户空间时,非阻塞IO用户进程同样是阻塞的(让出CPU)
因此,非阻塞IO是指在一个阶段不阻塞,第二个阶段阻塞
IO多路复用
IO多路复用,是通过 select/epoll 等函数轮询多个 socket,当用户进程调用了 select函数,整个用户进程就会被阻塞,直到收到数据准备就绪的返回。
后续操作和非阻塞IO相识,由于在 recvfrom
系统调用之前,需要的数据就已经准备好了,所以第一个阶段是非阻塞;第二个阶段还是阻塞。
因此,IO多路复用整个过程中,用户进程都是阻塞的
异步IO
异步IO是指用户进程发起read操作后,会立即收到一个返回,所以用户进程就可以去完成其他的工作了,从而不需要阻塞;
直到数据准备就绪并且完成了从内核空间拷贝到用户空间的工作,这时用户进程会收到一个通知,告诉它read操作已完成
因此,异步IO整个过程中用户进程不会被阻塞
总结
实际上 阻塞IO 和 非阻塞IO 都是 同步IO,用了特殊的异步函数才是异步IO。
阻塞和异步没有直接关系,网上很多阻塞同步异步一起说,容易让人产生误解,认为阻塞/非阻塞和同步/异步有什么关联。