Answers:
如果文件已移动(在同一文件系统中)或已重命名,则文件句柄将保持打开状态,并且仍可用于读取和写入文件。
如果删除了文件,则文件句柄将保持打开状态,并且仍然可以使用(这不是某些人所期望的)。在关闭最后一个句柄之前,不会真正删除该文件。
如果文件被新文件替换,则取决于具体方式。如果文件的内容被覆盖,则文件句柄将仍然有效并访问新内容。如果现有文件被取消链接,并且使用相同的名称创建了一个新文件,或者如果使用将新文件移动到了现有文件rename()
,则该文件与删除操作相同(请参见上文)-也就是说,文件句柄将继续引用文件的原始版本。
通常,一旦打开文件,即打开文件,并且没有人更改目录结构就可以更改它-他们可以移动,重命名文件或在其位置放置其他内容,只是保持打开状态。
在Unix中,只有delete,unlink()
这是有道理的,因为它不一定要删除文件-只是从目录中删除链接。
另一方面,如果基础设备消失(例如USB拔出),则文件句柄将不再有效,并且很可能在任何操作中产生IO /错误。您仍然必须关闭它。即使插回设备,也是如此,因为在这种情况下保持文件打开不明智。
如果要检查文件处理程序(文件描述符)是否正常,可以调用此函数。
/**
* 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(!fcntl(fd, F_GETFL)) {
检查的重点是什么?我猜你在找EBADF
。(您也可能忘记了初始化errno
为0)。
open(O_WRONLY|O_APPEND)
-st_nlink在我的描述符打开时始终保持> = 1一起使用。
删除文件的内存信息(您提供的所有示例都是删除文件的实例)以及磁盘上的inode一直存在,直到关闭文件为止。
硬件被热插拔是完全不同的问题,如果磁盘上的inode或元数据完全改变了,则您不应该期望程序会长时间存活。
以下实验表明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 data
删除data
。但是./a.out
将继续运行而不会出错,并产生以下整个输出:
line: 1234
line: 1234
line: 1234
line: 1234
line: 1234
我已经在Ubuntu 16.04.3。上进行了实验。
在/ proc /目录下,您将找到当前活动的每个进程的列表,只需找到您的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;
}
最后的代码很简单,您可以使用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;
}