文章目录
综述
在Linux下进行网络编程时,常见同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式。
同步和异步的概念描述的是用户线程与内核的交互方式:
同步是指用户线程发起IO请求后需要等待或者轮询内核IO操作完成后才能继续执行;
异步是指用户线程发起IO请求后仍继续执行,当内核IO操作完成后会通知用户线程,或者调用用户线程注册的回调函数。
阻塞和非阻塞的概念描述的是用户线程调用内核IO操作的方式:
阻塞是指IO操作在没有接收完成数据或者没有得到结果之前不会返回,需要彻底完成后才返回到用户空间;
非阻塞是指IO操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完成。
【同步/异步】表示是两个事件交互的是否有序依赖关系
同步:针对执行结果,A事件必须知道B事件的结果M后才执行得到结果。
异步:针对执行结果,执行A事件和执行B事件没有关系。
【阻塞/非阻塞】表示执行过程出现的状态
阻塞:针对执行者来说,执行A事件,执行过程因为条件未满足,执行状态变成等待状态。
非阻塞:针对执行者来说,就是事件A执行遇到未满足条件,执行另外独立的C事件。
总结:两者之间是没有关系的
【同步/异步】
概念上是:事件A,B的结果之间的是否存在依赖关系;
影响上是:保证依赖数据的正确性
【阻塞/非阻塞】
概念上是:自身执行状态。
影响上是:阻塞导致资源浪费。
特别注意:异步只有异步,同步才有阻塞和非阻塞的说法!
例子:
总整体看:传统的请求,是同步的(也是阻塞的),请求响应是有序的(请求响应之间也是等待的);AJAX是异步请求(也是非阻塞的)。
同步不等于阻塞:
单个看:AJAX从客户端执行单个请求看数据是同步,但是执行是非阻塞,在未收到响应继续执行其他请求。
参考:https://www.cnblogs.com/straybirds/p/9479158.html
1.同步阻塞IO(Blocking IO)
传统的IO模型,在linux中默认情况下所有的socket都是阻塞模式。当用户进程调用了read()这个系统调用,内核就开始了IO的第一个阶段:准备数据。对于网络IO来说,很多时候数据在一开始还没有到达(比如,还没有收到一个完整的UDP包),这个时候内核就要等待足够的数据到来。而在用户进程这边,整个进程会被阻塞。当内核一直等到数据准备好了,它就会将数据从内核中拷贝到用户内存,然后内核返回结果,用户进程才解除阻塞的状态,重新运行起来;几乎所有的程序员第一次接触到的网络编程都是从listen()、read()、write()等接口开始的,这些接口都是阻塞型的,一个简单的改进方案是在服务器端使用多线程(或多进程)。多线程(或多进程)的目的是让每个连接都拥有独立的线程(或进程),这样任何一个连接的阻塞都不会影响其他的连接。
2.同步非阻塞IO(Non-blocking IO)
默认创建的socket都是阻塞的,同步非阻塞IO是在同步阻塞IO的基础上,将socket设置为NONBLOCK,这个可以使用ioctl()系统调用设置。这样做用户线程可以在发起IO请求后可以立即返回,如果该次读操作并未读取到任何数据,用户线程需要不断发起IO请求,直到数据到达后,才真正读取到数据,继续执行。整个IO请求的过程中,虽然用户线程每次发起IO请求后可以立即返回,但是为了等到数据,仍需要不断地轮询、重复请求,消耗了大量的CPU资源。一般很少直接使用这种模型,而是在其他IO模型中使用非阻塞IO这一特性。
3.IO多路复用(IO Multiple)
IO多路复用模型是建立在内核提供的多路分离函数select基础之上的,使用select函数可以避免同步非阻塞IO模型中轮询等待的问题,此外poll、epoll都是这种模型。在该种模式下,用户首先将需要进行IO操作的socket添加到select中,然后阻塞等待select系统调用返回。当数据到达时,socket被激活,select函数返回。用户线程正式发起read请求,读取数据并继续执行。从流程上来看,使用select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了添加监视socket,以及调用select函数的额外操作,效率更差。但是,使用select以后最大的优势是用户可以在一个线程内同时处理多个IO请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。
4.信号驱动IO(signal driven IO)
调用sigaltion系统调用,当内核中IO数据就绪时以SIGIO信号通知请求进程,请求进程再把数据从内核读入到用户空间,这一步是阻塞的。
5.异步IO(Asynchronous IO)
经典的Proactor设计模式,也称为异步非阻塞IO。“真正”的异步IO需要操作系统更强的支持。在IO多路复用模型中,事件循环将文件句柄的状态事件通知给用户线程,由用户线程自行读取数据、处理数据。而在异步IO模型中,当用户线程收到通知时,数据已经被内核读取完毕,并放在了用户线程指定的缓冲区内,内核在IO完成后通知用户线程直接使用即可。