← 返回首页
Linux高级程序设计(五十)
发表时间:2021-12-03 00:46:53
文件描述符

文件描述符是一个非负整数,每一个文件描述符都唯一对应了一个打开的文件。使用open函数打开文件就会返回一个文件描述符。

深入理解文件描述符 - 每个进程被创建后标准输入(STDIN_FILENO)、标准输出(STDOUT_FILENO)、标准错误(STDERR_FILENO),对应0,1,2被记录在文件描述符表中。 - 每个进程对应一张打开文件描述符表,这是进程级数据结构,也就是每一个进程都各自有这样一个数据结构; - 内核维持一张打开文件表,文件表由多个文件表项组成,这是系统级数据结构,也就是说这样的数据结构是针对于整个内核而言的,每个进程都可共享的; - 每个打开的文件对应一个i节点(i-node)数据结构(Linux下只有i节点没有v节点),由于这是每一个打开的文件与之对应的,因此这也是一个系统级数据结构,存在于内核中,非进程所独有。

进程、打开文件描述符表、文件表项和i-node结点关系如下图所示。(暂时忽略进程中0、1和2号文件描述符分别默认为标准输入、标准输出和标准错误的情况)

通过以上分析,我们可以得出以下结论:

  1. 每启动一个进程都会为其分配一个task_struct结构体,在task_struct结构体中含有一个file_struct结构体指针,其所指向的file_struct结构体中,含有一个file*的指针数组fd_array,它就是打开文件描述符表,其中每一个元素都指向一个文件表项,这个数组的索引就是文件描述符。此外,file_struct结构体中的next_fd保存的是下一个分配的文件描述符,它会在调用open和close改变,最终使得每次open返回的都是当前可用的最小文件描述符;
  2. 每次调用open或者create(内部实际上还是调用的open),都会对新打开的文件分配一个file结构体,并且将打开文件的标志、状态、权限等信息填入这个file结构体中。这个file结构体也叫文件表项;
  3. 磁盘中的每个文件都对应一个i-node,每一个文件表项都会指向一个文件的i-node,但是同一文件的i-node可以对应多个文件表项(当多次调用open打开同一个文件时就会出现这种情况,不管是同一进程多次打开同一文件(如图中A进程的0号和2号文件描述符对应两个文件表项,但是最终指向同一i-node即同一文件),还是不同进程多次打开同一文件(如图中A进程3号文件描述符和B进程的3号文件描述符));
  4. 同一进程下的不同文件描述符是可以指向同一文件表项,即最终指向同一文件(如图中A进程的0号文件描述符和1号文件描述符,使用dup函数即可实现)。
  5. 子进程在创建时会拷贝父进程的打开文件描述符表,因此父子进程是共享文件表项的,如下图所示:

而相互独立的不同进程的打开文件描述符表是相互独立的,因此相互独立的多个进程之间的文件描述符可以相同,但是不同进程的文件描述符是不能指向同一文件表项的(除非这个文件描述符是从同一个祖先进程中继承得来的),但是这并不妨碍不同进程访问同一文件(如第3点结论);

  1. 指向同一文件表项的不同文件描述符(不同进程相同数值的文件描述符也看做不同)共享文件标志、文件偏移等信息;

  2. 每一个文件表项对应的file结构体中的f_count会记录通过该文件表项打开文件的次数,当f_count计数归0时这个文件表项才会被删除,因此,对于指向同一文件表项的两个不同文件描述符(如子进程所继承的父进程的文件描述符,或同一进程中dup的两个文件描述符指向同一个文件表项),即使其中一个文件描述符关闭了,只要仍然有文件描述符指向这个文件表项,那么就依然能通过这个文件表项访问文件,直到所有指向该文件表项的文件描述符都关闭了才不能再进行访问;

实例:

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

int main(int argc, char const *argv[])
{
    /* code */
    int fd1,fd2,fd3;

    close(0); // 关闭0文件描述符

    fd1 = open("haha.txt",O_RDONLY|O_CREAT,0664);
    fd2 = open("haha.txt",O_RDONLY|O_CREAT,0664);
    fd3 = open("haha.txt",O_RDONLY|O_CREAT,0664);

    printf("fd1=%d\n",fd1); //因为0号文件描述符已经关闭,输出0;
    printf("fd2=%d\n",fd2);
    printf("fd3=%d\n",fd3);

    return 0;
}

运行结果:

[root@iz2zefozq9h39txdb8s7npz shelldemo]# ./a.out
fd1=0
fd2=3
fd3=4

通过以下命令可以查看系统用户进程数和文件描述符数。

[root@iz2zefozq9h39txdb8s7npz shelldemo]# ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 14517
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 65535
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 65536
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited