我已经实现了一个文件锁定机制,沿着linux手册页中“open”的建议,其中说明:
想要使用a执行原子文件锁定的可移植程序 lockfile,并且需要避免依赖NFS支持O_EXCL,可以 在同一文件系统上创建一个唯一的文件(例如,合并 hostname和PID),并使用link(2)建立到lockfile的链接。如果 link(2)返回0,锁定成功。否则,请使用stat(2) 用于检查其链接数是否已增加到2的唯一文件 这种情况下锁定也是成功的。
这看起来效果很好,但是为了在测试中获得100%的代码覆盖率,我需要覆盖链接数增加到2的情况。
我已经尝试使用谷歌搜索了,但我似乎能找到的所有内容都是上面反复出现的“完成它的方式”。
有人可以向我解释一下哪种情况会导致链接失败(返回-1),但链接数会增加到2?
答案 0 :(得分:2)
您的问题的答案在Linux程序员手册的链接(2)页面的底部提供:
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.
答案 1 :(得分:1)
创建另一个文件比任何事情都麻烦。改为创建一个目录并检查创建的结果。 Unix手册指出只有一个任务可以成功创建一个目录,另一个任务会在目录已经存在时失败,包括2个任务同时尝试它的情况。操作系统本身可以解决问题,因此您无需这样做。
如果不是因为陈旧的锁,那就是你所要做的。然而,事情发生了,程序中止,并不总是删除他们的锁。所以实现可以更精细一些。
在脚本中我经常使用下面的代码。它自动处理陈旧的锁。您可以在C中实现相同的功能。检查手册页:
man -s 2 mkdir
EXECUTION_CONTROL_FILE:是名称PATH和Dir名称,类似于/ usr / tmp / myAppName
second_of_now:以秒为单位返回当前时间(包含在下面)
LOCK_MAX_TIME:锁定在被认为陈旧之前可以存在多长时间
睡眠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 "$1\n"|cut -c1-2`
year=`expr $year + 0`
month=`printf "$1\n"|cut -c3-4`
day=`printf "$1\n"|cut -c5-6`
day=`expr $day - 1`
hour=`printf "$1\n"|cut -c7-8`
min=`printf "$1\n"|cut -c9-10`
sec=`printf "$1\n"|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 "$sec\n"
}
像这样使用:
# 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