(一)小情景模拟阻塞与非阻塞,同步与异步
所需材料:本人、普通水壶、水开后会发出响声的水壶
情景一:我主动用普通水壶去烧水(主动请求事件:同步),并且一直站在旁边等待水开(等待数据准备好在进行下一步,阻塞),然后做饭; 同步阻塞
情景二:我主动用普通水壶去烧水(主动请求事件)不需要一直检测,每隔一段时间会过来看水是否开了(没有一直等待事件:非阻塞,但还是存在主动查看事件的行为),一直到查看到水开之后做饭;同步非阻塞
情景三:我主动请求用水开后会发出响声的水壶去烧水,然后一直站在那里,不会在每隔一段时间看水有没有开,而是等水开了水壶发出声音通知我(并不是我主动的查看水是否开了,而是等待数据就绪通知),我在做饭。 异步阻塞
情景四:我主动请求用水开后会发出响声的水壶去烧水(主动请求事件),然后我去干别的事情(没有自己主动等待查看数据就绪并且去处理其他事情),等待水开后水壶发生通知我,我在去做饭。异步非阻塞
总结:
(1)阻塞和非阻塞是指进程访问的数据如果尚未就绪,进程是否需要等待;如果等待,则进入阻塞;如果不等待则为非阻塞;
(2)同步和异步是指访问数据的机制;
同步一般指主动请求并等待I/O操作完毕的方式;当数据就绪后在都写的时候就必须阻塞;
异步是指主动请求数据后可以继续处理其他任务,随后等待I/O操作完毕,这样在进程都写时就不会阻塞;
(3)阻塞与非阻塞的区别
阻塞和非阻塞关注的是进程在等待调用结果(消息、返回值)时的状态。
阻塞是指调用结果返回之前,当前进程会被挂起。调用进程只有在得到结果才会返回。
非阻塞调用指不能立刻得到结果,该调用不会阻塞当前进程
(4)同步与异步的区别:
同步与异步关注的是进程之间的协作方式,
同步是A进程必须得到B进程通知才能去执行某件事(A执行),
异步是指A进程通知B进程去执行然后立刻得到返回,然后就可以去做自己的事,B完成之后会给A发一个通知(B执行)。
(二)网络I/O模型
(1)同步I/O模型: 阻塞I/O 非阻塞I/O 多路复用I/O 信号驱动式I/O
异步I/O模型:异步I/O
(2)同步阻塞I/O
对于网络socket流来说,主要分为两部分:一是等待网络分组到达,将分组内容拷贝到内核的某个缓冲区;二是将内核缓冲区内容拷贝给用户进程空间。
同步阻塞I/O
- 首先现在应用进程系统调用recv接收数据,发现内核缓冲区没有就绪数据到达,就等待数据就绪;
- 数据就绪好将内核缓冲区数据拷贝给进程空间,通知进程读取数据,这期间应用进程一直在等待数据就绪;
- 同步阻塞I/O模型如下图所示:
(2)同步非阻塞I/O
- 首先现在应用进程系统调用recvfrom接收数据,发现内核缓冲区没有就绪数据到达,就返回不处理,但还是会隔段时间查看数据是否就绪;
- 直到内核数据就绪,将内核缓冲区数据拷贝给进程空间,通知进程读取数据,这期间应用进程一直在等待数据就绪;
- 同步非阻塞I/O模型如下图所示:
在linux下,可以通过设置socket使其变为non-blocking,这样socket就为一个非阻塞socket;
- (3)同步多路复用I/O
多路I/O复用受阻于多个socket,只要检测到任意一个有可读事件,则应用进程就会读取阻塞数据。多路/O复用模型如图所示:
- 图中阻塞于
select
调用,等待数据报套接字变为可读。当select返回套接字可读这一条件的时候,则调用recvfrom
把所读数据报复制到应用进程缓冲区; - 同步非阻塞方式需要用户进程不停的轮询,而此时的多路I/O复用是由内核来帮他监听可读事件并通知他;
- 同步非阻塞方式每次只能监听一个socket,而可以等待多个socket,能实现同时对多个IO端口进行监听,当其中任何一个socket的数据准好了,就能返回进行可读,然后进程再进行recvform系统调用,将数据由内核拷贝到用户进程;
- UNIX/Linux 下的
select、poll、epoll
都是用来实现多路I/O复用的; - 不需要等待一个socket数据全部到达再处理,而是一部分socket的数据到达了就通知用户进程;
- Linux下的I/O复用包括select、poll、epoll,这三都是用来监听自己负责的socket状态,当内核发现可读事件时通过应用程序并进行处理。
- 为了实现服务器的高并发,经常使用I/O复用
- 并发:比如一个服务器可以连接多个客户端;
- 并行:在同一时间几个线程各自处理各自的事情
(4)信号驱动I/O模型
信号驱动式IO就是指进程预先告知内核,当某个描述符上发送事件时,内核使用信号通知相关进程。信号驱动式IO并没有实现真正的异步,因为通知到进程之后,依然是由进程来完成IO操作。
- 首先开启套接字的信号驱动式IO功能,并且通过
sigaction
系统调用安装一个信号处理函数,该函数调用将立即返回,当前进程没有被阻塞,继续工作; - 当数据报准备好的时候,内核则为该进程产生
SIGIO
的信号; - 随后既可以在信号处理函数中调用
recvfrom
读取数据报,并且通知主循环数据已经准备好等待处理,也可以通知主循环让它读取数据报;(其实就是一个待读取的通知和待处理的通知);
(5)异步式I/O
信号驱动式IO和异步IO的区别:
信号驱动式IO是进程事先建立SIGIO的信号处理程序(用sigaction设置IO信号的处理方式),有如果有数据到来,内核会给进程发一个信号,通知进程,进程捕获到这个信号就会去执行处理函数部分,一般在这个函数中执行IO操作(信号发生在IO之前,IO由进程完成)。
异步IO是有进程请求异步读操作,会把套接字描述符,缓冲区指针、缓冲区大小和文件偏移一起发给内核,如果有数据到来,内核会把数据拷到相应的位置,然后给进程发一个信号,如果数据没有来,也必须等数据来了,内核把数据拷完之后,才给进程发一个信号,由内核通知我们IO操作何时完成(信号发生在IO之后,IO由内核完成)
异步式I/O的模型