Linux环境C语言如何不打开文件检测是否被其他进程占用
Linux下无打开操作检测文件占用状态的实现方案
Linux默认没有提供专门的系统调用,用来在不打开目标文件的前提下检测文件是否被其他进程以读/写模式占用,但可以通过内核暴露的procfs接口实现完全无侵入的检测,结合文件锁机制可以直接满足互斥打开的需求。
核心原理
- Linux内核默认允许多个进程同时以读写模式打开同一个文件,原有代码里两个进程同时执行
fopen("distributed.txt", "r+")都会拿到有效文件指针,不会出现返回NULL的情况,这是VFS层的默认设计。 - 全程不打开目标文件的检测逻辑可以完全基于procfs实现:
- 第一步调用
stat()读取目标文件的元数据,获取文件所在设备号、inode号,这个操作不会触发文件打开动作,仅读取文件系统的元数据信息。 - 两种检测路径可选:
- 检测文件是否被加锁:直接读取
/proc/locks文件,遍历所有内核记录的锁条目,匹配对应设备号和inode号即可判断是否有进程持有该文件的锁,全程不需要触碰目标文件。 - 检测任意进程的打开动作:遍历
/proc下所有进程ID命名的目录,读取每个进程的/proc/<pid>/fd下的文件描述符符号链接,对比链接目标的设备号、inode号和目标文件是否一致,还可以读取/proc/<pid>/fdinfo/<fd>的flag字段,判断该文件描述符的打开模式是读、写还是读写。这个方法同样不需要打开目标文件,仅需要对应进程fd目录的读取权限(普通用户只能读取自身进程的fd信息,root可读取所有进程的)。
- 检测文件是否被加锁:直接读取
- 第一步调用
需求适配实现
要实现「第一个进程持有文件时,第二个进程不尝试打开文件直接返回NULL,等文件释放后再拿到有效指针」的效果,最稳定的方案是劝告式排他锁+procfs锁检测,比遍历所有进程fd的性能开销低很多,也不存在轮询的资源浪费。
修正后的可运行代码如下:
#include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <sys/stat.h> #include <string.h> #include <sys/sysmacros.h> // 给文件加整个文件范围的排他写锁 int lock_file(int fd) { struct flock fl; memset(&fl, 0, sizeof(fl)); fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; return fcntl(fd, F_SETLK, &fl); } // 不打开目标文件,直接通过/proc/locks检测是否有进程持有文件锁 int is_file_locked(const char *filepath) { struct stat st; if (stat(filepath, &st) == -1) { return -1; } FILE *locks_fp = fopen("/proc/locks", "r"); if (!locks_fp) return -1; char line[256]; while (fgets(line, sizeof(line), locks_fp)) { long long inode; unsigned int major, minor; if (sscanf(line, "%*d %*s %*c %*d %x:%x:%lld", &major, &minor, &inode) == 3) { if (major == major(st.st_dev) && minor == minor(st.st_dev) && inode == st.st_ino) { fclose(locks_fp); return 1; } } } fclose(locks_fp); return 0; } int main() { FILE *demo; const char *filepath = "distributed.txt"; if (getenv("FIRST_TIME")) { demo = fopen(filepath, "r+"); if (!demo) { perror("first process fopen failed"); return 1; } if (lock_file(fileno(demo)) == -1) { perror("first process lock failed"); fclose(demo); return 1; } printf("first process hold file for 15s\n"); sleep(15); fclose(demo); } else { // 不打开文件,先检测锁状态 if (is_file_locked(filepath)) { printf("file is occupied by other process, demo is NULL\n"); demo = NULL; } else { demo = fopen(filepath, "r+"); // 二次加锁检查避免竞争条件 if (demo && lock_file(fileno(demo)) == -1) { fclose(demo); demo = NULL; printf("file lock race, demo is NULL\n"); } else { printf("demo is %p \n", demo); fclose(demo); } } } return 0; }
注意事项
- 上述检测逻辑全程没有执行目标文件的open/fopen操作,完全符合不主动尝试打开文件的要求。
- 劝告锁需要所有访问该文件的进程都主动遵守加锁规则才能生效,如果需要内核强制拦截未持锁进程的打开操作,可以给文件设置强制锁属性,对应分区挂载时开启
mand参数即可。 - 如果需要等待文件释放而不是直接返回NULL,可以搭配inotify机制监听目标文件的关闭事件,不需要轮询检测锁状态。
内容的提问来源于stack exchange,提问作者Gourish Singla
相关产品推荐
相关产品推荐

