gpt4 book ai didi

c++ - writev() 真的是原子的吗?

转载 作者:太空宇宙 更新时间:2023-11-04 04:12:40 25 4
gpt4 key购买 nike

这是man writev说:

The data transfers performed by readv() and writev() are atomic: the data written by writev() is written as a single block that is not intermingled with output from writes in other processes (but see pipe(7) for an exception); analogously, readv() is guaranteed

这是来自 man 7 pipe :

   O_NONBLOCK disabled, n <= PIPE_BUF
All n bytes are written atomically; write(2) may block if there is not room for n bytes to be written immediately

O_NONBLOCK enabled, n <= PIPE_BUF
If there is room to write n bytes to the pipe, then write(2) succeeds immediately, writing all n bytes; otherwise write(2) fails, with errno set to EAGAIN.

O_NONBLOCK disabled, n > PIPE_BUF
The write is nonatomic: the data given to write(2) may be interleaved with write(2)s by other process; the write(2) blocks until n bytes have been written.

O_NONBLOCK enabled, n > PIPE_BUF
If the pipe is full, then write(2) fails, with errno set to EAGAIN. Otherwise, from 1 to n bytes may be written (i.e., a "partial write" may occur; the caller should check the return value from write(2) to see how many bytes were actually written), and these bytes may be interleaved with writes by other processes.
$ cat writev.c
#include <string.h>
#include <sys/uio.h>

int
main(int argc,char **argv) {
static char part1[] = "ST";
static char part2[] = "\n";
struct iovec iov[2];

iov[0].iov_base = part1;
iov[0].iov_len = strlen(part1);

iov[1].iov_base = part2;
iov[1].iov_len = strlen(part2);

writev(1,iov,2);

return 0;
}
$ gcc writev.c
$ unbuffer bash -c 'for ((i=0; i<50; i++)); do ./a.out & ./a.out; done' | wc -c
300 # < PIPE_BUF

# Run the following several times to get the output corrupted
$ unbuffer bash -c 'for ((i=0; i<50; i++)); do ./a.out & ./a.out; done' | sort | uniq -c
4
92 ST
4 STST

如果 writev 是原子的(根据文档),谁能解释为什么不同写入的输出是交错的?

更新:

一些相关数据来自strace -fo /tmp/log unbuffer bash -c 'for ((i=0; i<10000; i++)); do ./a.out & ./a.out; done' | sort | uniq -c

13301 writev(1, [{iov_base="ST", iov_len=2}, {iov_base="\n", iov_len=1}], 2 <unfinished ...>
13302 mprotect(0x56397d7d8000, 4096, PROT_READ) = 0
13302 mprotect(0x7f7190c68000, 4096, PROT_READ) = 0
13302 munmap(0x7f7190c51000, 90695) = 0
13302 writev(1, [{iov_base="ST", iov_len=2}, {iov_base="\n", iov_len=1}], 2) = 3
13301 <... writev resumed> ) = 3
24814 <... select resumed> ) = 1 (in [4])
13302 exit_group(0 <unfinished ...>
13301 exit_group(0 <unfinished ...>
13302 <... exit_group resumed>) = ?
13301 <... exit_group resumed>) = ?
24814 futex(0x55b5b8c11cc4, FUTEX_WAKE_PRIVATE, 2147483647 <unfinished ...>
24807 <... futex resumed> ) = 0
24814 <... futex resumed> ) = 1
24807 futex(0x7f7f55e8f920, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
13302 +++ exited with 0 +++
24807 <... futex resumed> ) = -1 EAGAIN (Resource temporarily unavailable)
13301 +++ exited with 0 +++
24807 futex(0x7f7f55e8f920, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
24814 futex(0x7f7f55e8f920, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
24807 <... futex resumed> ) = 0
24814 <... futex resumed> ) = 0
24807 read(4, <unfinished ...>
24814 select(6, [5], [], [], NULL <unfinished ...>
24807 <... read resumed> "STST\n\n", 4096) = 6
24808 <... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 13302
24807 write(1, "STST\n\n", 6 <unfinished ...>

最佳答案

按照规定,对于管道,当总 iov 长度不超过 PIPE_BUF 时,是的,因为:

The writev() function shall be equivalent to write(), except as described below

管道也不异常(exception)(管道一词甚至没有出现在 the writev specification 中)。

在 Linux 的实践中,也许不是。 writev 等同于单个 write 仅适用于实现"new"(大约 15 年前)iov 的内核文件类型-基于读/写后端。有些,如终端,仅实现使用单个缓冲区的旧接口(interface),Linux 将 writev(或 readv)模拟为多个 write 调用(或响应 read 调用)。 readv 案例也有问题,如您所见 in this commit to musl libc .

我不确定管道是否受此问题影响。您必须深入研究内核源代码。

关于c++ - writev() 真的是原子的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55527330/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com