{ 阻塞 vs 非阻塞 }

  • IO Model

    |

    I / O 模型

    I / O 模型的概念大概有:阻塞 / 非阻塞 / 同步 / 异步

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    一个用户进程发起 I/O 请求的例子:

    Linux内核会将所有的外部设备当作一个文件来操作,与外部设备的交互均可等同于对文件进行操作。
    即文件的读写都是通过系统调用进行的。

    Linux内核通过 file descriptor处理本地文件的读写, socket file descriptor 处理 Socket 网络读写
    那么, I/O将会涉及两个系统对象,一个是调用它的用户线程(or thread),另一个是系统内核 (kernel)

    一个读写操作:
    1. 用户进程调用 read 方法向内核发起读请求b并等待就绪
    2. 内核将要读取的数据复制到文件描述所指向的内核缓存区 (系统准备 IO 数据)
    3. 内核将数据从内核缓存区复制到用户的进程空间

    阻塞 vs 非阻塞

    • 阻塞 ( Blocking IO ):用户发起 I/O 操作后,需要等待其操作完成之后才能继续运行

      • 特点:阻塞式 I/O 模型简单。易于理解,但性能差,会照成用户 CPU 大量闲置
      • 优化:可以采用多线程的方式进行请求调用,但并不能解决根本问题

    • 非阻塞 ( No-Blocking IO ):用户进程发起 I/O 操作后,无需等待操作完成,会直接返回调用结果,即如果数据没有准备好,会直接返回失败,这就需要用户进程要定期轮询 I/O 是否就绪

      • 特点:能立即得到返回结果,当使用一个线程去处理 socket 请求,可以极大减少线程数量。但用户线程会不断轮询会增加额外的 CPU 的资源开销

    • 总结:阻塞 IO 与 非阻塞 IO 的本质区别主要在于 用户程序是否再等待调用结果(继续等待还是得到结果先处理其他事情)

    同步 IO vs 异步 IO

    • 同步 IO ( Synchronous IO ) :当系统内核将处理数据操作准备完毕之后,会主动读取内核数据,用户进程需要等待内核将数据复制到用户进程之后,再进行处理
    • IO 多路复用 ( IO- Multiplexing ):可以监视多个描述符,一旦某个描述符读写操作就绪,就可以通知程序进行相应的读写操作

      应用:Linux中使用的 I/O 多路复用机制:select, poll,epoll ( event driven IO),尽管实现的方式不同,但都属于同步 IO,它们都需要在读写事件就绪后,再自己进行读写的操作,内核向用户进程复制数据的过程仍然是阻塞的。

    • 特点:尽管使用了事件驱动判断就绪,但与 Blocking-IO 并没有什么太大的不同,甚至在读取的过程中,因为会使用到两个 system call ( select, recvfrom),相比于 blocking-io 的一个 recvfrom,可能在连接数不高的情况下,性能会更差。但有了 select 的优势就在于系统可以同时处理多个 connnection,效率更高。

    • 异步 IO ( Asynchronous IO ) : 当用户进程发起 IO 请求后,会直接返回请求成功,等到再接受到内核的 signal 通知的时候, IO 操作已经完成了
    • 非阻塞 ( no-blocking io ) vs 异步io ( asynchronous io )

      no-blocking io:虽然大部分时间都不会 block (loop check data ready),但内核数据准备好之后,还是需要主动调用 recvfrom 系统调用进行数据的复制,这期间 process block

      asynchronous io:整个过程会将任务交由内核处理,直到 IO done,才会向用户进程发送信号通知成功

    • 总结:同步 IO 和 异步 IO的本质区别在于内核数据 复制到 用户空间的时候用户线程是否阻塞等待
    • 大总结