异想天开

What's the true meaning of light, Could you tell me why

linux文件锁

日期:2015-02-06 17:02:59
  
最后更新日期:2015-04-14 14:50:27
【笔记】
某次查看某python监控脚本的输出日志,发现大量的Errno 11:
[code lang="cpp"]
[Errno 11] Resource temporarily unavailable already run
[/code] 该监控脚本的逻辑,大概为验证接口正确性,本意是验证有没有死锁,若5分钟内连续4次监控请求都超时,那么认为程序死锁了。该脚本在服务程序的本地监控,所以可以排除网络原因的超时,当时也是死锁了几次,所以武断地加了这个监控。该输出是python监控脚本的文件锁部分打出的:
[code lang="cpp"]
from fcntl import LOCK_EX, LOCK_SH, LOCK_NB,flock
class AutoSingle:
def __init__(self,filename):
self._fp=open(filename,"w")
try:
flock(self._fp,LOCK_EX | LOCK_NB)
except Exception,err:
print err,'already run'
sys.exit(0)
#main里面的语句
...
g_auto_file=AutoSingle("/tmp/server.pid")
...
[/code]
自己手动单独执行下该监控脚本还是出错Errno 11。奇怪,这个脚本后面会关闭打开的文件,释放了锁。看了man手册,查找了flock系统调用的用法,用c库测试了例子正常,则系统锁功能正常,当时对此很纳闷。后面使用lsof查找这个文件谁打开了它(这里机器名与程序名字更改了):
[code lang="cpp"]
root@localhost monitor]# lsof -f -- /tmp/server.pid
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
seedserver 23004 root 3w REG 8,1 0 1835019 /tmp/server.pid
[/code]
想到了原因,这个程序由于是监控脚本重启的,故其会继承监控脚本打开的文件。总结:
1.服务端程序继承了父进程打开的文件,囧,原则上服务端程序是要用daemon方式启动的,daemon程序启动时关闭所有打开0,1,2的文件。
2.监控脚本打开文件时,没有使用O_CLOEXEC(close-on-exec)标志位。该标志位的作用是:
[code lang="cpp"]
By default, the new file descriptor is set to remain open across an execve(2) (i.e., the
FD_CLOEXEC file descriptor flag described in fcntl(2) is initially disabled; the Linux-
specific O_CLOEXEC flag, described below, can be used to change this default)
[/code]
文件锁知识:
根据同时拥有锁的用户数量,linux系统对锁分类为排它锁(exclusive lock)和共享锁(shared lock)。假设某文件被一个进程锁住了,其它进程对该文件的访问会怎么样?根据这种情况,又可以分为:
[code lang="cpp"]
强制锁(Mandatory locking),内核会拦截该请求
To make use of mandatory locks, mandatory locking must be enabled both on the file system
that contains the file to be locked, and on the file itself. Mandatory locking is
enabled on a file system using the "-o mand" option to mount(8), or the MS_MANDLOCK flag
for mount(2). Mandatory locking is enabled on a file by disabling group execute permis-
sion on the file and enabling the set-group-ID permission bit (see chmod(1) and
chmod(2))
劝告锁(Mandatory locking),内核不会拦截,仅仅返回报错
[/code]
看了man手册,还有个问题需要验证,就是flock和fcntl在linux系统是使用文件锁的接口,前提父进程创建文件锁,同时创建时没有使用O_CLOEXEC标识位,子进程也不会关闭父进程打开的文件,对于子进程是否会继承父进程的文件锁么?
实验得知:
1.flock创建的文件锁,bug的原因正在于此。
2.fcntl创建的文件锁,子进程会继承文件,但不会继承文件锁
修改版本的文件锁,设置了O_CLOEXEC,fork出来的进程不会继承之前打开的文件。
[code lang="cpp"]
from fcntl import LOCK_EX, LOCK_SH, LOCK_NB,flock
class AutoSingle:
def __init__(self,filename):
self._fp=open(filename,"w")
flag= fcntl.fcntl(self._fp.fileno(),fcntl.F_GETFD )
fcntl.fcntl(self._fp.fileno(),fcntl.F_SETFD,flag|fcntl.FD_CLOEXEC)
try:
flock(self._fp,LOCK_EX | LOCK_NB)
except Exception,err:
print err,'already run'
sys.exit(0)
[/code]

备注:语法就是糖,这次用php实现了这个逻辑:
[code lang="cpp"]
class single{
var $_file;
var $_fp;
function single($file){
$this->_file = $file;
$this->_fp = fopen($this->_file , 'w');
if(flock($this->_fp , LOCK_EX | LOCK_NB)){
echo "Lock file \n";
} else{
echo "Lock file failed...\n";
exit();
}
}

function __destruct(){
flock($this->_fp,LOCK_UN);
// ...
fclose($this->_fp);
}
}
[/code]
思考:
文件锁可以做进程同步,那么某个进程挂了文件锁会不会释放?