在面试过程中,如果被问到关于IO的阻塞、非阻塞、同步、异步时,我们应该如何回答呢?
结合最近学习的课程,我作出了以下的总结,希望能与大家共同探讨!
先给出 陈硕大神原话:在处理IO的时候,阻塞和非阻塞都是同步IO,只有使用了特殊的API才是异步IO。
阻塞、非阻塞、同步、异步
对于一个典型的网络IO调用来说,阻塞、非阻塞、同步、异步都是在藐视IO的状态,而一个典型的IO调用,可以分为两个阶段:数据准备和数据读写。接下来拿recv()函数来谈这四个概念,
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
可以看出recv()函数需要传入的参数主要是sockfd,应用程序缓冲区buf以及buf的大小 。
首先我们来谈数据准备阶段。
数据准备分为阻塞和非阻塞:
阻塞:是指调用IO方法的线程进入阻塞状态,即sockfd工作在阻塞状态下,如果sockfd内核的tcp接收缓冲区中没有数据可读,recv就会阻塞当前的调用线程,直到有数据可读时返回。
非阻塞:如果sockfd工作在非阻塞模式状态下,是不会改变线程的状态,revc就会立即返回,我们可以通过返回值判断数据是否读入,例如返回值size大于0,表示实际读到数据的字节数;size==0,表示对方关闭了连接;size==-1&&errno=EAGAIN,size==-1表示出错,接着判断errno=EAGAIN或者EWOULDBLOCK表示socket设置为了非阻塞,连接成功但是没有数据,即数据未就绪,不会阻塞。
数据准备就绪以后,进入数据读写阶段
对于同步和异步,是分为IO同步异步以及业务逻辑层面的同步于异步,先来说说IO的同步与异步:
对于IO同步来说:应用程序需要自己将数据由内核缓冲区搬入buf里(不论是阻塞还是非阻塞),没有数据一直等待或者空转,这个期间应用程序不能执行其他事情;
对于IO异步来说:应用程序首先调用系统提供的异步IO接口,比如Linux平台下的aio_read、aio_write,将sockfd, buf以及通知方式(回调函数或者sigio信号通知)告知内核,由内核去将数据搬到buf,在这个期间应用程序可以去执行其他事情,内核将数据搬完后,内核就会按照事先约定的通知方式去通知应用程序,应用程序就可以直接开始处理数据。
注意::不论是阻塞还是非阻塞都是同步的,以及epoll一定是同步IO接口,因为epoll_wait(xxxxx,timeout),结束以后我们依旧要recv()。
再来说说业务逻辑层面的同步与异步,比如登录注册是同步实现还是异步实现:
同步:指的是A操作要调用B操作的API接口时,A操作一直等到B操作执行完后,得到返回值,然后才能继续向下处理。
异步:指的是A操作把自己感兴趣的事件以及通知方式告知于B,A继续执行自己的业务逻辑,等到B监听到相应事件后,再通知A去执行相应的处理逻辑。