今天遇到一个非常奇怪的race condition问题,是一段perl代码,进程互斥使用的是linux的文件锁,操作系统是rhel7.3.
这个函数是把一个文件中的一部分key=value改写,步骤是这样的:
1. 打开待读文件,上共享锁
2. 读文件内容到内存,关文件
3. 更改文件内容
4. 打开待写文件(和待读文件是一个文件),上排他锁
5. 删除文件内容,重新写入更改的文件内容,关闭文件
发现的问题是这个文件的原始内容有可能被删除掉,只剩下待写的内容。
直观的感觉是一个进程读到了一个空文件,然后只写了待写内容到文件中。
这个问题在测试机上出现过,调试中也出现过,但是没找到复现的方法。
但是从代码里看不出任何问题,看man page中的描述,一个文件不可能同时上共享锁和排它锁,所以在truncate的时候,不可能有其他的进程在读这个文件。
还没有找到原因。
linux flock man page: https://linux.die.net/man/2/flock
sub functionXXX {
my (%params) = @_;
my $setValue = 1;
return undef if (!defined($params{KEY}) || !defined($params{FILE}) ||
!defined($params{VALUE}));
my $outputString = "";
# We will not need to take any action even if this command fails
unless (! -f $params{FILE}) {
open(my $readFh, "$params{FILE}") || return;
flock( $readFh, 1 );
my @lines = <$readFh>;
close($readFh);
foreach my $line (@lines) {
chomp($line);
if ($line =~ /\s*$params{KEY}\s*=.*/) {
$outputString .= "$params{KEY} = $params{VALUE}\n";
$setValue = 0;
} else {
$outputString .= "$line\n";
}
}
}
# This is a case where the file already exists but does not have the entry
$outputString .= "$params{KEY} = $params{VALUE}\n" if ($setValue);
if (trim($outputString) eq "") {
$outputString = "$params{KEY} = $params{VALUE}";
}
unless (trim($outputString) eq "") {
open(my $fh, "+>>$params{FILE}") || return;
flock( $fh, 2 );
seek( $fh, 0, 0 );
truncate( $fh, 0 );
print $fh $outputString;
close($fh);
}
}