write(2)/ read(2)linux中进程之间的原子性

时间:2016-02-23 19:12:15

标签: linux filesystems tmpfs

我有一个案例,其中有两个进程作用于同一个文件 - 一个作为编写者,一个作为读者。该文件是一行文本文件,编写器在循环中重写该行。读者读取该行。伪代码如下所示:

作家流程

char buf[][18] = {
"xxxxxxxxxxxxxxxx",
"yyyyyyyyyyyyyyyy"
};
i = 0;
while (1) {
 pwrite(fd, buf[i], 18, 0);
 i = (i + 1) % 2;
}

读者流程

while(1) {
  pread(fd, readbuf, 18, 0);
  //check if readbuf is either buf[0] or buf[1]
}

运行这两个流程一段时间后,我可以看到readbufxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyxx

我的理解是写入对于大小为512字节的大小是原子的。但是从我的实验来看,原子性看起来只有16个字节。

手册页没有说明普通文件的原子性,它只提到512字节的管道原子性。

我用tmpfs和ext4尝试了这个,结果是一样的。使用O_SYNC,ext4写入成为原子并且我理解它,因为写入在它到达磁盘之前不会返回,但是O_SYNC对tmpfs(/dev/shm)没有帮助。

1 个答案:

答案 0 :(得分:4)

POSIX不对readwrite的原子操作提供任何最低保证,除了管道上的写入(写入最多PIPE_BUF(≥512)字节是保证是原子的,但读取没有原子性保证)。 readwrite的操作以字节值描述;除了管道之外,与围绕单字节write操作的循环相比,write操作不提供额外的保证。

我不知道Linux会给出任何额外的保证,无论是16还是512.在实践中,我希望它依赖于内核版本,文件系统,以及可能还有其他因素,如底层块设备,CPU数量,CPU架构等。

可选synchronized I/O data integrity completion中为O_SYNCO_RSYNC提供的O_DSYNCreadwrite保证SIO POSIX的功能)不是你需要的。它们保证在readwrite系统调用之前将写入提交到持久存储,但不对write操作时启动的read提出任何声明。正在进行中。

在您的方案中,读取和写入文件看起来不像正确的工具集。

  • 如果您只需要传输少量数据,请使用管道。不要过于担心复制:在大多数处理或上下文切换的规模上复制内存中的数据非常快。另外,Linux非常适合优化副本。
  • 如果需要传输大量数据,则应该使用某种形式的内存映射:如果不需要磁盘支持,则为共享内存段;如果是,则为mmap。这并不能神奇地解决原子性问题,但可能会提高正确同步机制的性能。要执行同步,有两种基本方法:
    • 生产者将数据写入共享内存,然后向消费者发送通知,准确指出可用的数据。消费者仅根据请求处理数据。通知可以使用相同的渠道(例如mmap + msync)或不同的渠道(例如管道)。
    • 生产者将数据写入共享内存,然后刷新写入(例如msync)。然后,生产者将一个众所周知的值写入一个机器字(sig_atomic_t通常会起作用,即使其原子性仅在信号上正式保证 - 或者实际上是uintptr_t)。消费者读取一个机器字,并且仅当该字具有可接受的值时才处理相应的数据。