浅谈5中io模型和同步io、异步io及R模式P模式

   五种io模型:

  阻塞io模型 

 非阻塞io模型  

io复用模型 

 信号驱动io模型  

异步io模型


 其中阻塞与非阻塞

  阻塞io:就是函数调用后,会被挂起,直到内核接受到了io的读写,拷贝到了应用进程的用户态后,函数才返回

 非阻塞io:就是函数调用后,立刻返回结果,这个结果告诉调用函数者内核是否接受并完成了io的读写


阻塞io 非阻塞io io复用  信号驱动io。这四个都可以认为是同步io模型

有人会觉得,同步不是要等待需要的数据吗,那么同步就是阻塞了,异步就是非阻塞了。

其实不然,同步异步 和 阻塞非阻塞 两种概念没有绝对的一等一关系。

这么说吧,同步io意思就是为了需要的数据,在逻辑上会一直‘等待’,但这种‘等待’不一定非得是阻塞的等待,不停的while轮询也算是等待。而最后等待完之后,由应用程序去拿数据,而并非完全由内核包办。

这么说就很清楚了吧,阻塞io的确是同步io,毋庸置疑的,例如TCP中的recv,为了等待[连接fd]上的数据,永远阻塞着,除非对端断开(但是我之前也看过一个文章很有意思,认为也存在异步阻塞io,事实是的确是这样的,即异步之后,阻塞处理事件,这就有点牵强了,不过也能说得通,一般初学者接触到的阻塞函数都是为了同步io设计的,这样记下来就好)。

 但是非阻塞io,比如把TCP的recv设置成非阻塞的,它会立刻返回一个值,一般来说肯定会是-1,(有人会说-1不是recv失败吗,注意recv有个特殊的条件,那么就是返回值为-1时,如果(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)成立,那么仍然表示recv成功,而如果非阻塞的话,errno就会被设置为EAGAIN。如果返回值-1,那么一半还是不满足我们需要接受数据的这个要求,我们会把它放到while里不停的recv,也就是所谓的轮询。那么可以说这种模式仍然为同步,虽然它的函数调用是非阻塞的,但它的行为表现了它是为了等待某种操作完成。只不过没有阻塞那么耿直的直接停在那了。

举个例子,现在是8点,9点的时候有球赛。现在有两种方式度过8-9点这一个小时。

阻塞的做法就是,坐在电视前的沙发上,干巴巴地等着,减轻消耗,球赛一旦开始了,屏幕已经开始播了,我就知道了,直接开始看,这就是阻塞。

而非阻塞的做法就是,我这个人比较好动,这一个小时我不安分,一会儿去拿个零食,一会儿去玩个游戏,但是每过一会儿还得回来盯一下屏幕看比赛开始没,直到比赛开始了,屏幕显示后一刹那,我又盯着屏幕,发现比赛开始了,然后我坐下看比赛。

这两种行为一个是阻塞,一个是非阻塞,但从本质上来看,我都是为了等待球赛的开始。所以说,这就是同步的。

而同步io的特点就是io的操作由应用程序完成的,也就是说,如果我要recv一个buffer,好的我应用程序开始阻塞等待,或者轮询等待,你内核如果收到了recv的数据,你就告诉我,我就不等待了,或者不轮询了(这个取决于代码设计了),我去recv我的buffer去了。不管是轮询还是阻塞,都是应用程序自己主动要求的,不达目的决不罢休的感觉。

而异步io的特点就是io的操作由内核全部完成的,也就是说,比如aio_read,我要读取一些数据存入buffer,好的我调用了函数,然后不管了,直接去执行别的操作了,你内核如果完成了,用个信号通知我就行了。这个就区别与同步的主动要求了,而换成了被动的接受,如果你内核io操作ok了,你给我个信号,我去做处理,你不给我信号,我也不会主动搭理你。

仍然拿球赛作为例子,我看球赛,但是这时我换了一个超智能电视。8点开始,我给电视说,球赛开始了告诉我啊,我去干别的事了,比如玩电脑,假如我真的玩过了,超过9点了,就代表我这个人(进程)没有被等待球赛(等待io可读数据)这件事束缚住,虽然错过球赛不符合我的意愿,但也没办法,我不会看时间,如果超级电视没通知我,我就没法采取措施(调用信号函数),但是这却不会束缚我不能干别的事,比如玩电脑;但是如果一切正常,然后到了9点,电视就响了告诉我球赛开始了,等于是相比起同步io的例子,我要自己观测球赛的开始时间,然后去看。而异步io就是球赛开始了,电视(内核)通过响声(信号)告诉我(应用程序)它帮我读到球赛开始了(数据读好了)

而io复用也是同步io,程序会阻塞在select,poll,epollwait这些io复用函数上,只不过它们可以同时阻塞多个fd上的io,所以叫‘复’用。说到底也算是主观等待某些数据的到来,因此也算同步io。


因此可以总结下同步io和异步io的区分。

如果进行io操作的过程中,进程发生过等待(阻塞或者轮询),那么它就是同步的io。可以理解成进程必须和内核步调一致。

反之,如果全程没有发生过等待,我有过io请求,你内核io操作完,给我一个提示,例如信号,我应用程序立刻去解决它,可以说,进程全程没有阻塞过,进程运行的整个的时间段 中 内核后台同时也io处理数据,可以说是异步io了。听起来好像比起同步io挺高效的,但根据不同的场合和需求选择同步io还是异步io吧,没有哪个绝对的好坏,只有相对的适用。

  这里又涉及到两个模式,即r模式和p模式。

Reactor模式就是同步io,主线程epollwait客户端上的可读事件,如果有事件发生了,ok,交给工作线程去recv数据吧。

Proactor模式就是异步io,主线程同样epollwait,但是这时并不像R模式那样,让线程去recv等待数据,而是用aio_read注册‘已读完’事件。然后'已读完'事件如果发生。那么之前ai_read会发回一个信号,信号处理函数里会开启线程处理数据,而数据的大小和地址都在aio_read注册时被指定好了。线程拿到数据后,同时也会用aio_write注册‘已写完’事件,同样也会有相应的信号处理函数,处理写完数据后的事情。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章