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

时间:2019-01-31 15:08:00

标签: linux perl system-calls atomic file-descriptor

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

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

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

问题:perl sysopen调用与linux的开放系统调用不同吗?是否可以通过多个进程对单个文件实现这种原子写操作?

编辑:添加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 "$1" instances of the same 
# executable which should append to the same output file.

max=$1
[[ -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)。有了这项工作,我现在可以执行并行操作,而不是以前的方法,该方法包括:对file1进行哈希处理,然后对file2进行哈希处理,然后比较2个文件中的键值对。现在,我可以并行执行哈希处理部分,并减少所需的时间-甚至更多。

谢谢

1 个答案:

答案 0 :(得分:6)

opensysopen都无关紧要;密钥是使用syswritesysread而不是print / printf / say / etc和readline / read / { {1}} /等。

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

write(2)映射到单个sysread调用,而read(2) / readline / read / etc等可能导致对eof的多次调用

因此,如果您使用的是POSIX系统,则通过使用read(2)syswrite,您将受到POSIX对这些调用的所有保证(无论它们可能是什么)。


  1. 如果您使用sysread / print / printf / etc,并且将写操作限制为小于(显式或自动)刷新之间的缓冲区大小,则会得到一个say调用。在旧版本的Perl中,缓冲区大小为4 KiB,在新版本的Perl中,缓冲区大小默认为8 KiB。 (大小是在构建write(2)时确定的。)