文件对象是已打开的文件在内存中的表示,主要用于建立进程和磁盘上的文件的对应关系。它由sys_open() 现场创建,由sys_close()销毁。文件对象和物理文件的关系有点像进程和程序的关系一样。当我们站在用户空间来看待VFS,我们像是只需与文件对象打交道,而无须关心超级块,索引节点或目录项。因为多个进程可以同时打开和操作同一个文件,所以同一个文件也可能存在多个对应的文件对象。文件对象仅仅在进程观点上代表已经打开的文件,它反过来指向目录项对象(反过来指向索引节点)。一个文件对应的文件对象可能不是惟一的,但是其对应的索引节点和目录项对象无疑是惟一的。
清单4. 文件对象
struct file {
……
struct list_head
f_list;
/*文件对象链表*/
struct dentry
*f_dentry;
/*相关目录项对象*/
struct vfsmount
*f_vfsmnt;
/*相关的安装文件系统*/
struct file_operations *f_op;
/*文件操作表*/
……
};
struct file_operations {
……
//文件读操作
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
……
//文件写操作
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
……
int (*readdir) (struct file *, void *, filldir_t);
……
//文件打开操作
int (*open) (struct inode *, struct file *);
……
};
根据文件系统所在的物理介质和数据在物理介质上的组织方式来区分不同的文件系统类型的。 file_system_type结构用于描述具体的文件系统的类型信息。被Linux支持的文件系统,都有且仅有一个file_system_type结构而不管它有零个或多个实例被安装到系统中。
而与此对应的是每当一个文件系统被实际安装,就有一个vfsmount结构体被创建,这个结构体对应一个安装点。
清单5. 和文件系统相关
struct file_system_type {
const char *name;
/*文件系统的名字*/
struct subsystem subsys;
/*sysfs子系统对象*/
int fs_flags;
/*文件系统类型标志*/
/*在文件系统被安装时,从磁盘中读取超级块,在内存中组装超级块对象*/
struct super_block *(*get_sb) (struct file_system_type*,
int, const char*, void *);
void (*kill_sb) (struct super_block *); /*终止访问超级块*/
struct module *owner;
/*文件系统模块*/
struct file_system_type * next;
/*链表中的下一个文件系统类型*/
struct list_head fs_supers;
/*具有同一种文件系统类型的超级块对象链表*/
};
struct vfsmount
{
struct list_head mnt_hash;
/*散列表*/
struct vfsmount *mnt_parent;
/*父文件系统*/
struct dentry *mnt_mountpoint;
/*安装点的目录项对象*/
struct dentry *mnt_root;
/*该文件系统的根目录项对象*/
struct super_block *mnt_sb;
/*该文件系统的超级块*/
struct list_head mnt_mounts;
/*子文件系统链表*/
struct list_head mnt_child;
/*子文件系统链表*/
atomic_t mnt_count;
/*使用计数*/
int mnt_flags;
/*安装标志*/
char *mnt_devname;
/*设备文件名*/
struct list_head mnt_list;
/*描述符链表*/
struct list_head mnt_fslink;
/*具体文件系统的到期列表*/
struct namespace *mnt_namespace;
/*相关的名字空间*/
};
清单6. 打开的文件集
struct files_struct {//打开的文件集
atomic_t count;
/*结构的使用计数*/
……
int max_fds;
/*文件对象数的上限*/
int max_fdset;
/*文件描述符的上限*/
int next_fd;
/*下一个文件描述符*/
struct file ** fd;
/*全部文件对象数组*/
……
};
struct fs_struct {//建立进程与文件系统的关系
atomic_t count;
/*结构的使用计数*/
rwlock_t lock;
/*保护该结构体的锁*/
int umask;
/*默认的文件访问权限*/
struct dentry * root;
/*根目录的目录项对象*/
struct dentry * pwd;
/*当前工作目录的目录项对象*/
struct dentry * altroot; /*可供选择的根目录的目录项对象*/
struct vfsmount * rootmnt; /*根目录的安装点对象*/
struct vfsmount * pwdmnt; /*pwd的安装点对象*/
struct vfsmount * altrootmnt;/*可供选择的根目录的安装点对象*/
};
清单7. 辅助查找
struct nameidata {
struct dentry *dentry;
/*目录项对象的地址*/
struct vfsmount *mnt;
/*安装点的数据*/
struct qstr last;
/*路径中的最后一个component*/
unsigned int flags;
/*查找标识*/
int last_type;
/*路径中的最后一个component的类型*/
unsigned depth;
/*当前symbolic link的嵌套深度,不能大于6*/
char *saved_names[MAX_NESTED_LINKS + 1];/
/*和嵌套symbolic link 相关的pathname*/
union {
struct open_intent open; /*说明文件该如何访问*/
} intent; /*专用数据*/
};
如上的数据结构并不是孤立存在的。正是通过它们的有机联系,VFS才能正常工作。如下的几张图是对它们之间的联系的描述。
如图5所示,被Linux支持的文件系统,都有且仅有一个file_system_type结构而不管它有零个或多个实例被安装到系统中。每安装一个文件系统,就对应有一个超级块和安装点。超级块通过它的一个域s_type指向其对应的具体的文件系统类型。具体的文件系统通过file_system_type中的一个域fs_supers链接具有同一种文件类型的超级块。同一种文件系统类型的超级块通过域s_instances链接。
从图6可知:进程通过task_struct中的一个域files_struct files来了解它当前所打开的文件对象;而我们通常所说的文件描述符其实是进程打开的文件对象数组的索引值。文件对象通过域f_dentry找到它对应的dentry对象,再由dentry对象的域d_inode找到它对应的索引结点,这样就建立了文件对象与实际的物理文件的关联。最后,还有一点很重要的是, 文件对象所对应的文件操作函数列表是通过索引结点的域i_fop得到的。图6对第三部分源码的理解起到很大的作用。