了解并发文件从多个进程写(Understanding concurrent file writes

2019-06-18 13:31发布

从这里: 是文件中的UNIX追加原子

假设在多个进程打开相同的文件,并追加到它的情况下。 O_APPEND保证寻求文件的末尾,然后开始写操作是原子。 所以多个进程可附加到相同的文件,没有过程将尽量覆盖任何其他进程的写,因为每个写大小是<= PIPE_BUF。

我写了一个测试程序,其中多个进程打开并写入同一个文件( write(2) 我要确保每个写大小> PIPE_BUF(4K)。 我原以为会看到一个过程覆盖别人的数据实例。 但是,这并不发生。 我用不同的写大小进行测试。 那是只是运气或者是有一个原因,它不会发生呢? 我的最终目标是要了解,如果多个进程附加到同一个文件需要协调他们写。

下面是完整的程序。 每个进程创建一个int缓冲区,以其充满所有值rank ,打开一个文件,并把它。

规格:1.4.3的openmpi在openSUSE 11.3 64位

编译为:mpicc -O3 test.c的,运行如下:-np的mpirun 8 ./a.out

#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>

int 
main(int argc, char** argv) {
    int rank, size, i, bufsize = 134217728, fd, status = 0, bytes_written, tmp_bytes_written;
    int* buf;
    char* filename = "/tmp/testfile.out";

    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);

    buf = (int*) malloc (bufsize * sizeof(int));   
    if(buf == NULL) {
        status = -1;
        perror("Could not malloc");
        goto finalize;
    }
    for(i=0; i<bufsize; i++) 
        buf[i] = rank;

    if(-1 == (fd = open(filename, O_APPEND|O_WRONLY, S_IWUSR))) {
        perror("Cant open file");
        status = -1;
        goto end;
        exit(-1);
    }

    bytes_written = 0;
    if(bufsize != (tmp_bytes_written = write(fd, buf, bufsize))) {
        perror("Error during write");
        printf("ret value: %d\n", tmp_bytes_written);
        status = -1;
        goto close;
    }

close:
    if(-1 == close(fd)) {
        perror("Error during close");
        status = -1;
    }
end:
    free(buf);
finalize:
    MPI_Finalize();
    return status;
}

Answer 1:

写的原子性小于PIPE_BUF仅适用于管道和FIFO。 对于文件写入,POSIX说:

POSIX.1-2008的这卷不指定从多个进程文件并发写入的行为。 应用程序应该使用某种形式的并发控制。

......这意味着你对你自己的 - 不同的UNIX的喜好将给予不同的保证。



Answer 2:

首先,O_APPEND或Windows上的等效FILE_APPEND_DATA意味着最大程度文件(文件“长度”)的增量是并发作家下原子 ,并且是由任何量,不只是PIPE_BUF。 这是通过POSIX,和Linux,FreeBSD的,OS X和Windows保证所有正确实现它。 桑巴也正确地实现它,V5并没有因为它缺少的线制式能力以原子追加NFS之前。 所以,如果你打开你的文件只追加-, 并发写入不再与 NFS,除非涉及撕裂彼此在任何主要的操作系统

这只字未提是否读取永远不会看到一个断写,虽然,对POSIX说,下面讲读(原子性)和write()到常规文件:

下面所有的职能应是原子在当他们常规文件或符号链接运行在POSIX.1-2008指定的效果相互... [许多功能] ...阅读()...写( )...如果每个两个线程调用这些函数之一,每个呼叫应由看到所有的其他呼叫,或没有人的规定影响。 [资源]

写操作可以相对于被序列化到其他的读取和写入。 如果文件数据的读取()可以证明(通过任何方式),以数据的写入()后发生的,它必须反映write()方法,即使调用是由不同的工艺制造。 [资源]

但反过来:

POSIX.1-2008的这卷不指定从多个进程文件并发写入的行为。 应用程序应该使用某种形式的并发控制。 [资源]

所有这三个要求的安全解读会建议所有重叠在同一个文件的程度,必须写入相对于彼此被序列化和读取,从而断写永远不会出现,以飨读者。

一个不太安全的,但仍然允许解释可能是,读取并彼此线程之间写操作只连载同一进程内,和进程之间写入是串行化仅相对于读出(即,存在顺序一致的I / O线程之间在排序一个过程,但过程之间的I / O是唯一的获得释放)。

当然,仅仅因为该标准要求这些语义并不意味着实现符合,但实际上与FreeBSD ZFS表现完美,非常最新的Windows(10.0.14393)与NTFS表现完美,近期ext4的Linux版本能够正常运行,如果O_DIRECT是。 如果您想在OS和申请体系如何重大符合标准更详细,看到这个答案



Answer 3:

这不是运气,在这个意义上,如果你深入到内核中你也许可以证明,在你的具体情况永远不会发生的一个过程write插入相邻的另一个。 我假设:

  • 您没有击中任何文件大小限制
  • 你是不是填充在其中创建测试文件的文件系统
  • 该文件是一个普通文件(不是一个套接字,管道或其他什么东西)
  • 该文件系统是本地
  • 缓冲区不跨越多个虚拟存储器映射(这一个被称为是真实的,因为它是malloc()版,其中所说的堆,其连续的。
  • 的处理不会被中断,用信号,或追踪而write()是忙。
  • 有没有磁盘I / O错误,RAM故障,或任何其他异常情况。
  • (其它)

你可能确实发现,如果所有这些假设成立真实的,它是操作系统的你碰巧使用始终内核完成单一的情况下write()用单个原子的连续写入以下文件系统调用。

这并不意味着你可以在这个始终是真实的计数。 你永远不知道什么时候可能不是真的时:

  • 该程序在不同的操作系统上运行的
  • 将文件移动到文件系统NFS
  • 该过程得到一个信号,而write()是正在进行,并且write()返回一个部分结果(大于所请求的字节少)。 不知道是否真的POSIX允许这种情况发生,但我编程防守!
  • 等等...

所以,你的实验不能证明你可以在非交错写入计数。



文章来源: Understanding concurrent file writes from multiple processes