在linux系统中对于一个IO事件来说,实际都分为了两步:
- 等待数据准备好
- 将数据从内核中拷贝到进程中
IO的两个步骤是它的基础,以下讨论各种IO模型的区别就是上面两步的不同,以下来逐个解释:
同步IO
1.阻塞
在linux中,所有的socket默认都是阻塞方式的,阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。IO的第一个阶段:准备数据。而很多时候数据在一开始还没有到达(比如,还没有收到一个完整的UDP包),这个时候kernel就要等待足够的数据到来。而对于进程这边,整个进程会被阻塞。一直等到数据准备好了,它就会将数据从内核中拷贝到用户内存,用户进程才解除阻塞的状态,重新运行起来。
2.非阻塞
非阻塞IO相对于阻塞IO来说,看下图,我们就可以知道,如果还没有数据准备好,那么就会立即返回错误码,而如果一直没有数据准备好的话,进程就会一直处于反复尝试读写这个文件描述符,称之为轮询。而对于阻塞,就一直死等,虽然听起来很傻,但不会像非阻塞一样,一直往返的操作,使得CPU处在一个较高的工作状态,是不可取的。
以下,就来举一个例子来说明一下阻塞和非阻塞的区别:
对于cmd来说,常用的就是获得文件描述符的状态标记(cmd=FGETFL)/设置文件状态标记(cmd=FSETFL),第三个参数有
- O_NONBLOCK:用于非阻塞I/O。
- O_ASYNC:用于信号驱动的I/O(异步I/O)。若此位设置,当文件标志符中有输入数据时会生成SIGIO信号。等
阻塞:
阻塞运行图:
而对于非阻塞来说:
阻塞IO和非阻塞IO的区别就在于:应用程序的调用是否立即返回
3.信号驱动
内核将数据准备好的时候,使用SIGIO信号通知应用程序进行IO操作。
4.多路转接
虽然从流程图上看起来和阻塞IO类似,实际上最核心在于IO多路转接能够同时等待多个文件描述符的就绪状态,select/epoll的好处就在于可以同时处理多个网络连接的IO。它的基本原理就是select/epoll会负责所有的socket,当某个socket有数据到达了,就通知用户进程。等待时间重叠,进而等的比重减小。可以使一个进程监视多个IO。实现原理:用户告诉内核想要监视的文件描述符所发生的事务(读、写、错误),当这些事务就绪后,内核通知用户。它的流程如图:
异步IO
相对于同步IO来说,异步IO只是发起了IO的需求,而真正的IO在内核中进行。数据拷贝的时候进程无需阻塞。用户进程发起read操作之后,立刻就可以开始去做其它的事。内核会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,会给用户进程发送一个信号,告诉它read操作完成了。
同步IO和异步IO的区别就在于:数据访问的时候进程是否阻塞
五种IO的区别: