当前位置 主页 > 服务器问题 > Linux/apache问题 >

    Linux中文件描述符fd与文件指针FILE*互相转换实例解析

    栏目:Linux/apache问题 时间:2019-11-20 12:06

    本文研究的主要是Linux中文件描述符fd与文件指针FILE*互相转换的相关内容,具体介绍如下。

    1.文件描述符fd的定义:文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。

    2.文件指针FILE定义说明文件指针的一般形式为:

    FILE *指针变量标识符;

    其中FILE应为大写,它实际上是由系统定义的一个结构,该结构中含有文件名、文件状态和文件当前位置等信息。在编写源程序时不必关心FILE结构的细节。

    使用系统调用的时候用文件描述符的时候比较多,但是操作比较原始。C库函数在I/O上提供了一些方便的包装(比如格式化I/O、重定向),但是对细节的控制不够。

    如果过度依赖其中的一种只会徒增麻烦,所以知道两者的转换是很有必要的。FILE*是对fd的封装

    当然,有人会说知道文件路径的话重新打开就是了,但是这会产生竞争条件(Race Conditions),首先重新打开文件,相当于是2个fd指向同一文件,然后如果在打开的期间文件被删除了又被新建了一个同名文件,2个fd指向的便是不同的文件。

    glibc库提供了两个转换函数fdopen(3)和fileno(3),都是<stdio.h>中的

    FILE *fdopen(int fd, const char *mode);
    int fileno(FILE *stream);

    PS:为了节省篇幅,还是继续忽略返回值的检查。

    来看看测试吧,是不是我们想的那样。

    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    
    int main()
    {
      const char* filename = "new.txt";
      int fd = open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
    
      FILE* fp = fdopen(fd, "w+");
      int fd2 = fileno(fp);
    
      printf("fd=%d | fd2=%d\n", fd, fd2);
    
      fclose(fp);
      close(fd);
      return 0;
    }
    $ gcc test.c 
    $ ./a.out 
    fd=3 | fd2=3

    参考fileno手册:

    The function fileno() examines the argument stream and returns its integer descriptor.

    FILE是对fd的封装,fileno()是直接取得被封装的fd,因此并未创建新的fd指向该文件。

    参考fdopen手册:

    The fdopen() function associates a stream with the existing file descriptor, fd. The mode of
    the stream (one of the values "r", "r+", "w", "w+", "a", "a+") must be compatible with the
    mode of the file descriptor.

    fdopen()是讲流(FILE对象)与已存在的文件描述符fd进行关联,因此也是未创建新的fd。值得注意的是,FILE指针的模式(mode)必须与文件描述符的模式兼容。

    关于mode参数先搁置会儿,目前我们知道的是,使用fileno和fdopen进行转换,都是在原有的fd上进行操作,并未产生新的fd。那么,再次审视刚才的代码,是否发现了问题?

    我们来检查下close(fd)的返回值,把close(fd)改成下列代码

      if (-1 == close(fd)) {
        perror("close");
        exit(1);
      }
    $ gcc test.c 
    $ ./a.out 
    close: Bad file descriptor

    没错,fclose在关闭文件指针的时候,内部其实也关闭了文件描述符(否则资源就泄露了),既然这里fp内部的文件描述符和fd是同一个,当fp被关闭时,fd也被关闭了,再次关闭fd就会出现“损坏的文件描述符”错误。