如果指向文件被移动或删除,Linux上的打开文件句柄会发生什么

发布于 2025-02-05 11:43:19 字数 331 浏览 2 评论 0原文

同时,如果尖头文件获取:

  • 移开 - >文件处理是否有效?
  • 删除 - >这是否导致EBADF,表明文件句柄无效?
  • 替换为新文件 - >文件句柄是否指向此新文件?
  • 用硬链接替换为新文件 - >我的文件是否处理“关注”此链接?
  • 替换为新文件的软链接 - >我的文件处理现在是否点击此软链接文件?

为什么要问这样的问题:我正在使用热插入的硬件(例如USB设备等)。可能会发生,设备(以及其 /dev /file)被用户或其他Gremlin重新分配。

解决这个问题的最佳实践是什么?

What happens to an open file handle on Linux if the pointed file meanwhile gets:

  • Moved away -> Does the file handle stay valid?
  • Deleted -> Does this lead to an EBADF, indicating an invalid file handle?
  • Replaced by a new file -> Does the file handle pointing to this new file?
  • Replaced by a hard link to a new file -> Does my file handle "follow" this link?
  • Replaced by a soft link to a new file -> Does my file handle hit this soft link file now?

Why I'm asking such questions: I'm using hot-plugged hardware (such as USB devices etc.). It can happen, that the device (and also its /dev/file) gets reattached by the user or another Gremlin.

What's the best practice dealing with this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

蓝海似她心 2025-02-12 11:43:20

如果将文件移动(在同一文件系统中)或重命名,则文件句柄保持打开状态,并且仍然可以用于读取和编写文件。

如果删除文件,则文件句柄保持打开状态并仍然可以使用(这不是某些人期望的)。在关闭最后一个句柄之前,该文件不会真正删除。

如果文件被新文件替换,则完全取决于如何。如果文件的内容被覆盖,则文件句柄仍然有效并访问新内容。如果现有文件未链接,并且使用Rename()将新文件移至现有文件中,则与删除相同(请参见上文) - IS,文件句柄将继续参考文件的原始版本。

通常,一旦文件打开,文件就打开了,没有任何更改目录结构可以更改它 - 他们可以移动,重命名文件或将其他内容放在其位置,它就可以保持打开状态。

在Unix中没有删除,只有lunink(),这很有意义,因为它不一定会删除文件 - 只需从目录中删除链接即可。


另一方面,基础设备消失(例如USB拔出),则文件句柄将不再有效,并且很可能会在任何操作时给出IO/错误。不过,您仍然必须关闭它。即使将设备重新插入,这也将是正确的,因为在这种情况下保持文件打开是不明智的。

If the file is moved (in the same filesystem) or renamed, then the file handle remains open and can still be used to read and write the file.

If the file is deleted, the file handle remains open and can still be used (This is not what some people expect). The file will not really be deleted until the last handle is closed.

If the file is replaced by a new file, it depends exactly how. If the file's contents are overwritten, the file handle will still be valid and access the new content. If the existing file is unlinked and a new one created with the same name or, if a new file is moved onto the existing file using rename(), it's the same as deletion (see above) - that is, the file handle will continue to refer to the original version of the file.

In general, once the file is open, the file is open, and nobody changing the directory structure can change that - they can move, rename the file, or put something else in its place, it simply remains open.

In Unix there is no delete, only unlink(), which makes sense as it doesn't necessarily delete the file - just removes the link from the directory.


If on the other hand the underlying device disappears (e.g. USB unplug) then the file handle won't be valid any more and is likely to give IO/error on any operation. You still have to close it though. This is going to be true even if the device is plugged back in, as it's not sensible to keep a file open in this case.

深爱成瘾 2025-02-12 11:43:20

文件处理指向Inode不通向路径,因此您的大多数场景仍然按照您的想象工作,因为该句柄仍指向文件。

具体而言,使用删除方案 - 由于某种原因,该函数称为“ unlink”,它在文件名(担型)和文件之间摧毁了“链接”。当您打开文件,然后取消链接时,该文件实际上仍然存在,直到其参考计数为零,这是您关闭句柄的时候。

编辑:在硬件的情况下,您已经打开了特定设备节点的句柄,如果拔下设备,即使设备返回,内核也会使所有访问权限失败。您将必须关闭设备并重新打开它。

File handles point to an inode not to a path, so most of your scenarios still work as you assume, since the handle still points to the file.

Specifically, with the delete scenario - the function is called "unlink" for a reason, it destroys a "link" between a filename (a dentry) and a file. When you open a file, then unlink it, the file actually still exists until its reference count goes to zero, which is when you close the handle.

Edit: In the case of hardware, you have opened a handle to a specific device node, if you unplug the device, the kernel will fail all accesses to it, even if the device comes back. You will have to close the device and reopen it.

梦魇绽荼蘼 2025-02-12 11:43:20

我不确定其他操作,但是至于删除:删除根本不会发生(实际上,即在文件系统中),直到关闭文件的最后一个打开句柄为止。因此,不应将文件从您的应用程序中删除。

通过创建,打开和立即删除文件,几个应用程序(不在乎)依赖于此行为,然后将其持续到应用程序的时间与应用程序完全持续 - 允许其他应用程序意识到第一个应用程序的生命周期无需查看过程地图等。

其他内容可能适用于类似的考虑。

I'm not sure about the other operations, but as for deletion: Deletion simply doesn't take place (physically, i.e. in the file system) until the last open handle to the file is closed. Thus it should not be possible to delete a file out from under your application.

A few apps (that don't come to mind) rely on this behavior, by creating, opening and immediately deleting files, which then live exactly as long as the application - allowing other applications to be aware of the first app's lifecycle without needing to look at process maps and such.

It's possible similar considerations apply to the other stuff.

风向决定发型 2025-02-12 11:43:20

如果要检查文件处理程序(文件描述符)是否还可以,则可以调用此功能。

/**
 * version : 1.1
 *    date : 2015-02-05
 *    func : check if the fileDescriptor is fine.
 */

#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>

/**
 * On success, zero is returned.  On error, -1  is  returned,  and  errno  is  set
 *      appropriately.
 */
int check_fd_fine(int fd) {
    struct stat _stat;
    int ret = -1;
    if(!fcntl(fd, F_GETFL)) {
        if(!fstat(fd, &_stat)) {
            if(_stat.st_nlink >= 1)
                ret = 0;
            else
                printf("File was deleted!\n");
        }
    }
    if(errno != 0)
        perror("check_fd_fine");
    return ret;
}

int main() {
    int fd = -1;
    fd = open("/dev/ttyUSB1", O_RDONLY);
    if(fd < 0) {
        perror("open file fail");
        return -1;
    }
    // close or remove file(remove usb device)
//  close(fd);
    sleep(5);
    if(!check_fd_fine(fd)) {
        printf("fd okay!\n");
    } else {
        printf("fd bad!\n");
    }
    close(fd);
    return 0;
}

if you want to check if the file handler(file descriptor) is okay, you can call this function.

/**
 * version : 1.1
 *    date : 2015-02-05
 *    func : check if the fileDescriptor is fine.
 */

#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>

/**
 * On success, zero is returned.  On error, -1  is  returned,  and  errno  is  set
 *      appropriately.
 */
int check_fd_fine(int fd) {
    struct stat _stat;
    int ret = -1;
    if(!fcntl(fd, F_GETFL)) {
        if(!fstat(fd, &_stat)) {
            if(_stat.st_nlink >= 1)
                ret = 0;
            else
                printf("File was deleted!\n");
        }
    }
    if(errno != 0)
        perror("check_fd_fine");
    return ret;
}

int main() {
    int fd = -1;
    fd = open("/dev/ttyUSB1", O_RDONLY);
    if(fd < 0) {
        perror("open file fail");
        return -1;
    }
    // close or remove file(remove usb device)
//  close(fd);
    sleep(5);
    if(!check_fd_fine(fd)) {
        printf("fd okay!\n");
    } else {
        printf("fd bad!\n");
    }
    close(fd);
    return 0;
}
∝单色的世界 2025-02-12 11:43:20

已删除文件的内存信息(您给出的所有示例是已删除文件的实例)以及inodes ondisk持续存在,直到关闭文件为止。

热插入的硬件是一个完全不同的问题,如果磁盘indodes或metadata完全改变了,您不应该期望您的程序长期保持生命。

The in-memory information of a deleted file (all the examples you give are instances of a deleted file) as well as the inodes on-disk remain in existence until the file is closed.

Hardware being hotplugged is a completely different issue, and you should not expect your program to stay alive long if the on-disk inodes or metadata have changed at all.

七七 2025-02-12 11:43:20

以下实验表明, Markr的答案是正确的。

Code.C:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <stdio.h>

void perror_and_exit() {
  perror(NULL);
  exit(1);
}

int main(int argc, char *argv[]) {
  int fd;
  if ((fd = open("data", O_RDONLY)) == -1) {
    perror_and_exit();
  }
  char buf[5];
  for (int i = 0; i < 5; i++) {
    bzero(buf, 5);
    if (read(fd, buf, 5) != 5) {
      perror_and_exit();
    }
    printf("line: %s", buf);
    sleep(20);
  }
  if (close(fd) != 0) {
    perror_and_exit();
  }
  return 0;
}

数据:

1234
1234
1234
1234
1234

使用GCC Code.c生产A.Out。运行./ A.Out。当您看到以下输出时:

line: 1234

使用RM数据删除数据。但是./ A.Out将继续运行而没有错误并产生以下整个输出:

line: 1234
line: 1234
line: 1234
line: 1234
line: 1234

我已经在Ubuntu 16.04.3上完成了实验。

The following experiment shows that MarkR's answer is correct.

code.c:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <stdio.h>

void perror_and_exit() {
  perror(NULL);
  exit(1);
}

int main(int argc, char *argv[]) {
  int fd;
  if ((fd = open("data", O_RDONLY)) == -1) {
    perror_and_exit();
  }
  char buf[5];
  for (int i = 0; i < 5; i++) {
    bzero(buf, 5);
    if (read(fd, buf, 5) != 5) {
      perror_and_exit();
    }
    printf("line: %s", buf);
    sleep(20);
  }
  if (close(fd) != 0) {
    perror_and_exit();
  }
  return 0;
}

data:

1234
1234
1234
1234
1234

Use gcc code.c to produce a.out. Run ./a.out. When you see the following output:

line: 1234

Use rm data to delete data. But ./a.out will continue to run without errors and produce the following whole output:

line: 1234
line: 1234
line: 1234
line: 1234
line: 1234

I have done the experiment on Ubuntu 16.04.3.

送君千里 2025-02-12 11:43:20

在 / PROC / DIRECTORY下,您将找到当前活动的每个过程的列表,只需找到您的PID,所有有关的数据就在那里。相互构造的信息是文件夹fd/,您会发现该过程当前打开的所有文件处理程序。

最终,您会找到与设备的符号链接(下/dev/否则/proc/bus/usb/),如果设备挂起链接将死亡,并且不可能刷新此手柄,则该过程必须关闭,并且再次打开(即使使用重新连接)

此代码可以读取您的PID链接当前状态

#include <unistd.h>
#include <stdio.h>
#include <dirent.h>

int main() {
    // the directory we are going to open
    DIR           *d;

    // max length of strings
    int maxpathlength=256;

    // the buffer for the full path
    char path[maxpathlength];

    // /proc/PID/fs contains the list of the open file descriptors among the respective filenames
    sprintf(path,"/proc/%i/fd/",getpid() );

    printf("List of %s:\n",path);

    struct dirent *dir;
    d = opendir(path);
    if (d) {
        //loop for each file inside d
        while ((dir = readdir(d)) != NULL) {

            //let's check if it is a symbolic link
            if (dir->d_type == DT_LNK) {

                const int maxlength = 256;

                //string returned by readlink()
                char hardfile[maxlength];

                //string length returned by readlink()
                int len;

                //tempath will contain the current filename among the fullpath
                char tempath[maxlength];

                sprintf(tempath,"%s%s",path,dir->d_name);
                if ((len=readlink(tempath,hardfile,maxlength-1))!=-1) {
                    hardfile[len]='\0';
                        printf("%s -> %s\n", dir->d_name,hardfile);

                } else
                    printf("error when executing readlink() on %s\n",tempath);

            }
        }

        closedir(d);
    }
    return 0;
}

此最终代码很简单,您可以使用链接功能。

int
open_dir(char * path)
{
  int fd;

  path = strdup(path);
  *strrchr(path, '/') = '\0';
  fd = open(path, O_RDONLY | O_DIRECTORY);
  free(path);

  return fd;
}

int
main(int argc, char * argv[])
{
  int odir, ndir;
  char * ofile, * nfile;
  int status;

  if (argc != 3)
    return 1;

  odir = open_dir(argv[1]);
  ofile = strrchr(argv[1], '/') + 1;

  ndir = open_dir(argv[2]);
  nfile = strrchr(argv[2], '/') + 1;

  status = linkat(odir, ofile, ndir, nfile, AT_SYMLINK_FOLLOW);
if (status) {
  perror("linkat failed");
}


  return 0;
}

Under /proc/ directory you will find a list of every process currently active, just find your PID and all data regarding is there. An interresting info is the folder fd/, you will find all file handlers currently opened by the process.

Eventually you will find a symbolic link to your device (under /dev/ or even /proc/bus/usb/), if the device hangs the link will be dead and it will be impossible to refresh this handle, the process must close and open it again (even with reconnection)

This code can read your PID's link current status

#include <unistd.h>
#include <stdio.h>
#include <dirent.h>

int main() {
    // the directory we are going to open
    DIR           *d;

    // max length of strings
    int maxpathlength=256;

    // the buffer for the full path
    char path[maxpathlength];

    // /proc/PID/fs contains the list of the open file descriptors among the respective filenames
    sprintf(path,"/proc/%i/fd/",getpid() );

    printf("List of %s:\n",path);

    struct dirent *dir;
    d = opendir(path);
    if (d) {
        //loop for each file inside d
        while ((dir = readdir(d)) != NULL) {

            //let's check if it is a symbolic link
            if (dir->d_type == DT_LNK) {

                const int maxlength = 256;

                //string returned by readlink()
                char hardfile[maxlength];

                //string length returned by readlink()
                int len;

                //tempath will contain the current filename among the fullpath
                char tempath[maxlength];

                sprintf(tempath,"%s%s",path,dir->d_name);
                if ((len=readlink(tempath,hardfile,maxlength-1))!=-1) {
                    hardfile[len]='\0';
                        printf("%s -> %s\n", dir->d_name,hardfile);

                } else
                    printf("error when executing readlink() on %s\n",tempath);

            }
        }

        closedir(d);
    }
    return 0;
}

This final code is simple, you can play with linkat function.

int
open_dir(char * path)
{
  int fd;

  path = strdup(path);
  *strrchr(path, '/') = '\0';
  fd = open(path, O_RDONLY | O_DIRECTORY);
  free(path);

  return fd;
}

int
main(int argc, char * argv[])
{
  int odir, ndir;
  char * ofile, * nfile;
  int status;

  if (argc != 3)
    return 1;

  odir = open_dir(argv[1]);
  ofile = strrchr(argv[1], '/') + 1;

  ndir = open_dir(argv[2]);
  nfile = strrchr(argv[2], '/') + 1;

  status = linkat(odir, ofile, ndir, nfile, AT_SYMLINK_FOLLOW);
if (status) {
  perror("linkat failed");
}


  return 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文