如果指定文件被移动,删除,Linux上的打开文件处理程序会发生什么情况

Linux上的打开文件处理程序会发生什么情况:

  • 移开 – >文件处理程序是否保持有效?
  • 已删除 – >这是否导致EBADF,表明一个无效的文件处理程序?
  • replace为新文件 – >文件处理程序是否指向这个新文件?
  • 通过硬链接replace为新文件 – >我的文件处理程序“跟随”此链接吗?
  • 用一个软链接replace一个新文件 – >我的文件处理程序现在是否打这个软链接文件?

为什么我问这样的问题:我正在使用热插拔硬件(如USB设备等)。 可能会发生,设备(也是/ dev / file)被用户或其他Gremlin重新连接。

处理这个问题的最佳做法是什么?

Solutions Collecting From Web of "如果指定文件被移动,删除,Linux上的打开文件处理程序会发生什么情况"

如果文件被移动(在相同的文件系统中)或重命名,则文件句柄保持打开状态,仍可用于读取和写入文件。

如果文件被删除,文件句柄保持打开,仍然可以使用(这不是一些人所期望的)。 该文件不会真正被删除,直到最后一个句柄关闭。

如果文件被新文件替换,则取决于如何。 如果文件内容被覆盖,则文件句柄仍然有效并访问新内容。 如果现有文件未链接,并且使用相同名称创建新文件,或者如果使用rename()将新文件移动到现有文件上,则与删除相同(参见上文) – 也就是文件句柄将继续引用该文件的原始版本。

一般来说,文件一旦打开,文件就会打开,没有人改变目录结构可以改变它们 – 它们可以移动,重命名文件,或者放置其他东西,它只是保持打开状态。

在Unix中没有删除,只有unlink() ,这是有道理的,因为它不一定删除文件 – 只是从目录中删除链接。


另一方面,如果底层设备消失(例如拔下USB),那么文件句柄将不再有效,并且很可能在任何操作中发生IO /错误。 你仍然必须关闭它。 即使设备被重新插入,情况也是如此,因为在这种情况下保持文件打开是不明智的。

文件句柄指向一个inode而不是一个路径,所以你的大部分场景仍然按照你的设想工作,因为句柄仍然指向文件。

具体来说,删除方案 – 该功能被称为“取消链接”的原因,它破坏了文件名(dentry)和文件之间的“链接”。 当你打开一个文件,然后取消连接,文件实际上仍然存在,直到它的引用计数变为零,这是关闭句柄的时候。

编辑:在硬件的情况下,你已经打开了一个特定的设备节点的句柄,如果你拔下设备,内核将无法访问它,即使设备回来。 您将不得不关闭设备并重新打开。

我不确定其他操作,但是对于删除操作:删除操作根本不会发生(物理上,即在文件系统中),直到文件的最后一个打开的句柄关闭。 因此,从应用程序下删除文件是不可能的。

一些应用程序(不会想到)依靠这种行为,通过创建,打开和立即删除文件,然后生活,只要应用程序 – 允许其他应用程序知道第一个应用程序的生命周期,而不需要看流程图等。

有可能类似的考虑适用于其他的东西。

如果你想检查文件处理程序(文件描述符)是否可以,你可以调用这个函数。

 /** * 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; } 

已删除文件的内存信息(您给出的所有示例都是已删除文件的实例)以及磁盘上的inode依然存在,直到文件关闭。

硬件被热插拔是一个完全不同的问题,如果磁盘inode或元数据已经改变你不应该期望你的程序长时间保持活跃状态​​。

在/ proc /目录下你可以找到当前活动的每一个进程的列表,只要找到你的PID和所有的数据就在那里。 Interresting信息是文件夹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; } 

这个最后的代码很简单,你可以用linkat函数来玩。

 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; }