perl sysopen 可以打开文件进行原子写入吗?

Can perl sysopen open file for atomic writes?

在阅读 APUE(第 3 版)一书时,我遇到了 open 系统调用及其让用户以 O_APPEND 模式打开文件进行 write 原子操作的能力,这意味着,多个进程可以写入文件描述符,内核确保多个进程写入单个文件的数据不重叠并且所有行都完好无损。

在使用 C/C++ 程序对开放系统调用进行成功实验后,我能够对其进行验证,并且它的工作原理与书中描述的一样。我能够启动写入单个文件的多个进程,并且所有行都可以 w.r.t 计入它们的进程 PID。

我希望观察到与 perl sysopen 相同的行为,因为我在工作中有一些任务可以从这种行为中获益。尝试了一下,但实际上没有用。当我分析输出文件时,我能够看到竞争条件的迹象(可能),因为有很多次交错的行。

问题:perl sysopen调用和linux的open系统调用不一样吗?是否可以实现这种多进程对单个文件的原子写操作?

编辑:添加 C 代码和用于测试的 perl 代码。

C/C++代码

int main(void)
{
  if ((fd = open("outfile.txt",O_WRONLY|O_CREAT|O_APPEND)) == -1) { 
    printf ("failed to create outfile! exiting!\n");
    return -1;
  }

  for (int counter{1};counter<=MAXLINES;counter++)
  { /* write string 'line' for MAXLINES no. of times */
    std::string line = std::to_string(ACE_OS::getpid())
      + " This is a sample data line ";
    line += std::to_string(counter) + " \n";
    if ((n = write(fd,line.c_str(),strlen(line.c_str()))) == -1) {
      printf("Failed to write to outfile!\n";
    }
  }
  return 0;
}

Perl 代码

#!/usr/bin/perl

use Fcntl;
use strict;
use warnings;

my $maxlines = 100000;

sysopen (FH, "testfile", O_CREAT|O_WRONLY|O_APPEND) or die "failed sysopen\n";
while ($maxlines != 0) {
  print FH "($$) This is sample data line no. $maxlines\n";
  $maxlines--;
}
close (FH);
__END__

更新(初步故障排除后):

多亏了下面答案中提供的信息,我才能让它工作。虽然我 运行 遇到了一些缺失行的问题,这是由于我用 O_TRUNC 打开每个进程的文件造成的,我不应该这样做,但最初错过了它。经过仔细分析 - 我发现了问题并进行了更正。一如既往 - linux 永远不会让你失望 :).

这是我用来启动进程的 bash 脚本:

#!/bin/bash

# basically we spawn "" instances of the same 
# executable which should append to the same output file.

max=
[[ -z $max ]] && max=6
echo "creating $max processes for appending into same file"

# this is our output file collecting all
# the lines from all the processes.
# we truncate it before we start
>testfile

for i in $(seq 1 $max)
do
    echo $i && ./perl_read_write_with_syscalls.pl 2>>_err & 
done

# end.

从输出文件验证:

[compuser@lenovoe470:07-multiple-processes-append-to-a-single-file]$  ls -lrth testfile 
-rw-rw-r--. 1 compuser compuser 252M Jan 31 22:52 testfile
[compuser@lenovoe470:07-multiple-processes-append-to-a-single-file]$  wc -l testfile 
6000000 testfile
[compuser@lenovoe470:07-multiple-processes-append-to-a-single-file]$  cat testfile |cut -f1 -d" "|sort|uniq -c
1000000 (PID: 21118)
1000000 (PID: 21123)
1000000 (PID: 21124)
1000000 (PID: 21125)
1000000 (PID: 21126)
1000000 (PID: 21127)
[compuser@lenovoe470:07-multiple-processes-append-to-a-single-file]$  

观察:

令我惊讶的是,系统上根本没有等待平均负载。我没想到。我相信内核一定已经以某种方式解决了这个问题,但不知道它是如何工作的。我很想知道更多。

这可能有哪些应用?

我做了很多文件到文件的核对工作,我们(在工作)总是需要解析巨大的数据文件(例如每个 30gb - 50gb)。有了这个工作 - 我现在可以进行并行操作,而不是我以前的方法,包括:散列文件 1,然后散列文件 2,然后比较来自 2 个文件的键值对。现在我可以并行执行散列部分,并进一步缩短所需时间。

谢谢

你是open还是sysopen都没关系;关键是使用 syswritesysread 而不是 print/printf/say/etc 和 readline/read/eof/等等

syswrite 映射到单个 write(2) 调用,而 print/printf/say/etc 可以导致多次调用 write(2)(即使启用了自动刷新)。[1]

sysread 映射到单个 read(2) 调用,而 readline/read/eof/etc 可以导致多次调用 read(2).

因此,通过使用 syswritesysread,如果您在POSIX 系统.


  1. 如果您使用 print/printf/say/etc,并将写入限制为小于(显式或自动)刷新之间的缓冲区大小,您我会接到一个 write(2) 电话。缓冲区大小在旧版本的 Perl 中为 4 KiB,在新版本的 Perl 中默认为 8 KiB。 (大小在构建perl时决定。)