周梦康 发表于 2015-10-17 14357 次浏览 标签 : Linux未完成

在php里面有fopen,返回值是一个文件资源的指针,在Unix C里面使用open或者create返回的则是一个文件描述符,那么文件描述符到底是个什么东西呢?紧紧局限于文字的描述,记忆还是不深刻,理解不透彻。

本篇是我自己根据《UNIX环境高级编程》第三章文件I/O做的笔记。

书上说:对内核而言,所有打开的文件都是通过文件描述符引用,文件描述符是一个非负整数。当打开一个现有文件或创建一个新文件时,内核向进程反悔一个文件描述符。我的理解,只要用到文件描述符的位置,就可以认为是在对一个文件在操作,下图是表示两个进程对同一个文件进行操作时。

对文件描述符的理解,以及文件描述符和进程的关系

从上图可知:

  1. 一个进程可能打开很多文件,每一个进程就有自身的一个文件描述符表,文件描述符表以非负整数来索引在该进程中打开的文件。这里说的fd标志,也就是我们在打开一个文件时的模式,比如只读,只写,读写,追加写等等。文件描述符表的每一项还有一个文件指针,指向文件表

  2. 说文件表,上面这个图片也是正好说明了,两个进程打开了同一个文件,每个进程都会活的各自的一个文件表,这也比较好理解,因为两个进程,对同一个文件的操作可能不一样,文件表里记录着文件状态标志,如果一个读,一个写,当然各有不同。再说当前文件偏移量,比如对文件执行追加写(O_APPEND),那么此时文件的偏移量就是整个文件的长度。

上面的解释还是很抽象,今天(2015.10.23)把task_structfiles_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;
}

参考链接

http://oss.org.cn/kernel-book/ch08/8.2.4.htm

评论列表