Perl 读取fifo时的重叠输出:如何修复/避免这种情况?
我试图从2个文件中聚合数据,因此我决定通过单独的写入进程将数据发送到指定的fifo,并启动单独的读取进程来读取和处理聚合的数据。所有的读/写操作都发生在一个ramdisk(/dev/shm)上,这个ramdisk方便地大到大约100GB 这是一个工作文件,我确保写入fifo的每条数据线都少于512字节,这样管道就可以保留其原子行为 但在尝试了多次运行之后,我发现读卡器进程正在接收重叠的输出,当我尝试从每个进程传输1000多万行时,这种情况就开始发生了。我的每个数据行都以新行终止 我在“+Perl 读取fifo时的重叠输出:如何修复/避免这种情况?,perl,pipe,fifo,rhel5,overlapped-io,Perl,Pipe,Fifo,Rhel5,Overlapped Io,我试图从2个文件中聚合数据,因此我决定通过单独的写入进程将数据发送到指定的fifo,并启动单独的读取进程来读取和处理聚合的数据。所有的读/写操作都发生在一个ramdisk(/dev/shm)上,这个ramdisk方便地大到大约100GB 这是一个工作文件,我确保写入fifo的每条数据线都少于512字节,这样管道就可以保留其原子行为 但在尝试了多次运行之后,我发现读卡器进程正在接收重叠的输出,当我尝试从每个进程传输1000多万行时,这种情况就开始发生了。我的每个数据行都以新行终止 我在“+>fif
write_log => sub {
my ($filehandle, $log_message) = @_;
select $filehandle ; $|++;
syswrite ($filehandle, $log_message, length($log_message))
or die "write_log: syswrite fail!\n";
},
读卡器进程:
read_log => sub
{
# In my endless reading loop,
# if I detect keyword END 2 times (as
# i have 2 processes), I exit the reading loop
# and do further operations.
#
my ($end_check_value) = @_;
sysopen (FH,$logfile, O_CREAT|O_RDONLY)
or die "($$) read_log: Failed to sysopen\n";
my ($h, $end) = (undef,0);
select FH ; $|++ ;
print STDOUT get_ts().'|'."($$) read_log: now tailing logfile with check count $end_check_value\n";
for (;;)
{
while (my $line = <FH>)
{
chomp $line;
$end++ if $line =~ m/END/g;
last if $end == $end_check_value;
my $key = (split(/\s/,$line))[0];
$h->{$key}++;
}
sleep(1) ; seek (FH,0,1);
# break out of for loop if we
# have collected the 'END' tags
# from all worker processes
if ($end == $end_check_value)
{
print STDOUT get_ts().'|'."($$) read_log: breaking for loop ",
"with end_check: $end_check_value\n";
last;
}
} close (FH);
},
以下是FIFO的性能统计数据,其中多个进程向FIFO写入25000000行,读卡器进程将它们读回散列。平均需要25-30分钟。它比写入文件的进程慢
test string is 141 bytes long
20190426-10:25:13.455|28342|2-test-fifo.pl: Starting..
20190426-10:25:13.456|28345|CHILD starting (read_and_hash)
20190426-10:25:13.456|28345|READ_AND_HASH now hashing files
20190426-10:25:14.458|28346|CHILD starting (s1_data_gather)
20190426-10:25:14.458|28346|Working on sit1 data..
20190426-10:25:14.458|28347|CHILD starting (s2_data_gather)
20190426-10:25:14.458|28347|Working on sit2 data..
20190426-10:48:48.454|28346|Finished working on S1 data..
20190426-10:48:48.457|28342|Reaped 28346
20190426-10:48:48.462|28345|read LAST line from S2 data
20190426-10:48:52.657|28347|Finished working on s2 data..
20190426-10:48:52.660|28342|Reaped 28347
20190426-10:48:52.669|28345|read LAST line from S2 data
20190426-10:48:53.130|28345|READ_AND_HASH finished hashing files
(read_n_hash): finished hashing. keys count
s1 = 25000000
s2 = 25000000
20190426-10:48:53.130|28345|starting comparison. doing source to target
20190426-10:49:49.566|28345|finished comparing source to target. now comparing target to source
20190426-10:50:45.578|28345|comparing target to source ends. finished
20190426-10:51:57.220|28342|Reaped 28345
20190426-10:51:57.220|28342|2-test-fifo.pl: Ending..
您可能必须为要写入的文件启用自动刷新。如果使用open()函数打开文件,而不是通过IO::File之类的OO接口打开文件,那么在成功打开文件(比如$fifo)后,您需要这样的代码
select $fifo;
$| = 1;
请注意,select()为打印选择输出文件句柄,这类文件不指定特定的文件句柄。如果要恢复到目标STDOUT,则在上述操作之后选择STDOUT,或者,要学究化:
my $oldfh = select $fifo;
$| = 1;
select $oldfh;
我不认为文件模式('+您在这里看到的可能是一个简单的并发产品。您假设读卡器及时从FIFO中提取数据。如果两个写卡器都有机会在读卡器再次尝试读取之前写入多条记录呢?如果FIFO通过写入部分达到容量呢?写入er将在写入过程中部分阻塞,然后读取器将有机会清空队列,但不能保证写入部分行的写入者将是下一个要写入的写入者。这将导致交错行 <> P>如果我关于自动填充的回答不能解决你的问题,你可能需要考虑这样写的交错的可能性。
如上面的评论所述,使用数据报套接字(SOCK_DGRAM)可能会更好而不是先进先出。这样,每条消息都是一个原子单元,没有交错的机会。我会尝试一下,然后返回结果。如果它有效,我会接受你的答案作为解决方案。有一件事-打开时使用“啊,如果你在”Re“中读取文件,我想每次管道中没有更多数据时,你都会遇到EOF。”,这不是真的。读取将阻塞(或在非阻塞句柄上返回错误
EAGAIN
或ewoodblock
)。当写入程序关闭句柄时,它确实会返回EOF。是的,我在考虑这样的情况,即您正在跟踪真实文件的尾部。但是写入程序是否在写入之间关闭FIFO?这是一个命名的FIFO,因此这种行为是可能的。我们需要查看代码。文件句柄上的选择非常有效。现在没有更多的重叠数据了输出中的nes。感谢分享您的知识。我真的很感激。答案被接受并投票,因为这是正确的做法。干杯。这可能是一件棘手的事情。您能不能给我们看一下您的(相关)代码?
my $oldfh = select $fifo;
$| = 1;
select $oldfh;