-
阻塞I/O
读写文件,网络I/O。当发生读写文件或网络I/O读写时,操作会阻塞当前线程,直到完成I/O操作,才能继续向下执行。
一般采用多进程或多线程的方式来解决此类问题,但是由于过多的进程或过多的线程都会造成大量的资源浪费,并且多线程上下文切换,也会带来很大的开销
-
执行流程:
1\. 应用程序调用recvform()系统调用函数,进入内核空间 2\. 内核空间等待数据准备好 3\. 内核空间将数据从内核空间拷贝到用户空间 4\. 应用程序拿到数据进行后续处理
总结:
上述整个过程,当前用户线程是阻塞等待的。
-
非阻塞I/O
当应用程序调用系统调用函数,进入到内核空间后,如果内核空间数据没有准备好,那么此时内核空间之间返回给用户空间一个错误,此时用户空间不需要等待内核空间数据准备好,而可以去执行其他工作代码,然后定期轮询,查看内核空间是否准备好了,如果准备好,将内核空间中的数据拷贝到用户空间,应用收到数据后,继续执行后续的工作。
-
执行过程:
应用程序发起系统调用,进入到内核空间
如果内核空间数据还没准备好,则直接返回error。
应用程序会定时轮询,数据是否准备好了
数据准备好之后,则将数据从内核空间拷贝到用户空间
-
应用程序拿到数据之后,进行后续处理
总结:
不管内核空间数据是否准备好,都直接返回,然后应用程序定时轮询内核空间,数据是否准备好,准备好之后则将数据从内核空间拷贝到用户空间。
-
IO多路复用
一个线程可以监听多个I/O事件,当有事件就绪时,可以通知线程执行响应的工作代码。
-
执行过程:
1\. 应用程序调用select系统调用函数,并将fd_set从用户空间拷贝到内核空间,然后在内核空间中对fd_se遍历一遍,如果没有就绪的I/O事件,则内核进行休 眠,当有就绪的事件时,则唤醒内核 2\. 通知用户线程就绪事件的数量 3\. 就绪数量大于0时,select再遍历一次fd_set,找出就绪的事件 4\. 然后再执行相应的操作。比如:应用程序调用recvfrom系统调用,进入内核空间,内核将数据拷贝到用户空间进行后续处理
总结:
多路复用本质也是同步I/O
-
信号驱动I/O
用户进程向内核发送一个信号,告诉内核需要什么样的数据,然后用户进程就可以去干其他工作了,当内核数据准备好后,就会给用户进程发送一个信号,去执行相应的读写操作。
-
执行过程:
1\. 首先用户进程调用sigaction系统调用,告知内核需要什么数据,并返回一个sigio handler,用户进程就可以接着去干其他事儿了 2\. 当内核数据准备好后,会给用户进程发送一个信号 3\. 用户进程收到信号后,接着调用recvfrom系统调用,进入内核空间 4\. 内核将数据拷贝到用户空间,进行后续处理
-
异步I/O
用户调用aio_read系统调用之后,无论内核是否准备好数据,都直接返回,当内核数据准备好之后,内核直接将数据拷贝到用户空间。
-
执行过程:
1\. 用户进程调用aio_read系统调用 2\. 内核无论是否准备好数据直接返回 3\. 当内核数据准备好后,直接将数据拷贝到用户空间
总结:
异步I/O每个阶段都是非阻塞的
总结