c语言多进程flock,从flock引发的一个bug谈起(1) 进程的文件描述符

本文介绍了在c语言中使用flock进行多进程文件锁时遇到的一个bug,该bug导致多个进程因等待锁而卡死。文章详细解释了flock的用法,包括其作为劝告锁的性质,以及在父子进程间如何传递和持有文件锁。通过一个具体的代码示例,展示了当父进程未关闭文件并释放锁就调用Python脚本时,导致子进程意外持有锁并引起问题的情况。此外,还探讨了Linux内核中关于文件描述符和文件锁的管理机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引子

前两天我们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(&ltime);

fprintf(

### C语言 `flock` 函数的用法与示例 #### 什么是 `flock` 函数? `flock` 是 Linux 系统中的一个文件锁定函数,用于实现进程间的文件访问控制。它可以防止多个进程同时修改同一个文件的内容,从而避免数据竞争和不一致的情况发生。`flock` 可以设置独占锁(exclusive lock)或共享锁(shared lock),并支持非阻塞模式。 #### 函数原型 ```c #include <sys/file.h> int flock(int fd, int operation); ``` - **参数说明**: - `fd`: 文件描述符,由 `open()` 或 `creat()` 返回。 - `operation`: 锁定操作类型,常见的值有: - `LOCK_SH`: 设置共享锁(允许其他进程读取文件)。 - `LOCK_EX`: 设置独占锁(阻止其他进程访问文件)。 - `LOCK_UN`: 解除已有的锁。 - `LOCK_NB`: 非阻塞模式标志,通常与其他标志组合使用。 #### 使用示例 以下是一个完整的例子,展示了如何使用 `flock` 对文件加锁、解锁的过程: ```c #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int fd = open("example.txt", O_RDWR | O_CREAT, 0666); // 打开/创建文件 if (fd == -1) { perror("open"); return EXIT_FAILURE; } // 尝试获取独占锁 if (flock(fd, LOCK_EX) == -1) { // 如果失败则报错退出 perror("flock"); close(fd); return EXIT_FAILURE; } printf("Lock acquired on file.\n"); // 写入一些数据到文件 const char *msg = "This is a test message.\n"; ssize_t bytes_written = write(fd, msg, strlen(msg)); if (bytes_written == -1) { perror("write"); flock(fd, LOCK_UN); // 解锁 close(fd); return EXIT_FAILURE; } // 解锁 if (flock(fd, LOCK_UN) == -1) { perror("unlock failed"); } else { printf("File unlocked successfully.\n"); } close(fd); // 关闭文件描述符也会自动释放锁 return EXIT_SUCCESS; } ``` 此代码片段实现了以下几个功能: 1. 打开或创建名为 `example.txt` 的文件。 2. 获取对该文件的独占锁 (`LOCK_EX`)。 3. 向文件中写入一段字符串。 4. 解除锁 (`LOCK_UN`)。 5. 关闭文件描述符,此时即使未显式解除锁,系统也会自动释放锁[^3]。 #### 特殊情况处理 当尝试对已被锁定的文件再次加锁时,如果没有使用 `LOCK_NB` 参数,当前线程会被挂起直至锁可用为止。如果希望立即返回而不是等待,则应结合 `LOCK_NB` 使用。例如: ```c if (flock(fd, LOCK_EX | LOCK_NB) == -1 && errno == EWOULDBLOCK) { fprintf(stderr, "File already locked by another process.\n"); close(fd); return EXIT_FAILURE; } ``` 以上逻辑会在检测到目标文件正被占用时打印提示信息并终止程序运行。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值