如果我有两个线程,thread0
和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));
输出会交错吗?例如。
thread0 => 000000111
thread1 => 111111000
最佳答案
首先,请注意,您的问题是“是否将对数据进行交织?”,而不是“是否write()
调用[需要]是原子的?”这些是不同的问题...
“TL; DR”摘要:
小于或等于write()
字节的管道或FIFO中的
PIPE_BUF
不会被交错write()
对其他任何内容的调用将介于“可能不会被交错”到“永远不会被交错”与“几乎肯定不会被交错”到“赢得”的大多数实现之间的范围内。永远不会被交错”范围。 完整答案
如果要写入管道或FIFO,则对于
write()
或更少字节的PIPE_BUF
调用,您的数据将完全不会被交织。每个the POSIX standard for
write()
(请注意粗体部分):RATIONALE
...
An attempt to write to a pipe or FIFO has several major characteristics:
Atomic/non-atomic: A write is atomic if the whole amount written in one operation is not interleaved with data from any other process. This is useful when there are multiple writers sending data to a single reader. Applications need to know how large a write request can be expected to be performed atomically. This maximum is called {PIPE_BUF}. This volume of POSIX.1-2008 does not say whether write requests for more than {PIPE_BUF} bytes are atomic, but requires that writes of {PIPE_BUF} or fewer bytes shall be atomic.
...
POSIX标准在Windows系统上的适用性至多是有争议的。
因此,对于管道或FIFO,不会将数据交织到
PIPE_BUF
字节以内。这如何应用于文件?
首先,文件追加操作必须是原子的。根据相同的POSIX标准(同样,请注意粗体部分):
If the O_APPEND flag of the file status flags is set, the file offset shall be set to the end of the file prior to each write and no intervening file modification operation shall occur between changing the file offset and the write operation.
另请参阅Is file append atomic in UNIX?
那么,这如何适用于非追加的
write()
调用?实现的共性。有关示例,请参见Linux read/write syscall implementations。 (不过,请注意,“问题”直接交给了VFS实现,因此答案也可能是“它很可能取决于您的文件系统...”)
内核内部
write()
系统调用的大多数实现都将使用相同的代码来执行附加模式和“正常” write()
调用的实际数据写入-以及pwrite()
调用。唯一的区别是所使用的偏移量的来源-对于“正常”的write()
调用,所使用的偏移量将是当前文件的偏移量。对于追加write()
调用,使用的偏移量将是文件的当前结尾。对于pwrite()
调用,使用的偏移量将由调用方提供(除非Linux损坏-它使用当前文件大小而不是提供的offset参数作为在追加模式下打开的文件上pwrite()
调用的目标偏移量。请参见“BUGS” the Linux pwrite()
man page.的部分)因此,追加数据必须是原子的,并且几乎可以肯定地将相同的代码用于所有实现中的非追加
write()
调用。但是,“必须在原子上”要求中的“写操作”允许返回的字节数少于所请求的字节总数:
The
write()
function shall attempt to writenbyte
bytes ...
即使在追加操作中也允许
write()
部分结果。但是即使那样,确实要写入的数据也必须是原子写入的。部分
write()
的几率是多少?这取决于您要写的内容。我从未见过磁盘填充以外的文件的部分write()
结果或实际的硬件故障。甚至是部分read()
结果。我无法看到write()
操作的所有方式,该操作将所有数据都存储在内核内存中的单个页面上,从而导致除了磁盘已满或发生硬件故障以外的其他任何原因,导致部分write()
出现。如果再次查看Is file append atomic in UNIX?,您会看到实际测试表明追加
write()
操作实际上是原子的。因此,答案是“多线程会交错写入吗?”是,“不,只要数据不跨越内核空间中的页面边界,几乎可以肯定不会对4KB(页面大小)以下的写入数据进行交织。”甚至跨越页面边界可能也不会改变太多。
如果您要写入少量数据,则取决于您是否愿意处理交错数据的几乎肯定甚至从未发生但可能会发生的结果。如果它是文本日志文件,我认为这没关系。
请注意,使用多个线程写入同一个文件可能不会更快—内核很可能会锁定内容并有效地对实际的
write()
调用进行单线程处理,以确保它可以满足写入的原子性要求。管道并附加到文件。
关于c - 多线程会做write()交错吗,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43648347/