在阅读 APUE(第 3 版)一书时,我遇到了开放系统调用及其让用户以O_APPEND
模式打开文件以进行write
原子操作的能力,这意味着多个进程可以写入文件描述符,内核确保多个进程写入单个文件的数据不会重叠并且所有行都完好无损。
在使用 C/C++ 程序成功尝试开放系统调用后,我能够验证相同的内容,并且它的工作方式就像书中描述的那样。我能够启动多个进程,这些进程写入单个文件,并且所有行都可以考虑到他们的进程PID中。
我希望在perlsysopen
中观察到相同的行为,因为我在工作中有一些任务可以从这种行为中受益。试过了,但实际上没有用。当我分析输出文件时,我能够看到竞争条件的迹象(可能),因为有很多次交错的行。
问:perlsysopen
调用与 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 sysopenn";
while ($maxlines != 0) {
print FH "($$) This is sample data line no. $maxlinesn";
$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)。有了这个工作 - 我现在可以执行并行操作,而不是我以前的方法,该方法包括:散列文件1,然后散列文件2,然后比较来自2个文件的键,值对。现在我可以并行完成哈希部分,并缩短所需的时间 - 甚至更进一步。
谢谢
不管你是open
还是sysopen
;关键是使用syswrite
和sysread
而不是print
/printf
/say
/etc和readline
/read
/eof
/etc。
syswrite
映射到单个write(2)
调用,而print
/printf
/say
/etc 可能会导致多次调用write(2)
(即使启用了自动刷新)。[1]
sysread
映射到单个read(2)
调用,而readline
/read
/eof
/etc 可能会导致多次调用read(2)
。
因此,通过使用syswrite
和sysread
,如果您使用的是 POSIX 系统,您将受到 POSIX 对这些调用(无论它们是什么)的所有保证的约束。
- 如果使用
print
/printf
/say
/etc,并将写入限制为小于(显式或自动)刷新之间的缓冲区大小,则会收到单个write(2)
调用。在旧版本的 Perl 中,缓冲区大小为 4 KiB,在较新版本的 Perl 中默认为 8 KiB。(大小是在构建perl
时决定的。