网络I/O模型

首先先说一下同步、异步和阻塞、非阻塞的一些概念

1、同步和异步

    同步和异步描述的是用户线程与内核的交互方式:

    同步是指用户线程发起I/O请求后需要等待或者轮询内核I/O操作完成时才能继续执行

    异步是指用户线程发起I/O请求后仍继续执行,当内核I/O操作完成后会通知用户线程,或者调用用户线程注册的回调函数。

2、阻塞和非阻塞

    阻塞和非阻塞描述的是用户线程调用内核I/O操作的方式:

    阻塞是指I/O操作需要彻底完成后才返回到用户空间

    非阻塞是指I/O操作被调用后立即返回给用户一个状态值,无需等到I/O操作彻底完成。

    一个I/O操作其实分成了两个步骤:发起I/O请求和实际的I/O操作

    阻塞I/O和非阻塞IO的区别在于第一步,发起I/O请求是否会被阻塞,如果阻塞直到完成那么就是传统的阻塞I/O,如果不阻塞,那么就是非阻塞I/O

扫描二维码关注公众号,回复: 2317631 查看本文章

    同步I/O和异步IO的区别在于第二个步骤是否阻塞,如果实际的I/O读写阻塞请求进程,那么就是同步I/O

3、常见JavaI/O模型

    a、阻塞I/O模型

        在I/O请求无法立即完成则保持阻塞

        阶段1:等待数据就绪。网络I/O的情况等待远端数据陆续到达,磁盘I/O的情况就是等待磁盘数据从磁盘读取到内核态内存中

        阶段2:数据复制。处于系统安全,用户态的程序没有权限直接读取内核态内存,因此内核负责将内核态内存的数据复制一份到用户态内存中

    阻塞I/O模型图

            

    b、非阻塞I/O

        非阻塞I/O就是告诉内核,当所请求的I/O操作无法完成时,不要讲进程睡眠,而是立刻返回一个错误码,这样请求就不会阻塞

        阶段1:I/O操作函数将不断地测试数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为止。整个I/O请求的过程中,虽然用户线程每次发起I/O请求可以立即返回,但是为了等到数据,需要不打的轮询、重复请求,这是对CPU时间的极大浪费。

        阶段2:数据准备好后,从内核复制到用户空间。

    非阻塞I/O模型图

            

    c、I/O复用模型

        I/O复用会用到select或者poll函数,在这两个系统调用中的某一个上阻塞,而不是阻塞与真正的I/O系统调用。函数也会使进程阻塞,但是和阻塞I/O所不同的是,这个两个函数都可以同时阻塞多个I/O操作。而且可以同时对多个读操作、多个写操作的I/O函数进行检测,知道有数据可读或可写时,才真正的调用I/O函数。

    I/O复用模型图

                

     调用select/poll方法由一个用户态线程负责轮询多个socket,知道某个阶段1的数据就绪,再通知实际的用户线程执行阶段2的复制操作。

    d、信号驱动I/O模型

        首先调用sigaction来安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用recvfrom来读取数据,并通知主循环数据已准备好被处理,可以读取数据报。

    信号驱动I/O模型图

            

    信号驱动模型的好处是:当等待数据到达时,可以不阻塞。主循环可以继续执行,只是等待信号处理程序的通知;或者数据已经被准备好被处理,或者数据已准备好被读取。

    e、异步I/O模型

        异步I/O是POSIX规范定义的。通常,这些函数会通知内核来启动操作并在整个操作完成时通知我们。

    异步I/O模型图

            

    异步I/O使用了Proactor设计模式实现了这一机制。

    当整个过程(包括阶段1和阶段2)全部完成时,通知应用程序来读取数据。

4、I/O模型的比较

    前四种模型的区别是阶段1不相同,阶段2都相同,都是将数据从内核复制到用户空间。而异步I/O的两个阶段都不同于前四个模型。贴一张图

        

    同步I/O操作引起的请求进程阻塞,知道I/O操作完成。异步I/O操作不引起请求进程阻塞。前四种I/O模型都是同步I/O模型,而异步I/O模型才是真正的异步I/O。


猜你喜欢

转载自blog.csdn.net/yanghan1222/article/details/80444142