I/O 模型

    1778
    最后修改于

    I/O 模型#

    Blocking IO#

    BIO 模型,是完全的同步模型,最符合人类直觉。简单来说就是发起一个 IO 系统调用,然后会导致当前线程出让 CPU 控制权,线程被挂起,直到系统调用完成,在合适的时机再被 OS 唤醒。
    对应于系统调用的openfcntl

    loading...

    这很简单也很有用,但还可以做得更好。

    Non-Blocking IO#

    NIO 模型,非阻塞 IO 模型,这是相对 BIO 的一种改进。在 Linux 上,也就是对于openfnctl 这两个系统调用增加O_NONBLOCK 选项。

    如果可能,文件会以非阻塞模式打开。 open 操作或是对其返回的 FD 执行的任何后续 I/O 操作都不会导致调用进程阻塞。
    如果当前需要做的 I/O 操作对应的文件 / 设备没有准备好(例如被占用),会返回一个错误EAGAIN
    表明当前 I/O 操作没有准备好。
    当无法继续进行 I/O 时(如 Buffer 不足),也会返回一个错误EAGAIN,以及此次 I/O 完成的字节数。
    等下一次,程序再次进行 IO 操作的时候,继续询问,当对应资源准备就绪时,再由程序进行实际上的读写操作,发起 I/O 系统调用。
    loading...

    Note

    值得注意的是,O_NONBLOCK 对常规文件或者块设备无效。当对这样的文件进行 IO 操作时,仍然是阻塞的(即使当前文件被其他进程占用)。但是在 Server 场景下,谈论的多半是 socket 进行通信,此时 O_NONBLOCK 是有效的。
    使用如下命令可以查看所有文件类型

    man 2 stat
    

    通常你会得到如下的结果,每种对应一个文件类型。

    #define        S_IFIFO  0010000  /* named pipe (fifo) */
    #define        S_IFCHR  0020000  /* character special */
    #define        S_IFDIR  0040000  /* directory */
    #define        S_IFBLK  0060000  /* block special */
    #define        S_IFREG  0100000  /* regular */
    #define        S_IFLNK  0120000  /* symbolic link */
    #define        S_IFSOCK 0140000  /* socket */
    #define        S_IFWHT  0160000  /* whiteout */
    

    TODO
    use jslinux show an io example JSLinux (bellard.org)

    NIO 参考#

    open(2) - Linux manual page (man7.org)
    fcntl(2) - Linux manual page (man7.org)
    read(2) - Linux manual page (man7.org)
    write(2) - Linux manual page (man7.org)

    I/O Multiplexing#

    IO 复用模型,与前两个模型不太一样,前两个模型。是针对单文件 I/O 时采用的模型。
    而 IO 复用模型则是基于事件系统的多文件的 I/O。它并非描述单个文件进行 IO 操作时发生的事情。而是多个文件需要 IO 时,要进行的协调操作。在 Linux 中对应着select,poll,epoll 这三个 API。
    其思路在于,同时监听多个目标文件的事件,当其中任何一个目标文件准备好时,可以对准备好的文件进行进行 I/O 处理。
    其中select,poll,epoll 并不进行实际上的 I/O 操作,而是帮助线程监听多个文件的状态,当有文件准备好 I/O 时,可以进行读取。因此,可以它可以与 BIONIO 结合使用。一般而言,结合NIO 是更好的选择。
    loading...

    Note

    值得注意的是,尽管可以通过等待 select 这三个 api 意图是拿到准备好的进行 I/O 的事件,但是并不能保证 ready 队列中 FD,一定可以正常读取 / 写入,仍然有可能会为了等待数据而阻塞。具体可参见 select(2) - Linux manual page (man7.org)BUGS部分。
    可能会将 socket 文件视为 “已准备好读取”,但程序随后实际读取时仍然被阻塞。例如,数据可能在到达后因错误检查和校验而被丢弃,可能会发生这种情况。也可能还存在其他 FD 被错误地报告为就绪的情况。因此,在不应阻塞的套接字上使用 O_NONBLOCK 可能更安全。

    c - Non blocking sockets when using I/O multiplexing - Stack Overflow

    Asynchronous IO#

    异步 IO 模型, IO 相比于 BIO 和 NIO,是完全异步的。从数据的准备,以及数据在内核及进程缓冲区间传送的行为,都由内核代为执行。
    loading...
    尽管这一模型看起来很简单但有多种不同 AIO 实现。

    posix aio#

    作为 GLIBC 的一部分进行实现,提供了一个内部使用线程池的实现(线程池 + BIO/NIO 模拟),效率低下。

    linux aio#

    最初在 linux 2.5 内核中引入。 最初是用于异步磁盘 I/O,且会直接操作磁盘 I/O(绕过缓冲区),因此不适用于非常规文件,如管道、socket 通信(会成为 BIO)Linux AIO status & todo [LWN.net]
    后续加入补丁中增加了对 socket 的文件的支持(通过轮询 ring bufferA new kernel polling interface [LWN.net]

    littledan/linux-aio: How to use the Linux AIO feature (github.com)
    io_submit: The epoll alternative you've never heard about (cloudflare.com)
    How io_uring and eBPF Will Revolutionize Programming in Linux - The New Stack

    io_uring#

    linux 5.1 内核引入的新异步 IO api。有 SQ(Submission Queue)和 CQ(Completion Queue)两个队列,用户程序可以通过 mmap 将此数据结构映射到用户程序空间。当用户提交 I/O 任务时(io_uring_submit),可以减少切换到内核态到次数。简单来说,在轮询模式下,当用户线程修改 SQ,提交了新 IO 任务,如果 SQ 内核线程是活跃的,那么就可以立即获取到该提交并进行处理,此时可以避免系统调用,如果 SQ 非活跃,则进行相应的系统调用,通知内核新的 I/O 任务到达。

    reference#

    kernel_new_features/io_uring/ 文章 / 浅析开源项目之 io_uring.md at main・0voice/kernel_new_features (github.com)

    » asynchronous disk I/O (libtorrent.org)

    [译] Linux 异步 I/O 框架 io_uring:基本原理、程序示例与性能压测(2020) (arthurchiao.art)

    Learn about different I/O Access Methods and what we chose for ScyllaDB

    io stack | 系统技术非业余研究 (yufeng.info)
    Linux 下异步 IO (libaio) 的使用以及性能 | 系统技术非业余研究 (yufeng.info)

    Blocking I/O, Nonblocking I/O, And Epoll (eklitzke.org)

    • 🥳0
    • 👍0
    • 💩0
    • 🤩0
    总浏览量 4,282