我已经按照linux手册页中"open"的建议实现了一个文件锁定机制,该建议指出:
想要执行原子文件锁定的可移植程序并且需要避免依赖NFS对O_EXCL的支持在同一个文件系统上创建一个唯一的文件(例如,合并主机名和PID),并使用link(2)建立到lockfile的链接。如果Link(2)返回0,表示锁成功。否则,使用stat(2) on检查其链接计数是否增加到2的唯一文件在这种情况下,锁也是成功的。
这似乎工作完美,但是在我的测试中获得100%的代码覆盖率,我需要覆盖链接计数增加到2的情况。
我试着用谷歌搜索,但我似乎能找到的都是上面提到的"做的方式"。
谁能向我解释一下什么情况会导致链接失败(返回-1),但链接计数增加到2?
您的问题的答案在Linux程序员手册链接页的底部提供:
On NFS file systems, the return code may be wrong in case the NFS
server performs the link creation and dies before it can say so. Use
stat(2) to find out if the link got created.
创建另一个文件比什么都麻烦。请创建一个目录,然后检查创建结果。Unix手册指出,只有一个任务可以成功创建目录,如果目录已经存在,则另一个任务将失败,包括两个任务同时尝试创建目录的情况。操作系统自己处理这个问题,所以你不必。
如果不是为了可能的过期锁,这就是您所要做的。然而,事情发生了,程序中止并且不总是移除它们的锁。所以实现可以更精细一些。
在一个脚本中,我经常使用下面的代码。它自动处理过期的锁。您可以在c中实现相同的功能。man -s 2 mkdir
EXECUTION_CONTROL_FILE:是一个名称路径和目录名,类似于/usr/tmp/myappname
second_of_now:以秒为单位返回当前时间(如下所示)
LOCK_MAX_TIME:锁在被认为过期之前可以存在的时间(以秒为单位)
sleep 5:总是假设锁会做一些简短而甜蜜的事情。如果不是,也许你的睡眠周期应该更长。
LockFile() {
L_DIR=${EXECUTION_CONTROL_FILE}.lock
L_DIR2=${EXECUTION_CONTROL_FILE}.lock2
(
L_STATUS=1
L_FILE_COUNT=2
L_COUNT=10
while [ $L_STATUS != 0 ]; do
mkdir $L_DIR 2>/dev/null
L_STATUS=$?
if [ $L_STATUS = 0 ]; then
# Create the timetime stamp file
second_of_now >$L_DIR/timestamp
else
# The directory exists, check how long it has been there
L_NOW=`second_of_now`
L_THEN=`cat $L_DIR/timestamp 2>/dev/null`
# The file does not exist, how many times did this happen?
if [ "$L_THEN" = "" ]; then
if [ $L_FILE_COUNT != 0 ]; then
L_THEN=$L_NOW
L_FILE_COUNT=`expr $L_FILE_COUNT - 1`
else
L_THEN=0
fi
fi
if [ `expr $L_NOW - $L_THEN` -gt $LOCK_MAX_TIME ]; then
# We will try 10 times to unlock, but the 10th time
# we will force the unlock.
UnlockFile $L_COUNT
L_COUNT=`expr $L_COUNT - 1`
else
L_COUNT=10 # Reset this back in case it has gone down
sleep 5
fi
fi
done
)
L_STATUS=$?
return $L_STATUS
}
####
#### Remove access lock
####
UnlockFile() {
U_DIR=${EXECUTION_CONTROL_FILE}.lock
U_DIR2=${EXECUTION_CONTROL_FILE}.lock2
(
# This 'cd' fixes an issue with UNIX which sometimes report this error:
# rm: cannot determine if this is an ancestor of the current working directory
cd `dirname "${EXECUTION_CONTROL_FILE}"`
mkdir $U_DIR2 2>/dev/null
U_STATUS=$?
if [ $U_STATUS != 0 ]; then
if [ "$1" != "0" ]; then
return
fi
fi
trap "rm -rf $U_DIR2" 0
# The directory exists, check how long it has been there
# in case it has just been added again
U_NOW=`second_of_now`
U_THEN=`cat $U_DIR/timestamp 2>/dev/null`
# The file does not exist then we assume it is obsolete
if [ "$U_THEN" = "" ]; then
U_THEN=0
fi
if [ `expr $U_NOW - $U_THEN` -gt $LOCK_MAX_TIME -o "$1" = "mine" ]; then
# Remove lock directory as it is still too old
rm -rf $U_DIR
fi
# Remove this short lock directory
rm -rf $U_DIR2
)
U_STATUS=$?
return $U_STATUS
}
####
second_of_now() {
second_of_day `date "+%y%m%d%H%M%S"`
}
####
#### Return which second of the date/time this is. The parameters must
#### be in the form "yymmddHHMMSS", no centuries for the year and
#### years before 2000 are not supported.
second_of_day() {
year=`printf "$1n"|cut -c1-2`
year=`expr $year + 0`
month=`printf "$1n"|cut -c3-4`
day=`printf "$1n"|cut -c5-6`
day=`expr $day - 1`
hour=`printf "$1n"|cut -c7-8`
min=`printf "$1n"|cut -c9-10`
sec=`printf "$1n"|cut -c11-12`
sec=`expr $min * 60 + $sec`
sec=`expr $hour * 3600 + $sec`
sec=`expr $day * 86400 + $sec`
if [ `expr 20$year % 4` = 0 ]; then
bisex=29
else
bisex=28
fi
mm=1
while [ $mm -lt $month ]; do
case $mm in
4|6|9|11) days=30 ;;
2) days=$bisex ;;
*) days=31 ;;
esac
sec=`expr $days * 86400 + $sec`
mm=`expr $mm + 1`
done
year=`expr $year + 2000`
while [ $year -gt 2000 ]; do
year=`expr $year - 1`
if [ `expr $year % 4` = 0 ]; then
sec=`expr 31622400 + $sec`
else
sec=`expr 31536000 + $sec`
fi
done
printf "$secn"
}
像这样使用:
# Make sure that 2 operations don't happen at the same time
LockFile
# Make sure we get rid of our lock if we exit unexpectedly
trap "UnlockFile mine" 0
.
. Do what you have to do
.
# We need to remove the lock
UnlockFile mine