为什么 `aio_write` 不允许并发缓冲区访问?

Why does `aio_write` disallow concurrent buffer access?

Linux man-page for aio_write

The buffer area being written out must not be accessed during the operation or undefined results may occur.

我强调的是"accessed",严格解释不仅是存储到缓冲区,而且是从缓冲区加载。

Mac OS X 上的手册页说

Modifications of the Asynchronous I/O Control Block structure or the buffer contents after the request has been enqueued, but before the request has completed, are not allowed.

这听起来更合理一些;可以读取缓冲区,但不能修改。不过,违规的后果仍然含糊不清。

考虑如何在 OS 中实现这一点,我不明白为什么读取访问会成为问题,而我可以从并发写入中想象的唯一问题是实际写入的数据可以是初始缓冲区内容和并发存储到缓冲区的任意组合。

然而,未定义的行为带来了很多可能性,考虑到这一点,我们可以在访问时获取 SIGSEGV(底层页面被锁定以防止并发访问?),或者读取可能 return 垃圾数据(文件系统进行就地加密或压缩?),或者文件可能留下永久不可读的块(块校验和,然后同时修改,然后写出?)。未定义的行为甚至不排除存储设备固件崩溃,或 OS.

我的问题是,考虑到我们拥有的系统和硬件,实际、合理地会发生什么?我假设语言是故意含糊不清的,以免限制未来的实施。

My question is, what could actually, reasonably happen, given the systems and hardware we have? I assume the language is left intentionally vague to not constrain future implementations.

仔细看看 API:

int aio_write(struct aiocb *aiocbp);

注意到它没有指向 const 的指针吗?警告很清楚,一旦你将 aiocbp 参数传递给 aio_write(),数据属于 AIO 代码,直到操作完成。您可以读取数据,但您可以合理地期望它的状态是什么?根据 API 和规范,您根本不能指望任何东西。甚至观察到的行为也可能看起来完全是随机的。此外,出于性能(一致性)原因,AIO 可能会锁定该块的缓存行,任何来自另一个内核的读取都可能会干扰整个系统的性能。

在缺少 lock/unlock 语义的情况下,无论何时将非常量数据传递给另一个执行线程,您都不能合理地期望从该数据块中持续读取任何内容,直到 API你正在使用的已经完成了你期望它执行的任何工作。无论他们的文档是否这样说,这都是事实。

Linux,BSD(MacOS 是 BSD 风格),POSIX 说不同的话。

POSIX 说:

For any system action that changes the process memory space while an asynchronous I/O is outstanding to the address range being changed, the result of that action is undefined.

Linux 手册似乎更严格,有两种可能:

  1. 这是解释的问题。作者可能考虑过 write accesses 但只是写了 accesses,
  2. 可能是任何访问因为实现可以自由使用任何可能禁止任何访问的机制(IO期间的严格锁定或保护)。

BSD 还说:

If the request is successfully enqueued, the value of iocb-_aio_offset can be modified during the request as context, so this value must not be referenced after the request is enqueued

因此明确禁止某些读访问(对控制结构)。

正如 Martin 在评论中所说:我不知道为什么有人会想在 I/O 完成通知 之前访问 structs/buffers/s。但它也太严格了:好的,对于 写访问权限 来说很明显,但是可以想象(虽然不常见)您想要 读访问权限 [=36] 的场景=] 在 IO 期间写入缓冲区(在显示时写入帧缓冲区内容 - 或类似方式)。

无论如何,如果您违反了限制,可能会发生任何不好的事情,所以请不要违反。