鉴于我打开了一个文件,有没有办法确定其他进程是不是也打开了该文件

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。

如果你使用它,显然按照你的要求清理它。

【讨论】:

以上是关于鉴于我打开了一个文件,有没有办法确定其他进程是不是也打开了该文件的主要内容,如果未能解决你的问题,请参考以下文章

检查文件是不是打开[重复]

文件被哪个进程锁定?

Java:如何处理两个进程试图修改同一个文件[重复]

节点或shell脚本中有没有办法知道子进程是不是启动了自己的子进程?

确定持有文件锁的线程

修改打开的文件 c++