引子
前两天我们QA发现了一个比较有意思的bug,我细细分析一下,发现多个进程卡死在一个·配置文件上。简单的说,我们为了防止多个进程同时写同一个配置文件,将文件格式破坏,我们用了flock,对于写打开,同时调用flock 系统调用,LOCK_EX方式。当然了由于持有锁,就必须临界区要小,写完之后,尽量释放,持有锁的期间不要有time cost high 的操作,否则,会有其他进程获取不到文件锁,活活饿死。
这个bug比较有意思的地方是,大家都等锁的原因调用那个了一个python脚本,而这个脚本并不需要操作配置文件,仅仅是因为父进程system函数调用python脚本之前,没有关闭文件释放锁,导致python脚本很无辜的持有了这本锁,而python偏偏是个time cost high的操作,这就真是急中风偏偏遇到了慢郎中,外围一群进程焦急地等待这把锁,而python进程却占着毛坑不那啥,呵呵。
我们知道,linux存在强制锁(mandatory lock)和劝告锁(advisory lock)。所谓强制锁,比较好理解,就是你家大门上的那把锁,最要命的是只有一把钥匙,只有一个进程可以操作。所谓劝告锁,本质是一种协议,你访问文件前,先检查锁,这时候锁才其作用,如果你不那么kind,不管三七二十一,就要读写,那么劝告锁没有任何的作用。而遵守协议,读写前先检查锁的那些进程,叫做合作进程。我们代码用的是flock这种劝告锁。
Linux实现了POSIX规定的基于fcntl系统调用文件加锁机制,同时LINUX还支持BSD 变体的flock系统调用实现的劝告锁,当然system V变体的lockf也支持,大家可以自行查找手册。对于fcntl这个系统调用,大家可以阅读Stevens大神的UNIX网络编程卷2进程间通信,讲解的非常好。我的重点是flock。
应用层
flock的应用层接口如下
#include int flock(int fd, int operation);
其中fd是系统调用open返回的文件描述符,operation的选项有:
LOCK_SH :共享锁
LOCK_EX :排他锁或者独占锁
LOCK_UN :解锁。
事实上Linux内核也实现了LOCK_MAND选项,所然manual中没有提到。这种情况我们不讨论。
注意了,flock系统调用实现的FL_FLOCK类型的锁,本质是一种劝告锁,只有多个进程之间遵循要读写,先调锁的协议,才会生效。遵循协议的进程叫合作进程。
下面看一段代码:
#include#include#include #include #include #include #include #includeint main()
{
char buf[128];
time_t ltime;
int fd = open("./tmp.txt",O_RDWR);
if(fd < 0)
{
fprintf(stderr,"open failed %s\n",strerror(errno));
return -1;
}
int ret = flock(fd,LOCK_EX);
if(ret)
{
fprintf(stderr,"flock failed for father\n");
return -2;
}
else
{
time(<ime);
fprintf(