在进程结构体struct task_struct中有两个变量fs_truct * fs和files_struct *files保存了文件相关的信息。其中fs_truct * fs保存了文件系统的相关信息,比如文件系统挂载的根目录和当前目录以及对应的目录项。files_struct *files保存了进程打开的文件的信息。
当进程通过open系统调用打开一个文件时,该系统调用找到这个文件后,会把文件封装到一个file结构的实例中提供给进程,这个实例称为file对象。系统使用一个数组来管理进程打开的文件的file对象,数组中的每个元素都指向一个进程所打开文件的file对象。这个数组叫文件描述符数组,数组元素的下标叫做文件描述符(fd,file describle),该描述符就是系统对文件的标识。内核通过系统调用dup、dup2和fctl可以使文件描述符数组中的多个元素指向同一个文件的file对象,即同一个file对象可以有多个文件描述符。
进程描述符数组中存放了一个进程所访问的所有文件,把这个文件描述符数组和这个数组在系统中的一些动态信息组合到一起,就形成了一个新的数据结构——进程打开文件表,即files_struct,其定义如下:
struct files_struct {
atomic_t count; //引用计数
spinlock_t file_lock; /* Protects all the below members. Nests inside tsk->alloc_lock */
struct fdtable *fdt; //指向后面的fdtab描述符表
struct fdtable fdtab; //管理文件描述符
fd_set close_on_exec_init; //位图
fd_set open_fds_init; //位图
struct file * fd_array[NR_OPEN_DEFAULT]; //文件描述符数组
};
显然,这个结构应该属于进程的私有数据,所以进程控制块task_struct用指针files指向它。
struct task_struct {
...
/* open file information */
struct files_struct *files;
...
};
file_struct中的fdt和fdtab用于管理文件文件描述符,一个是fdtable类型,另一个是其指针类型。fdtable的定义如下:
struct fdtable {
unsigned int max_fds; //可以打开的最大文件数
int max_fdset; //位图的最大长度,描述fd是否被使用了
int next_fd; //下一个可用的fd
struct file ** fd; /* current fd array 指向files_struct的fd_array */
fd_set *close_on_exec;
fd_set *open_fds; //打开的文件标记,比如第2位为0,则打开了2号文件
struct rcu_head rcu;
struct files_struct *free_files;
struct fdtable *next;
};
fdtable结构的fd字段指向文件对象的指针数组。该数组的长度存放在max_fds字段中。通常,fd字段指向files_struct结构的fd_array字段,
该字段包括32个文件对象指针。如果进程打开的文件数目多于32,内核就分配一个新的、更大的文件指针数组,并将其地址存放在fd字段中,
内核同时也更新max_fds字段的值。详见expand_files:
expand_files(files, fd)---->expand_fdtable:
alloc_fdtable:
fdt = kmalloc(sizeof(struct fdtable), GFP_KERNEL);
data = alloc_fdmem(nr * sizeof(struct file *));
fdt->fd = (struct file **)data;
data = alloc_fdmem(max_t(unsigned int,2 * nr / BITS_PER_BYTE, L1_CACHE_BYTES));
fdt->open_fds = (fd_set *)data;
data += nr / BITS_PER_BYTE;
fdt->close_on_exec = (fd_set *)data;
copy_fdtable:
cpy = ofdt->max_fds * sizeof(struct file *);
set = (nfdt->max_fds - ofdt->max_fds) * sizeof(struct file *);
memcpy(nfdt->fd, ofdt->fd, cpy);
memset((char *)(nfdt->fd) + cpy, 0, set);
cpy = ofdt->max_fds / BITS_PER_BYTE;
set = (nfdt->max_fds - ofdt->max_fds) / BITS_PER_BYTE;
memcpy(nfdt->open_fds, ofdt->open_fds, cpy);
memset((char *)(nfdt->open_fds) + cpy, 0, set);
memcpy(nfdt->close_on_exec, ofdt->close_on_exec, cpy);
memset((char *)(nfdt->close_on_exec) + cpy, 0, set);