七叶笔记 » golang编程 » 关于IO的同步,异步,阻塞,非阻塞

关于IO的同步,异步,阻塞,非阻塞

IO 模型

目前 unix 存在五种IO模型,分别是:

阻塞型 IO(blocking I/O)

非阻塞性IO(nonblocking I/O)

IO 多路复用 (I/O multiplexing)

信号驱动IO(signal driven I/O)

异步IO(asynchronous I/O)

IO的两个阶段

等待数据准备好

将数据从内核缓冲区复制到用户进程缓冲区

同步,异步的区别

那么究竟什么是同步和异步的区别呢?请重点读一下原文6.2节中的信号驱动IO和异步IO中的比较。最后总结出来是:

同步IO,需要用户进程主动将存放在内核缓冲区中的数据拷贝到用户进程中。

异步IO,内核会自动将数据从内核缓冲区拷贝到用户缓冲区,然后再通知用户。

这样,同步和异步的概念就非常明显了。以上的五种IO模型,前面四种都是同步的,只有第五种IO模型才是异步的IO。

阻塞和非阻塞

那么阻塞和非阻塞呢?注意到以上五个模型。阻塞IO,非阻塞IO,只是上面的五个模型中的两个。阻塞,非阻塞,是针对单个进程而言的。

当对多路复用IO进行调用时,比如使用 poll 。需注意的是,poll是系统调用,当调用poll的时候,其实已经是陷入了内核,是内核 线程 在跑了。因此对于调用poll的用户进程来讲,此时是阻塞的。

因为poll的底层实现,是去扫描每个 文件描述符 (fd),而如果要对感兴趣的fd进行扫描,那么只能将每个描述符设置成非阻塞的形式(对于用户进程来讲,设置fd是阻塞还是非阻塞,可以使用系统调用fcntl),这样才有可能进行扫描。如果扫描当中,发现有可读(如果可读是用户感兴趣的)的fd,那么select就在用户进程层面就会返回,并且告知用户进程哪些fd是可读的。

这时候,用户进程仍然需要使用read的系统调用,将fd的数据,从内核缓冲区拷贝到用户进程缓冲区(这也是poll为同步IO的原因)。

那么此时的read是阻塞还是非阻塞呢?这就要看fd的状态了,如果fd被设置成了非阻塞,那么此时的read就是非阻塞的;如果fd被设置成了阻塞,那么此时的read就是阻塞的。

不过程序已经执行到了这时候,不管fd是阻塞还是非阻塞,都没有任何区别,因为之前的poll,就是知道有数据准备好了才返回的,也就是说内核缓冲区已经有了数据,此时进行read,是肯定能够将数据拷贝到用户进程缓冲区的。

相关文章