进程持有flock文件排他锁时其他进程写入行为解析
flock锁行为说明与代码问题排查
核心机制结论
- 默认配置下
flock实现的是建议性文件锁,内核不会主动拦截未调用flock的进程的读写操作。也就是说,某进程持有排他锁时,其他进程直接调用write写入文件会直接执行成功,既不会阻塞也不会返回失败——锁的互斥逻辑只对主动调用flock申请锁的进程生效。 flock默认工作在阻塞模式:调用flock(fd, LOCK_EX)时如果锁已被其他进程持有,调用会永久阻塞直到锁被释放,不会立刻返回错误。如果需要拿不到锁直接返回失败,需要传入LOCK_EX | LOCK_NB参数,此时拿锁失败会返回-1,置错误码为EWOULDBLOCK。- 如果需要强制拦截未持锁进程的读写(即强制锁),需要同时满足两个条件:文件所在文件系统挂载时指定
mand参数开启强制锁支持、目标文件设置setgid位并关闭组执行权限(执行chmod g+s,g-x 文件名设置)。生产环境不推荐使用强制锁,语义隐患较多。
代码存在的问题
观察到的两个异常现象,基本都是代码实现不严谨导致的:
- 缺失必要头文件
贴出的代码没有包含任何系统调用/库函数对应的头文件,编译时会产生大量隐式声明警告,直接运行会出现各类不可预期的行为:open、O_RDWR宏定义需要引入<fcntl.h>,缺失的话编译器无法识别O_RDWR宏,会默认将其作为值为0的普通变量处理,对应只读打开标志O_RDONLY,此时write调用会因权限不足失败,这就是写入不生效的直接原因。flock需要引入<sys/file.h>,write/close/sleep需要引入<unistd.h>,printf/perror需要引入<stdio.h>,缺失这些头文件会导致系统调用参数传递、返回值解析异常,很容易出现加锁状态判断错误。
- 未校验关键系统调用的返回值
代码没有判断open的返回值,如果文件不存在、权限不足,open会返回-1,后续把无效fd传给flock/write会导致所有调用全部异常。另外测试第二个进程加锁时,如果没有加LOCK_NB参数,flock会阻塞在调用处不会返回,很容易被误判为加锁成功。 - 输出逻辑不严谨
write.c中printf("%d",n)没有加换行符,而stdout默认是行缓冲模式,没有换行的输出可能不会立刻刷新到终端,容易误判返回值。
修正后可运行的测试代码
lock.c(持锁进程)
#include <fcntl.h> #include <sys/file.h> #include <unistd.h> #include <stdio.h> int main() { int fd = open("okas", O_RDWR); if (fd == -1) { perror("open file failed"); return 1; } // 非阻塞模式申请排他锁 int ret = flock(fd, LOCK_EX | LOCK_NB); if (ret == -1) { perror("get exclusive lock failed"); close(fd); return 1; } printf("pid %d hold exclusive lock, running...\n", getpid()); while (1) { sleep(1); } // 演示释放锁逻辑,实际死循环不会走到 flock(fd, LOCK_UN); close(fd); return 0; }
write.c(写入测试进程)
#include <fcntl.h> #include <unistd.h> #include <stdio.h> int main() { int fd = open("okas", O_RDWR); if (fd == -1) { perror("open file failed"); return 1; } // 如果要测试锁对写的互斥,可在这里加入flock加锁逻辑 int ret = write(fd, "1", 1); if (ret == -1) { perror("write failed"); close(fd); return 1; } printf("write %d byte success\n", ret); close(fd); return 0; }
测试步骤
- 编译两个程序:
gcc lock.c -o lock && gcc write.c -o write - 提前创建测试文件:
touch okas - 第一个终端运行
./lock,看到持锁成功的输出后,打开第二个终端再次运行./lock,会立刻返回Resource temporarily unavailable的错误,符合排他锁互斥预期。 - 第二个终端运行
./write,会直接提示写入成功,查看文件会发现第一个字节被改为1,这就是建议性锁不强制拦截IO的正常表现。
内容的提问来源于stack exchange,提问作者Rowooz
相关产品推荐
相关产品推荐

