io操作中常见的几个概念


1. 概述

web应用中,io这块是一个重点,不同的策略对系统的性能影响很大.
对于io操作来说,用户线程发起io请求,内核负责完成此请求,并反馈结果,这两个角色之间需要进行协调:
1. 系统挂起用户线程,操作完成之后,系统返回结果,唤醒用户线程
2. 系统返回状态码,用户线程轮询结果,直到操作完成.
3. 用户线程发起请求时附带回调信息,内核接受请求后返回,用户线程执行别的逻辑;系统会在操作完成之后回调,有的系统自动创建新用户线程完成io处理,有的系统会利用已有的用户线程执行处理逻辑
4. 用户系统注册一个大的信号处理线程,用户线程发起请求后直接返回,系统在操作完成之后发送信号,信号处理线程接手处理.
可以看出,有的方式会造成线程挂起等待,有的会造成线程空转;一个线程好不容易等到系统分配了时间片,却又无奈的交出自己的时间片,浪费系统资源,我们的目标就是尽量少的线程,尽量少浪费线程的时间片,尽量少的线程切换,要做到这点,还是先来说说io中较常见的同步、异步、阻塞和非阻塞这几个概念.

2. 同步

我理解的io同步是指线程做io请求时需要等待内核将数据准备好,浪费了自己的cpu时间片.比如read,如果数据没准备好则线程直接挂起,浪费了一次线程调度;如果数据准备好了,需要将数据从内核空间拷贝到用户空间,可能分给自己1ms,结果等待拷贝数据花了了0.5ms,,实际只花费了0.5ms在业务上.
也有人是从当前线程是否需要等待处理结果来考量,当前线程需要等待,那就是同步,这个和我的观点实际没差别,到了异步这儿就不太一样,我们下面说.

3. 异步

与同步模式相反,异步模式下用户线程只需要注册相关信息,操作系统在操作完成后,将数据从内核空间拷贝到用户空间,然后根据注册信息通知用户进程处理io结果.整个流程中无需用户线程干预,用户线程可以充分利用分配给自己的时间.
有的io调用返回的是状态码,用户线程再根据状态码做相应的处理,但实际去做io操作时还是要主动的发起系统调用去获取数据,这是同步模型;还有的io操作是通过信号驱动,内核在操作完成后发送信号通知用户进程处理,用户进程捕获到信号后再发起系统调用去读取数据,实际上还是同步模式.
回到前面的问题,当前线程不等待io的操作结果这是否可以认为是异步?
我是这样想的,io数据从内核空间拷贝到用户空间这一步所花费的时间,不花在这个用户线程就花在别的用户线程,,总是消耗了用户线程的cpu时间片,除非由内核来驱动用户线程.

4. 阻塞

阻塞是指进行io操作时是否引起线程挂起,挂起了就是阻塞模式.很多时候会把同步和阻塞混淆,主要是因为同步一般都是由阻塞实现的.仔细想想,非阻塞也可以是同步,创建socket时如果指定BLOCK为false,那所有的操作都变成非阻塞,此时可能还是同步模式.
阻塞模式既有有点又有缺点,因为会阻塞,在请求不活跃时会节约cpu;因为会阻塞,也就造成了线程切换,也就浪费了cpu.

5. 非阻塞

没啥好说的,线程一路畅通无阻,看起来挺好,可如果忙着做状态检测,那就极大的浪费了cpu资源.

5 四种理论模型

同步异步,阻塞非阻塞,交叉组合,共有四种模型

5.1 同步阻塞

最经典的使用方式,最简单的,最喜欢的….
io操作会引起线程阻塞,只有系统准备好了时才会有返回,可以说不会浪费任何cpu资源.一般会有少量的线程接入请求,再来一个线程池处理接入的请求,简单有效.如果你的系统处理的连接不多,或者大部分不活跃,不用犹豫就它了.
在请求频繁时,同步阻塞会放大线程调度的成本,如果总得连接数超过线程池大小还会造成请求排队,此时还是尽早调整策略.

5.2 同步非阻塞

建立socket时,可以指定no block,此时所有的操作都会立刻返回,线程再根据返回值做相应的处理,这是一种轮询的方案,相比于同步阻塞,会多出若干次的系统调用,很不合算.
另外还有一种多路复用的io模式,线程先向内核注册若干个感兴趣的事件,然后一直等待,在某个或若干个事件符合条件后,内核将其打包返回,线程接到返回值,再去处理事件.
相比于直接在read/write上block住,同步阻塞多了一个获取事件的调用,因此相比于同步阻塞会有额外的系统开销;不过,因为一个线程可以同时监听多个连接,也就能一次处理若干个连接,在连接数较多时可以节约线程调度的成本,优势明显.
5.3 异步阻塞

这个没啥说的,略

5.4 异步非阻塞

用户线程先发起io请求,内核立刻返回,于是用户线程就可以做其它的事.在数据准备好之后,内核会将数据拷贝到用户空间,再给用户线程发送操作完成的通知.这个模型下,可以每个事件一个线程,也可以每个连接一个线程,相比于其他模式,能够最大化的节省线程数;另外,由于用户线程不需要去主动检查,每个用户线程都能用满自己的时间片,整个系统的性能值得期待.
理论说得这么好可别轻易动心,能用够用省力气才是王道,一般的用个同步阻塞就够了.真要用异步io,最好测测,以前的linux的异步io实现的不是太好,不知道现在啥状况了.

6 java中的模型

jdk一开始仅支持bio模式,也就是同步阻塞模式,在1.4中提供了nio,支持多路复用,1.7中又引入了aio,支持异步io.

本文章版权归环信所有,转载请注明出处。更多技术文章请访问http://blog.easemob.com/?p=235