这是man writev所说的:



这是从man 7 pipe:


$ 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时,是,因为:



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

在Linux上,实际上可能不是。等效于单个writevwrite仅适用于实现基于iov的读/写后端的"new"(大约15年前)的内核文件类型。有些工具(如终端机)仅实现使用单个缓冲区的旧接口(interface),Linux将writev(或readv)模拟为多个write调用(或分别为read调用)。 readv的情况也是有问题的,因为您可以看到in this commit to musl libc

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

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

10-11 18:33