参考自知乎作者“勤劳的小能手”:zhuanlan.zhihu.com/p/115912936
阻塞IO
- 应用向内核发起系统调用
recvfrom,读数据 - 此时数据未准备好,内核等待数据。
- 内核将数据复制到用户空间
- 返回成功提示
非阻塞IO
- 应用向内核发起系统调用
recvfrom,读数据 - 此时没准备好数据,立刻返回错误码EWOULDBLOCK
- 应用再次向内核发起RECVFROM读取数据,已有数据包准备好
- 内核将数据拷贝到用户空间
- 完成后,返回成功提示。
可以看到,非阻塞IO以轮询的方式发起系统调用,尝试获取数据。
IO复用模型
进程通过将一个或多个IO传递给select,阻塞在select操作上,select帮我们侦测多个IO是否准备就绪,当有准备就绪时,select返回数据可读状态,应用程序再调用recvfrom读取数据。
总结来说,IO复用的基本思路,就是通过select/poll,epoll来监控多个IO,不必为每个IO创建对应的线程,体现为“1 VS N”
信号驱动IO模型
前面讲的select是以轮询的方式,可想而知有些情况下会做很多次轮询都没有准备好数据。
信号驱动IO,不以轮询的方式监控数据就绪状态,避免了大量数据状态的轮询
在调用sigaction时候建立一个SIGIO的信号联系,当内核数据准备好之后再通过SIGIO信号通知线程数据准备好后的可读状态,当线程收到可读状态的信号后,此时再向内核发起recvfrom读取数据的请求.
异步IO
应用告知内核启动某个操作,并让内核在整个操作完成之后,通知应用。
这种模型与信号驱动模型的主要区别在于,信号驱动IO只是由内核通知我们何时可以开始下一个IO操作,而异步IO模型是由内核通知我们操作什么时候完成。
总结
阻塞IO:
调用recvfrom读取数据时,内核没有数据,会一直等。
非阻塞IO:
调用recvfrom读取数据时,内核没有数据,会立即返回错误码,应用会不断的重复轮询,直到有数据。
IO复用模型:
使用同一个线程监控,轮询多路IO,在某一路可用时,返回可读状态。应用调用recvfrom
信号驱动:
不轮询。通过系统调用sigaction,立即返回。数据准备好后,内核会通知到应用,应用调用recvfrom读取数据。
异步IO:
前面的非阻塞,IO复用,信号驱动等,都需要应用请求,再请求拿数据。而异步IO,应用只需要发送read请求,内核收到后会建立起信号联系,数据准备好后,“内核自觉地”把数据从内核转移到用户空间。最终给个完成通知。
在IO模型里面如果请求方从发起请求到数据最后完成的这一段过程中都需要自己参与,那么这种我们称为同步请求;反之,如果应用发送完指令后就不再参与过程了,只需要等待最终完成结果的通知,那么这就属于异步。