操作系统为进程维护了打开的文件列表,每个进程维护了一个file数组字段(struct file * fd[NR_OPEN]);每个元素指向一个file结构体。每个file结构体有一个字段指向inode结构体,inode管理这个文件的内容、权限等信息。这里分析的是file结构体的管理。
下面是file结构体的定义
struct file {
mode_t f_mode;
loff_t f_pos;
unsigned short f_flags;
unsigned short f_count;
off_t f_reada;
struct file *f_next, *f_prev;
int f_owner; /* pid or -pgrp where SIGIO should be sent */
struct inode * f_inode;
struct file_operations * f_op;
unsigned long f_version;
void *private_data; /* needed for tty driver, and maybe others */
};
下面是对file结构体的管理,当进程打开一个文件的时候,就可能需要从中申请一个file结构体。
/*
* linux/fs/file_table.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/mm.h>
struct file * first_file;
int nr_files = 0;
// 双向循环链表,first_file指向头指针,头插法插入一个节点
static void insert_file_free(struct file *file)
{
file->f_next = first_file;
file->f_prev = first_file->f_prev;
file->f_next->f_prev = file;
file->f_prev->f_next = file;
first_file = file;
}
// 删除一个节点
static void remove_file_free(struct file *file)
{
// 如果要删除的节点是第一个节点,则更新头指针,指向下一个节点
if (first_file == file)
first_file = first_file->f_next;
// 如果被删除的节点后面还有节点,则需要更新下一个节点的prev指针,指向当前节点的上一个节点
if (file->f_next)
file->f_next->f_prev = file->f_prev;
// 同理,更新上一个节点的next指针,指向被删除节点的下一个节点
if (file->f_prev)
file->f_prev->f_next = file->f_next;
// 置空
file->f_next = file->f_prev = NULL;
}
// file插入链表,成为最后一个节点
static void put_last_free(struct file *file)
{
// 保证file脱离了原来的链表
remove_file_free(file);
// 插入链表,但是不更新头指针first_file,所以file成为最后一个节点
file->f_prev = first_file->f_prev;
file->f_prev->f_next = file;
file->f_next = first_file;
file->f_next->f_prev = file;
}
void grow_files(void)
{
struct file * file;
int i;
// 申请一页内存
file = (struct file *) get_free_page(GFP_KERNEL);
if (!file)
return;
// i=PAGE_SIZE/sizeof(struct file),即一页可以存多少个节点,更新最大节点数
nr_files+=i= PAGE_SIZE/sizeof(struct file);
/*
当前是初始化的时候,先初始化一个节点,需要初始化的节点数减一,执行insert_file_free
前需要保证first_file非空,见insert_file_free中的first_file
*/
if (!first_file)
file->f_next = file->f_prev = first_file = file++, i--;
// 形成一个链表
for (; i ; i--)
insert_file_free(file++);
}
// file链表初始化
unsigned long file_table_init(unsigned long start, unsigned long end)
{
first_file = NULL;
return start;
}
// 获取一个可以的file结构体
struct file * get_empty_filp(void)
{
int i;
struct file * f;
if (!first_file)
grow_files();
repeat:
// nr_files是链表的总节点数
for (f = first_file, i=0; i < nr_files; i++, f = f->f_next)
// 找到空闲的节点
if (!f->f_count) {
// 脱离链表
remove_file_free(f);
// 清空内存
memset(f,0,sizeof(*f));
// 插入链表末尾
put_last_free(f);
// 标记已使用
f->f_count = 1;
f->f_version = ++event;
return f;
}
// 没有找到空闲节点,扩容,再找
if (nr_files < NR_FILE) {
grow_files();
goto repeat;
}
return NULL;
}
从图中我们可以看出,系统维护了一个双向循环的链表,保存了一系列已使用和未使用的file结构体。first_file指针执行第一个空闲的节点,进程申请file结构体的时候就把该节点放到链表结尾。first_file指针指向下一个空闲节点。如果没有空闲节点了,就会自动扩容。