一个有效的dentry结构必定有一个inode结构,这是因为一个目录项要么代表着一个文件,要么代表着一个目录,而目录实际上也是文件。所以,只要dentry结构是有效的,则其指针d_inode必定指向一个inode结构。但是inode却可以对应多个。
整个结构其实就是一棵树,如果看过我的设备模型kobject就能知道,目录其实就是文件(kobject、inode)再加上一层封装,这里所谓的封装主要就是增加两个指针,一个是指向父目录,一个是指向该目录所包含的所有文件(普通文件和目录)的链表头。
这样才能有我们的目录操作(比如回到上次目录,只需要一个指针步骤【..】,而进入子目录需要链表索引需要多个步骤)
dentry相关的操作(inode里面已经包含了mkdir,rmdir,mknod之类的操作了)
struct dentry_operations { /* 该函数判断目录对象是否有效。VFS准备从dcache中使用一个目录项时,会调用该函数. */ int (*d_revalidate)(struct dentry *, unsigned int); int (*d_weak_revalidate)(struct dentry *, unsigned int); /* 该目录生成散列值,当目录项要加入到散列表时,VFS要调用此函数。 */ int (*d_hash)(const struct dentry *, struct qstr *); /* 该函数来比较name1和name2这两个文件名。使用该函数要加dcache_lock锁。 */ int (*d_compare)(const struct dentry *, const struct dentry *, unsigned int, const char *, const struct qstr *); /* 当d_count=0时,VFS调用次函数。使用该函数要叫 dcache_lock锁。 */ int (*d_delete)(const struct dentry *); /* 当该目录对象将要被释放时,VFS调用该函数。 */ void (*d_release)(struct dentry *); void (*d_prune)(struct dentry *); /* 当一个目录项丢失了其索引节点时,VFS就掉用该函数。 */ void (*d_iput)(struct dentry *, struct inode *); char *(*d_dname)(struct dentry *, char *, int); struct vfsmount *(*d_automount)(struct path *); int (*d_manage)(struct dentry *, bool); } ____cacheline_aligned; 4)文件对象(file)文件对象描述的是进程已经打开的文件。因为一个文件可以被多个进程打开,所以一个文件可以存在多个文件对象。但是由于文件是唯一的,那么inode就是唯一的,目录项也是定的!
进程其实是通过文件描述符来操作文件的,每个文件都有一个32位的数字来表示下一个读写的字节位置,这个数字叫做文件位置。
一般情况下打开文件后,打开位置都是从0开始,除非一些特殊情况。Linux用file结构体来保存打开的文件的位置,所以file称为打开的文件描述。file结构形成一个双链表,称为系统打开文件表。
file 775 struct file { 776 union { 777 struct llist_node fu_llist; /* 每个文件系统中被打开的文件都会形成一个双链表 */ 778 struct rcu_head fu_rcuhead; 779 } f_u; 780 struct path f_path; 781 #define f_dentry f_path.dentry 782 struct inode *f_inode; /* cached value */ 783 const struct file_operations *f_op; /* 指向文件操作表的指针 */ 784 785 /* 786 * Protects f_ep_links, f_flags. 787 * Must not be taken from IRQ context. 788 */ 789 spinlock_t f_lock; 790 atomic_long_t f_count; /* 文件对象的使用计数 */ 791 unsigned int f_flags; /* 打开文件时所指定的标志 */ 792 fmode_t f_mode; /* 文件的访问模式(权限等) */ 793 struct mutex f_pos_lock; 794 loff_t f_pos; /* 文件当前的位移量 */ 795 struct fown_struct f_owner; 796 const struct cred *f_cred; 797 struct file_ra_state f_ra; /* 预读状态 */ 798 799 u64 f_version; /* 版本号 */ 800 #ifdef CONFIG_SECURITY 801 void *f_security; /* 安全模块 */ 802 #endif 803 /* needed for tty driver, and maybe others */ 804 void *private_data; /* 私有数据 */ 805 806 #ifdef CONFIG_EPOLL 807 /* Used by fs/eventpoll.c to link all the hooks to this file */ 808 struct list_head f_ep_links; 809 struct list_head f_tfile_llink; 810 #endif /* #ifdef CONFIG_EPOLL */ 811 struct address_space *f_mapping;/* 页缓存映射 */ 812 #ifdef CONFIG_DEBUG_WRITECOUNT 813 unsigned long f_mnt_write_state; 814 #endif 815 } __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */重点解释一些重要字段:
首先,f_flags、f_mode和f_pos代表的是这个进程当前操作这个文件的控制信息。这个非常重要,因为对于一个文件,可以被多个进程同时打开,那么对于每个进程来说,操作这个文件是异步的,所以这个三个字段就很重要了。
对于引用计数f_count,当我们关闭一个进程的某一个文件描述符时候,其实并不是真正的关闭文件,仅仅是将f_count减一,当f_count=0时候,才会真的去关闭它。对于dup,fork这些操作来说,都会使得f_count增加,具体的细节,以后再说。