多线程是否会执行write()交错

时间:2017-04-27 03:51:18

标签: c multithreading

如果我有两个主题,?plot.forecastthread0 thread1确实:

thread0

const char *msg = "thread0 => 0000000000\n"; write(fd, msg, strlen(msg));

thread1

输出会交错吗?例如。

const char *msg = "thread1 => 111111111\n";
write(fd, msg, strlen(msg));

1 个答案:

答案 0 :(得分:1)

首先,请注意您的问题是"数据是否会被交错?"而不是" write()调用[必须是]原子?"这些是不同的问题......

<强>&#34; TL; DR&#34;总结

  • write()到管道或FIFO小于或等于PIPE_BUF个字节不会被交错
  • write()对其他任何内容的调用将介于&#34之间的范围内;可能不会被交错&#34;到了&#34;永远不会被交错#34;在&#34;中的大多数实现几乎肯定不会被交错#34;到了&#34;永远不会被交错#34;范围。

完整答案

如果您正在写入管道或FIFO,那么对于write()或更少字节的PIPE_BUF调用,您的数据根本不会被交错。

the POSIX standard for write()(注意粗体部分):

  

<强> 基本原理

     

...

     

尝试写入管道或FIFO有几个主要特征:

     
      
  • 原子/非原子:如果在一次操作中写入的总量与来自任何其他进程的数据不交错,则写入是原子的。   当有多个写入器将数据发送到a时,这很有用   单读者。应用程序需要知道写请求的大小   期望以原子方式进行。调用此最大值   {} PIPE_BUF。这个POSIX.1-2008的卷不说是否写   超过{PIPE_BUF}个字节的请求是原子的,但需要这样做   写入{PIPE_BUF}或更少的字节应为原子

  •   
  • ...

  •   

然而,POSIX标准对Windows系统的适用性充其量是值得商榷的。

因此,对于管道或FIFO,数据不会被交错到PIPE_BUF个字节。

这对文件有何影响?

首先,文件追加操作必须是原子的。按照相同的POSIX标准(再次注意粗体部分):

  

如果设置了文件状态标志的O_APPEND标志,则为文件偏移量   应在每次写入之前设置为文件的末尾,并且否   干预文件修改操作应在更改之间进行   文件偏移和写入操作。

另见Is file append atomic in UNIX?

那么这如何适用于非追加write()来电?

实施的共性。有关示例,请参阅Linux read/write syscall implementations。 (请注意,&#34;问题&#34;直接传递给VFS实现,因此答案可能也是&#34;它可能很好地取决于您的文件系统......&#34;)< / p>

内核中write()系统调用的大多数实现将使用相同的代码来执行追加模式和&#34;正常&#34;的实际数据写入。 write()来电,pwrite()来电。唯一的区别将是所使用的偏移的来源 - 对于&#34;正常&#34; write()调用所使用的偏移量将是当前文件偏移量。对于追加write()次调用,使用的偏移量将是文件的当前末尾。对于pwrite()调用,使用的偏移量将由调用者提供(除了Linux被破坏 - 它使用当前文件大小而不是提供的偏移参数作为pwrite()调用打开的文件的目标偏移量追加模式。请参阅the Linux pwrite() man page.)&#34; BUGS&#34;部分

所以附加数据必须是原子的,并且在所有实现中,几乎可以肯定地将相同的代码用于非追加write()调用。

但&#34;写操作&#34;在append-must-be-atomic要求允许返回少于请求的总字节数:

  

write()函数尝试nbyte个字节......

即使在追加操作中也允许部分write()结果。但即使这样,写入的数据也必须以原子方式编写。

部分write()的几率是多少?这取决于你写的是什么。我永远不会看到磁盘填满或实际硬件故障之外的文件的部分write()结果。甚至是部分read()结果。我无法看到write()操作的任何方法,该操作在内核内存中的单个页面上包含其所有数据,导致除磁盘已满或硬件故障情况之外的任何内容中的部分write()

如果再次查看Is file append atomic in UNIX?,您会发现实际测试显示附加write()操作实际上是原子操作。

所以答案是&#34;多线程会不会写()交错?&#34;是,&#34;不,只要数据不跨越内核空间中的页面边界,数据几乎肯定不会被交错为4KB(页面大小)或低于4KB的页面。&#34;甚至跨越页面边界可能也不会改变那么多的几率。

如果您正在编写小块数据,那么这取决于您是否愿意处理交错数据几乎可以肯定但从未发生但可能无论如何的结果。如果它是一个文本日志文件,我会认为它无论如何都不重要。

请注意,使用多个线程写入同一个文件可能不会更快 - 内核可能会锁定内容并有效地单线程实际的write()调用确保它可以满足写入管道和附加到文件的原子性要求。