看了很多网上的文章, 写下一下我自己的理解, 有点杂, 不喜勿喷
概念:
同步IO : synchronous IO
异步IO: asynchronous IO
阻塞IO: blocking IO
非阻塞IO: non-blocking IO
参考文档: https://www.cnblogs.com/sss4/p/11124917.htmlhttps://www.cnblogs.com/sss4/p/11124917.html
怎样理解阻塞非阻塞与同步异步的区别? - 知乎https://www.zhihu.com/question/19732473/answer/241673170
1. 操作系统内核 和 应用程序进程
操作系统运行拥有内核空间
应用程序运行拥有用户空间
应用程序 要通过操作系统才能与外接设备通信, 如网卡、磁盘等通信。
应用程序要得到网卡输入的数据就要从操作系统的内核空间拷贝至用户空间。
拿 TCP 连接来说,
socket.recieve() (伪代码)
这句话代表:由我们的应用程序 告诉操作系统要接受客户端数据,这个信号发送到操作系统的Kernal (内核空间),内核在接收到客户端数据后将网卡数据准备好,再复制到用户空间(我们的应用程序)
即这四个步骤:
1.socket.reveive 等待用户分组发完数据 (慢)
2.数据由用户空间到内核空间 (快)
3.等待内核准备数据 (慢)
4.内核准备玩据,数据由内核空间回到用户空间 (快)
IO 模型的演变就是围绕优化第一步和第三步而进行的。
2. IO 模型
1) 阻塞IO
进程执行 recieve后, 会阻塞, 直到收到客户端的数据, 从内核空间拷贝至用户空间。在阻塞期间, 进程啥也不能干。如果阻塞时又来了一个新的客户端连接,我们的进程是无法处理的, 只能等到之前的那个阻塞结束,才能处理新的客户端连接。 这样在高并发的环境下,就比较低效。 比如python web 框架: Django, 就是阻塞IO。
2) 非阻塞IO
socket.setBlocking(false), 即 socket.recieve时 并不会阻塞, 如果客户端数据没到,内核会告诉应用进程客户端数据还没准备好,我们的应用进程就可以继续做其他事,适时再来询问内核客户端数据准备好没, 如果准备好了,就进行响应处理。这样应用进程就不用阻塞,增强了并发的处理能力。但这样不停的轮询内核,也是比较耗费系统资源。
3) IO 多路复用
IO多路复用属于 非阻塞IO, 操作系统为应用进程提供一个底层代理, 一个存放在监听列表,可以监听是否有新的连接,可以监听已连接的客户端是否有数据。这样IO事件都是由内核主动去告诉应用程序, 而不需要应用程序去轮训内核, 这样,我们的应用进程就解放出来可以做其他的事情。
不同操作系统中IO多路复用模型介绍
select( linux/windows)
IO多路复用模型得以实现得核心:就是操作系统 监控1个[sk......conn,]列表,不断轮询每1个sk/conn/是否可以accpet/revive,随着监控列表的增加,效率会递减;
poll(Linux)
和select机制一样,但是支持监控的socket会比select多
epoll (Linux)
和select机制完全不一样
1.epoll很高级,epoll不会去再通过操作循环检查监控的socket列表中,那些socket出现了读操作,而是给需要监听的socket 1--1绑定1个回调函数;
2.检测的socket中 有1个soket出现了读操作,直接执行调用那个和该sk/con绑定的回调函数执行sk.accpet() 和conn.receve()
python中 selectors 包可以自动帮我们选择这个代理。
以上的IO模型都属于同步IO, 是因为它们都需要等待 操作系统把数据从内核空间拷贝至用户空间。
4)异步IO
异步IO 不需要去吧数据从内核空间拷贝到用户空间,而是把这个IO过程交给了内核,让内核去完成IO,并将数据拷贝至用户进程指定的缓存区, 完美实现了异步。 如python的Tornado框架。
另外, 阻塞于同步,非阻塞和异步, 这两对词在很多时候,意思非常相近。阻塞或非阻塞,是倾向于描述进程等待调用结果时的状态, 如在执行过程中遇到IO等待是否要将进程挂起。 而同步和异步主要用于描述消息通信机制,调用立即返回不论是否有结果,这就是异步, 如果调用必须要等到结果才返回,那就是同步。
5)信号IO
同步IO中还有个信号IO, 用得较少,没有深入研究过。