在php里面有fopen
,返回值是一个文件资源的指针,在Unix C里面使用open
或者create
返回的则是一个文件描述符,那么文件描述符到底是个什么东西呢?紧紧局限于文字的描述,记忆还是不深刻,理解不透彻。
本篇是我自己根据《UNIX环境高级编程》第三章文件I/O做的笔记。
书上说:对内核而言,所有打开的文件都是通过文件描述符引用,文件描述符是一个非负整数。当打开一个现有文件或创建一个新文件时,内核向进程反悔一个文件描述符。我的理解,只要用到文件描述符的位置,就可以认为是在对一个文件在操作,下图是表示两个进程对同一个文件进行操作时。
从上图可知:
一个进程可能打开很多文件,每一个进程就有自身的一个
文件描述符表
,文件描述符表以非负整数来索引在该进程中打开的文件。这里说的fd标志,也就是我们在打开一个文件时的模式,比如只读,只写,读写,追加写等等。文件描述符表的每一项还有一个文件指针,指向文件表
。说文件表,上面这个图片也是正好说明了,两个进程打开了同一个文件,每个进程都会活的各自的一个文件表,这也比较好理解,因为两个进程,对同一个文件的操作可能不一样,文件表里记录着文件状态标志,如果一个读,一个写,当然各有不同。再说
当前文件偏移量
,比如对文件执行追加写(O_APPEND
),那么此时文件的偏移量就是整个文件的长度。
上面的解释还是很抽象,今天(2015.10.23)把task_struct
和files_struct
两个关键字添加进来,做下笔记加深理解,不一定正确。
首先一个进程的控制由进程控制块(PCB)来管理,PCB的数据结构就是task_struct
,其中就定义了文件描述符表:
struct task_struct { ... struct files_struct *files; #files包含了进程当前所打开的文件(struct file *fd[NR_OPEN])。在Linux中,一个进程最多只能同时打开NR_OPEN个文件。而且,前三项分别预先设置为标准输入、标准输出和出错消息输出文件。 ... }
每个进程用一个files_struct
结构来记录文件描述符的使用情况,这个files_struct
结构称为用户打开文件表,它是进程(task_struct
)的私有数据。files_struct
结构在/usr/include/linux/sched.h
中定义如下:
struct files_struct { atomic_t count; /* 共享该表的进程数 */ rwlock_t file_lock; /* 保护以下的所有域,以免在tsk->alloc_lock中的嵌套*/ int max_fds; /* 当前文件对象的最大数*/ int max_fdset; /* 当前文件描述符的最大数*/ int next_fd; /* 已分配的文件描述符加1*/ struct file ** fd; /* 指向文件对象指针数组的指针 */ fd_set *close_on_exec; /* 指向执行exec()时需要关闭的文件描述符*/ fd_set *open_fds; /* 指向打开文件描述符的指针*/ fd_set close_on_exec_init; /* 执行exec()时需要关闭的文件描述符的初值集合*/ fd_set open_fds_init; /* 文件描述符的初值集合*/ struct file * fd_array[32]; /* 文件对象指针的初始化数组*/ };
file_struct
和图中的文件描述符表,我有点对不上了。
顺便问下为什么别人在/usr/include/linux/fs.h
看到file
结构的定义,而我却没有看到呢?是因为版本号么?
文件描述符的使用,一个父进程fork
一个子进程,然后这两个进程都往同一个文件里写入数据。
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #include <unistd.h> #include <signal.h> int main(void){ pid_t pid; int fd; signal(SIGCHLD, SIG_IGN); fd = open("test",O_RDWR); if(fd == -1){ perror("open error:"); exit(0); } pid = fork(); if(pid == -1){ perror("fork error:"); exit(0); } if(pid == 0){ write(fd,"child",5); close(fd); }else if(pid > 0){ write(fd,"parent",6); close(fd); } return 0; }
参考链接