同步/异步、阻塞/非阻塞
同步
发出一个功能调用时,在没有得到结果之前,该调用就不返回,也就是必须一件一件事做,等前一件做完了才能做下一件事。
异步
当一个异步过程调用发出后,调用者一般不能立刻得到结果,实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者
通知调用者的三种方式:
- 状态——监听被调用者的状态(轮询),调用者需要每隔一定时间检查一次,效率会很低;
- 通知——当被调用者执行完成后,发出通知告知调用者,无需消耗太多性能
- 回调——当被调用者执行完成后,会调用调用者提供的回调函数
阻塞
调用结果返回之前,当前线程会被挂起(线程进入非可执行状态,在这个状态下,OS不会给线程分配时间片,即线程暂停运行),调用结果返回后线程进入就绪态
非阻塞
调用结果返回之前,该函数不会阻塞当前线程,而会立刻返回
同步和异步的区别
请求发出后,是否需要等待结果,才能继续执行其他操作
在网络IO模型中,数据拷贝时进程阻塞是同步,反之则是是异步
阻塞和同步的区别
同步调用在结果返回之前,线程并没有进入挂起状态,OS还会给它分配占用CPU的时间片;
阻塞调用在结果返回之前,线程处于挂起状态,OS不会再为其分配占用CPU的时间片,直到结果返回后,线程进入就绪状态在可能继续运行
阻塞和非阻塞的区别
在网络IO模型中,应用程序的调用立即返回是非阻塞,反之则是阻塞
同步/异步和阻塞/非阻塞的区别
在网络编程中,阻塞和非阻塞发生在第一阶段(准备数据阶段);同步和异步发生在第二阶段(准备好的数据从内核缓冲区拷贝到用户进程缓冲区阶段)
用户空间和内核空间
在Linux/Unix中,对于一次读取IO的操作,数据并不会直接拷贝到应用程序的缓冲区(用户空间),它首先会被拷贝到操作系统内核的缓冲区(内核空间)中,然后才会从操作系统内核的缓冲区拷贝到应用程序的缓冲区
可以看做是两个过程:
- Waiting for the data to be ready(等待数据到达内核缓冲区);
- Copying the data from the kernel to the process(从内核缓冲区拷贝数据到应用程序缓冲区)
五种I/O模型
-
阻塞I/O
-
非阻塞I/O
-
I/O多路复用
-
信号驱动I/O
-
异步I/O
其中前四种是同步I/O模型,只有第五种是异步的。
1 阻塞I/O模型
![](https://img.haomeiwen.com/i1700062/1b818f38d567e2bf.png)
当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据,对于network IO来说,一般数据在一开始还没有到达,kernel会等待足够的数据到来,而在用户进程这边,整个进程会被阻塞挂起,当kernel等到数据准备好,它就会将数据从内核缓冲区拷贝到用户进程缓冲区,然后kernel返回结果,用户进程才进入就绪状态,重新运行起来。
作为一个网络的服务器,阻塞式的等待会浪费大量的资源和时间,当服务器调用函数sendto
给远端的一个客户端发送数据时,这时候不做其他的事情那么往后的动作都堵塞在后面,这样显然是不合适的,解决这种问题的方法往往是创建一个进程或者线程去解决这件事。
阻塞式的I/O的好处就是它非常的稳定,他能保证连接保证接受和发送数据的确定性,这是大多大的互联网公司使用多线程的原因,并且使用线程池也会大大增加线程开启的效率。
2 非阻塞I/O模型
![](https://img.haomeiwen.com/i1700062/1e3361d5cc049da0.png)
当用户进程发出recvfrom操作时,如果kernel中的数据还没有准备好,那么它并不会阻塞用户进程,而是立刻返回,从用户进程角度讲 ,它发起一个recvfrom操作后,并不需要等待,而是马上就得到了一个结果,用户进程通过结果判断数据是否准备好,如果没有准备好过段时间再次发送recvfrom操作,一旦kernel中的数据准备好了,并且又再次收到了用户进程的recvfrom调用操作,马上将数据从内核缓冲区拷贝到了用户进程缓冲区
非阻塞方式下,当前执行流以睡眠的方式来等待请求或者数据的到来,而采用轮询的方式,每隔一个时间段便返回一次,如果有数据到来则返回,如果没有,则返回一个错误码WSAEWOULDBLOCK,需要不断的用循环来等待数据的成功返回,这个过程往往会占用大量的CPU。
非阻塞的缺点,一次只能等待一个事件的到来。
3 阻塞的I/O复用模型(select,poll,epoll...)
![](https://img.haomeiwen.com/i1700062/4525b5bcdbb700e6.png)
- select,poll,epoll就是IO多路复用模型,其特点就在于单个系统调用可以同时处理多个网络连接的IO,它的基本原理就是select/poll/epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程
- 当用户进程调用了select/poll/epoll,整个进程会被阻塞,而同时,kernel会“监视”所有select/poll/epoll负责的socket,当任何一个socket中的数据准备好了,select/poll/epoll就会返回。这个时候用户进程再调用recvfrom操作,将数据从内核缓冲区拷贝到用户进程缓冲区
缺点就是,无论是上面所述的哪种函数,每次发送或者接受一个数据都会进入两次内核与用户的转换。
4 信号驱动的I/O复用模型(SIGIO)
![](https://img.haomeiwen.com/i1700062/41e0bc40d7b436f7.png)
用的很少,就不做讲解了。
5 异步I/O模型(POSIX的aio_系列函数)
![](https://img.haomeiwen.com/i1700062/8b8811a505d129b2.png)
当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者的输入输出操作。
6 5种模型的比较
![](https://img.haomeiwen.com/i1700062/23aa2b158d9d563d.png)
参考:
https://blog.51cto.com/wpfbcr/1783543
https://zhuanlan.zhihu.com/p/121826927
网友评论