基于协程io_uring 异步网络库系列 VII(资料): io_uring 和 NONBLOCK | short read 问题 | io_uring blocking io 资料

专栏内的所有笔记本身是和他们自洽的(也许漏了一篇讲如何理解协程和函数式编程中的 call/cc 的笔记,博客中也上传了,当然实际这系列笔记不是一个能够快速上手的,而是一个系列的学习,主要目的是供我自己复习或者有对 C++ 协程与 Proactor 网络框架编写感兴趣的读者。


本篇是对于 VI 中的一些问题的资料补充。我给出一个结论,不要给 io_uring 读写的文件使用 NONBLOCK 设定,否则效率将会降低。(存在 EAGAIN 也认为作为 返回值 res 返回)。

这里其实我之前有一个问题的,对于普通的非阻塞read write 来说可能每次读到写到的 n 都不等于我们请求的 nbytes,然后最好情况也最多是 64kb,不可能够传输长(相对)连接大文件的,这个问题 io_uring 提交的时候是否能支持更大的 nbytes 呢? 当然 manual 的说法是

 Each I/O operation is, in essence, the equivalent  of a system call you would have made otherwise, if you were not using io_uring.

以及:

After you retrieve a CQE, minimally, you might be inter‐

ested in checking the res field of the CQE structure, which corresponds to the return value of the sys‐

tem call's equivalent, had you used it directly without using io_uring.

结论根据 read 的行为是 short read 是可能的。(如果只在本地实测可能没有出现,有人实测到了:io_uring still has its wrinkles. We are scrambling right now to fix a problem du... | Hacker News (ycombinator.com),当然如果你对 regular file 进行,像 eventfd 都会频繁地触发完成尽管什么也没有被写入)。而 short write 在 blocking 的时候应该是不会出现(除了关闭连接),在 nonblocking 的时候可能出现。所以是需要处理的。反正我写的时候是按照可能有 short read 的情况来的。另外,涉及 NONBLOCK flag set 的时候,会有 EAGAIN 的问题。实际后续这个好像针对 socket 打了一些 patch,io_uring: fix up O_NONBLOCK handling for sockets - Patchwork (kernel.org),有时间可以再研究一下。所以目前的时间还是采用 blocking 的 file descriptor 然后考虑 short read 的情况。(实际写小 demo 的时候不用考虑,因为局域网或者完全走 os 的应该很少出现这种情况,除非一次你往 TCP socket 里读写一个G?这种已经要用到 tcp 用来支持 long fat networks 的 scale option了)。实在是不知道,因为没有详细的文档.... 也没有像 UNP 这种分析 select poll 的资料,实际这些都和实现有关。

这种问题其实是 CSAPP 里面说的 short count 问题,这里复习一下:

In practice, you will never encounter short counts when you read from disk

files except on EOF, and you will never encounter short counts when you write

to disk files. 但是对网络 socket fd,这种情况很容易发生。

具体的情况我找不到什么一手资料,不过百度到一个内容(感觉好像 UNP 里面应该会有,但是我忘记了,手头也没有实体书):

1. read总是在接收缓冲区有数据时立即返回,而不是等到给定的read buffer填满时返回。

只有当receive buffer为空时,blocking模式才会等待,而nonblock模式下会立即返回-1(errno = EAGAIN或EWOULDBLOCK)

2. blocking的write只有在缓冲区足以放下整个buffer时才返回(与blocking read并不相同)

nonblock write则是返回能够放下的字节数,之后调用则返回-1(errno = EAGAIN或EWOULDBLOCK)

 对于blocking的write有个特例:当write正阻塞等待时对面关闭了socket,则write则会立即将剩余缓冲区填满并返回所写的字节数,再次调用则write失败(connection reset by peer)。

上面这个 write 的表述我感觉是错(不完整)的,实际编程必须考虑 short write 的问题, 比如你超过内核缓冲区了 c - How many bytes can I write at once on a TCP socket? - Stack Overflow)

io_uring 的系统调用是用 blocking 的还是 non blocking 的好呢?首先,我们早就知道了如果要用 epoll、select、poll 这些时候 nonblock 的 read/write 才好,特别是 ET 的(Blocking I/O, Nonblocking I/O, And Epoll (eklitzke.org))?,但是像 read 不管是 blocking 还是 nonblock 都要处理 short count 问题。linux aio(只支持 direct io 的版本)对于buffered io 的行为 behave exactly as their blocking system calls。

For instance, a read operation under io_uring, started with the IORING_OP_READ operation, which issues the equivalent of the read(2) system call, would return as part of res what read(2) would have returned if called directly, without using io_uring.

来自 <io_uring(7) — Arch manual pages>

就像 epoll 里面的需要对 socket 都采用 non-blocking 的 listen fd,根据 manual:

 On Linux, the new socket returned by accept() does not inherit
       file status flags such as
O_NONBLOCK and O_ASYNC from the
       listening socket.
(解决方案是通过 accept4 指定 nonblock

来自 <accept(2) - Linux manual page>

For level-triggered epoll, nonblocking sockets can help to minimize epoll_wait() calls, its an optimization issue. For edge-triggered epoll, you MUST use nonblocking sockets AND call read() or write() until they return EWOULDBLOCK. If you don't, you can miss kernel notifications.

回来我们的问题,io_uring cat 的实现里面,并没有设置 NONBLOCK flag,实际你不知道他是怎么做 的,最初的 pdf 和 manual 里面都找不到这方面的介绍。ScyllaDB 一个技术文章里面有这样的一句话:

  • It works with any kind of I/O: it doesn’t matter if they are cached files, direct-access files, or even blocking sockets. That is right: because of its async-by-design nature, there is no need for poll+read/write to deal with sockets. One submits a blocking read, and once it is ready it will show up in the completion ring.

来自 <How io_uring and eBPF Will Revolutionize Programming in Linux - ScyllaDB>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值