鉴于我打开了一个文件,有没有办法确定其他进程是不是也打开了该文件
Posted
技术标签:
【中文标题】鉴于我打开了一个文件,有没有办法确定其他进程是不是也打开了该文件【英文标题】:Given that I've got a file open, is there a way to determine if other processes also has the file open鉴于我打开了一个文件,有没有办法确定其他进程是否也打开了该文件 【发布时间】:2020-01-07 22:57:40 【问题描述】:我有一个文件描述符(使用 open(2) 获得)。在某个时间点,我可以确定该文件已被取消链接(fstat(2),检查 st_nlinks)。但是,在我可以关闭我的文件描述符之前,我想验证没有其他进程仍然打开文件(至少用于写入)。
inotify 可以(并且确实)为我提供了一些不错的事件,例如,当 st_nlinks 更改时 IN_ATTRIB 和当另一个进程关闭文件时 IN_CLOSE_WRITE (并打开它以供写入,但这并不意味着没有人仍然有它可以写作)。
基本上我正在跟踪一个日志文件,并且需要确保我获得了所有已写入的信息,即使在重命名之后也是如此,但是一旦文件被重命名和/或删除,和 所有作者都已关闭,我没有必要让我的文件描述符保持打开状态。
(避免比赛 w.r.t. 打开文件并确保 inotify 描述符引用同一文件超出了此问题的范围。)
【问题讨论】:
据我所知,这不是一个简单的方法......当然,你总是可以走 /proc/pid/fds ...... 您是否探索过flock
,如果这是您平台上的一个选项?请注意,这需要所有各方都承诺使用它,但它是一个内置选项。替代方案是高度非标准且特定于操作系统的。
如果 linux 解决方案可以接受,请查看fcntl(fd, F_SETLEASE)
,如果有人打开了文件,它将失败。
了解lsof
的工作原理?
在任何时间点在系统上使用 >100k 打开文件描述符遍历 /proc/*/fd/* 是不可行的。另外,如果文件被重命名,这也会变得不准确...... F_SETLEASE 看起来很有趣。因此,如果我理解正确,我可以取出读取租约,并在另一个进程打开文件进行写入或截断文件时收到通知?关闭文件怎么样?我不在乎打开,我想知道最后一个 writer 何时关闭文件。 lsof 遍历 /proc/*/fd/*.
【参考方案1】:
并非万无一失,但如果您可以使用 inotify 跟踪重命名应该“足够好”,显然有一些竞争条件(根据处理任何 inotify 的标准)。对此进行改进需要在 /proc 引用的每个未删除文件上使用 stat() ,并且在我看来并不能有效地改进解决方案。
首先,步骤,给定一个 pid 和 fd(作为字符串,因为这是我们从 readdir(3) 获取它们的方式,获取它打开的模式:
static
int getflags(const char* pidn, const char* fdn)
char fnamebfr[525];
char bfr[1 << 13];
sprintf(fnamebfr, "/proc/%s/fdinfo/%s", pidn, fdn);
int fd = open(fnamebfr, O_RDONLY);
if (fd < 0)
if (errno != ENOENT)
perror(fnamebfr);
return 0;
int r = read(fd, bfr, sizeof(bfr));
if (r < 0)
perror(fnamebfr);
r = 0;
else if (r == sizeof(bfr))
r = 0;
fprintf(stderr, "bfr in %s is too small.\n", __FUNCTION__);
else
bfr[r] = 0;
r = 0;
char *fb = strstr(bfr, flagsstr);
if (!fb)
fprintf(stderr, "Error locating '%s' in %s, content:\n%s\n", flagsstr, fnamebfr, bfr);
else
char *nptr;
fb += strlen(flagsstr);
r = strtol(fb, &nptr, 8);
if (*nptr != '\n')
fprintf(stderr, "Parse warning for strtol, endp=\n%s\nbfr=%s\n", nptr, bfr);
close(fd);
return r;
注意:如果文件在运行 /proc//fd/ 之间关闭,我们将在此处返回 0 (O_RDONLY),如果文件描述符被重新使用...那么我们将完全返回错误的标志。
现在我们可以遍历 /proc/ 在这些标志中查找 O_WRONLY 或 O_RDWR:
static int fileopenforwrite(const char* path)
size_t tlen = strlen(path);
DIR *proc, *fd;
struct dirent *procent, *fdent;
int _fd;
proc = opendir("/proc");
if (!proc)
perror("/proc");
return -1;
while ((procent = readdir(proc)))
char *endptr;
char fdpath[MAXNAMLEN + 10];
strtol(procent->d_name, &endptr, 10);
if (*endptr)
continue; /* not a pid */
sprintf(fdpath, "/proc/%s/fd", procent->d_name);
_fd = open(fdpath, O_RDONLY);
if (_fd < 0)
if (errno != ENOENT) /* process terminated + waited */
perror(fdpath);
fd = fdopendir(_fd);
if (!fd)
perror(fdpath);
close(_fd);
continue;
while ((fdent = readdir(fd)))
if (fdent->d_type == DT_DIR)
continue; /* skip . and .. */
char readbuf[MAXNAMLEN + 11];
ssize_t r = readlinkat(_fd, fdent->d_name, readbuf, sizeof(readbuf));
if (r < 0)
perror(fdpath);
else if (r >= (int)sizeof(readbuf))
fprintf(stderr, "Bufferoverflow in %s (%s).", __FUNCTION__, __FILE__);
else
readbuf[r] = 0;
if (strncmp(path, readbuf, tlen) == 0 &&
(!readbuf[tlen] || strcmp(" (deleted)", readbuf + tlen) == 0))
/* We have an FD to the file, now we want to know if it's
* open for writing */
int f = getflags(procent->d_name, fdent->d_name);
if (f & (O_RDONLY | O_RDWR))
closedir(fd);
closedir(proc);
return 0;
closedir(fd); /* implicitly closes _fd */
closedir(proc);
return 1;
这将返回 0 表示是,1 表示错误,-1 表示无法打开 /proc。
如果你使用它,显然按照你的要求清理它。
【讨论】:
以上是关于鉴于我打开了一个文件,有没有办法确定其他进程是不是也打开了该文件的主要内容,如果未能解决你的问题,请参考以下文章