在说fd、inode和file关系之前,我们先了解VFS的几个概念。分别是进程描述符、超级块对象、索引节点和文件对象。
超级块对象代表一个已安装的文件系统,由super_block
结构体表示,定义在文件linux/fs中。
索引节点对象代表一个具体文件,由struct inode
结构体表示,定义在linux/fs.h中。
文件对象表示一个进程已经打开的文件。由struct file
结构体表示,定义在linux/fs.h中。
struct file {
...
struct inode *f_inode; /* cached value */
const struct file_operations *f_op;
...
};
f_inode指向一个具体文件。
进程描述符来代表一个进程,由结构体struct task_struct
表示,该结构定义在include/linux/sched.h文件中。
struct task_struct {
...
/* open file information */
struct files_struct *files;
...
};
我们要注意files域,该域指向一个进程打开的所有文件。
files_struct表示一个进程打开的所有文件,files_struct
结构体定义在文件linux/fdtable.h中。该结构体由进程描述符中的files域指向。所有与每个进程打开的文件及文件描述符都包含在其中,其结构体描述如下:
struct files_struct {
/*
* read mostly part
*/
atomic_t count;
bool resize_in_progress;
wait_queue_head_t resize_wait;
struct fdtable __rcu *fdt;
struct fdtable fdtab;
/*
* written part on a separate cache line in SMP
*/
spinlock_t file_lock ____cacheline_aligned_in_smp;
unsigned int next_fd;
unsigned long close_on_exec_init[1];
unsigned long open_fds_init[1];
unsigned long full_fds_bits_init[1];
struct file __rcu * fd_array[NR_OPEN_DEFAULT];
};
struct fdtable
结构体如下:
struct fdtable {
unsigned int max_fds;
struct file __rcu **fd; /* current fd array */
unsigned long *close_on_exec;
unsigned long *open_fds;
unsigned long *full_fds_bits;
struct rcu_head rcu;
};
fd域指向的就是进程打开的文件,它实际是个数组,fd数组指针指向已打开的文件对象链表,默认情况下,指向fd_array数组,NR_OPEN_DEFAULT是个定值,当一个进程打开的文件对象超过NR_OPEN_DEFAULT时,内核将分配一个新数组,并且用fd指针指向它。
文件描述符fd就是索引值,在fd数组中找到文件对象。
我们用write的系统调用来分析如何找到文件的,
首先会文件文件描述符fd获取结构体类型struct fd
struct fd f = fdget_pos(fd);
struct fd {
struct file *file;
unsigned int flags;
};
fdget_pos函数中会调用很多很函数,它会先用当前进程current找到files域,然后在files中找到struct fdtable类型指针fdt,在fdt里面找到指向文件对象的fd数组,fd数组根据文件描述符fd(索引)找到进程打开的文件对象,最后返回。
fdget_pos返回的是一个struct fd结构体,这个结构体里面就是我们找到的文件对象,后面就可以根据这个文件对象找到文件的索引节点。
注意:文件对象代表的是进程打开的文件,索引节点代表的是真正的文件,可能有多个不同进程的文件对象指向同一个索引节点