一.阻塞与非阻塞
- 阻塞
在网络通信中,我们会调用read,recv等函数去读取网络中发来的数据,数据先会来到内核的TCP缓冲区中,然后再将数据从内核拷贝到用户的buff中,当缓冲区中没有数据时,read默认会阻塞,也就是将整个进程挂起,等到数据的到来,直到数据就绪,才会被唤醒继续执行;
包括send,send做的工作是将数据从user拷贝到kernel,但是有时内核缓冲区没有足够的位置,也会发生阻塞;
阻塞还是不阻塞的过程由read(),send()等函数内部的实现所决定. - 缺点:需要等待的时间不确定,不过可以设置超时时间,开启一个专门读取或者发送线程去干这些事等一些手段控制.
-
非阻塞 NIO
与阻塞不同,同样的当我们调用read等函数时,当内核缓冲区还没有数据到,read会立刻返回,不会等待,所以当前的进程不会被OS挂起;也就是说在一定程度上保证了当前进程不会被挂起. -
分析:没有数据也会返回,所以需要我们循环不断的去读取;或者可以用IO复用函数将我们想要监控的fd交给他们,有数据到了内核帮我们处理.
二.将fd修改为非阻塞:
#include<fcntl.h>
三.同步异步
- 同步:可认为是一种顺序执行,是一种依赖的关系.在实际开发中,我们某些函数A的执行依赖其他函数B的返回,也就是A函数的执行要等到B函数执行完毕,A在那里死等B.
- 异步:可以认为是一种并行处理方式,就比如说我们看视频需要加载,我们不是等待,而是可以给女朋友回个微信(也有可能是男朋友),当然你也可能是实力单身.
来看一个比专业一点的回答:
所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回
当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,
通过状态,通知和回调来通知调用者. 如信号机制,回调函数等
(在描述阻塞和非阻塞的时候并没有感觉到那个效率高,这里明显感觉异步调用可以干很多事!)
四.五种IO模型: 网上大佬举了个很合适的例子
- 阻塞IO:
A拿着一支鱼竿在河边钓鱼,并且一直在鱼竿前等,在等的时候不做其他的事情,十分专心
只有鱼上钩的时,才结束掉等的动作,把鱼钓上来。
- 非阻塞IO:
B也在河边钓鱼,但是B不想将自己的所有时间都花费在钓鱼上,
在等鱼上钩这个时间段中,B也在做其他的事情(一会看看书,一会读读报纸,一会又去看其他人的钓鱼等)
但B在做这些事情的时候,每隔一个固定的时间检查鱼是否上钩。
一旦检查到有鱼上钩,就停下手中的事情,把鱼钓上来。
每次客户询问内核是否有数据准备好,即文件描述符缓冲区是否就绪。
当有数据报准备好时,就进行拷贝数据报的操作。
当没有数据报准备好时,也不阻塞程序,内核直接返回未准备就绪的信号,等待用户程序的下一个轮寻。
- 信号驱动IO:
C也在河边钓鱼,但与A、B不同的是,C比较聪明,他给鱼竿上挂一个铃铛,
当有鱼上钩的时候,这个铃铛就会被碰响,C就会将鱼钓上来。
信号驱动IO模型,应用进程告诉内核:
当数据报准备好的时候,给我发送一个信号,对SIGIO信号进行捕捉,
并且调用我的信号处理函数来获取数据报。
- IO多路转接(IO复用):
D同样也在河边钓鱼,但是D生活水平比较好,D雇了个下人帮他看着所有的鱼竿,
一旦哪个鱼竿有动静,就将这个鱼竿交给老板D,老板去收鱼就好了.实际上这个下人是内核.emmmm
select poll epoll
- 异步IO:
E也想钓鱼,但E有事情,于是他雇来了F,让F帮他等待鱼上钩,E去把妹,一旦有鱼上钩,
F就打电话给E,E就会将鱼钓上去。 (打电话也就是 信号,回调等通知手段)
注意区分这里的同步异步和并发编程中的同步异步.
看了一些讲的比较好的博文,这篇还不错:
https://blog.csdn.net/ZYZMZM_/article/details/97814160