2010年(16)
分类: LINUX
2010-04-13 13:42:52
内核中要注意的是各种结构体,结构体之间的联系和各个函数以及函数之间的调用关系,参数的传递和函数的功能。
内核中数据结构的字段无外乎包括三种字段:属性字段,调用方法,指向其它结构的指针。具体如下图所示:
文件结构: 包括索引节点和数据,文件系统使用索引节点来记录文件信息。文件系统将文件索引节点号和文件名同时保存在目录中,目录是将文件的名称和它的索引节点号结合在一起的一张表。目录中每一对文件名称和索引节点号称为一个连接。
索引节点号和文件名的关系是1对多的关系
如下图所示:三个不同进程打开同一个文件,其中两个进程使用同一个硬链接。每个进程都使用自己的file对象,但只需要两个dentry对象,每个硬链接对应一个目录项对象。这两个dentry对象指向同一个inode对象,inode 对象标识super_block 对象,以及普通磁盘文件。
下图是文件系统的一个整体图:
一.与文件系统相关的系统结构:
1. VFS 相关的数据结构super_block , inode ,file , dentry 。
2. 与进程相关的结构 fs_struct (每个进程都有它自己当前的工作目录和根目录,而fs_struct就用于维护它们) , files_struct (表示进程当前打开的文件)。
二.路径名查找
当进程必须识别一个文件时,就把它的文件路径名传递给某个VFS系统调用,然后VFS根据路径名导出相应的索引节点。
这个过程是:分析路径名拆分成一个文件名序列,除了最后一个文件名外,所有文件名必定是目录。
如果路径名的第一个字符是“/”,那么这个路径名是绝对路径,因此从current->fs->root(进程的根目录)所标识的目录开始搜索。否则路径名是相对路径,因此从current->fs->pwd(进程的当前目录)所标识的目录开始搜索。
内核代码可以引用当前进程, 通过存取全局项 current, 它在
特定目录中的一个文件名与它相应的索引节点是通过目录项对象联系起来的。所以目录项高速缓存极大地加速了路径解析过程。
文件名解析可以直接定位到文件对应的dentry ,因此路径名分析可以避免从磁盘读取中间目录。
路径名查找是有path_lookup()函数执行的,函数解析在文件“linux内核函数追踪”中。
在某些情况下,一个文件操作可能有VFS本身去执行,无需调用底层函数。如关闭打开的文件(是释放在内存中的file 对象),并不需要涉及磁盘上的相应文件。类似地,修改文件指针lseek(),而这个文件指针是打开文件与进程交互所涉及的一个属性,VFS只需要修改对应的文件对象,而不必访问磁盘上的文件。
从某种意义上说,VFS是一个通用文件系统,但同时在处理一些无需涉及底层磁盘的操作时,它能够独立处理而无需要用到实际系统的功能。
文件系统结构关系图
存放已安装文件系统的有关信息。对基于磁盘的文件系统,它存放在磁盘的文件系统控制块(filesystem control block)中。
所有超级块对象都以双向循环链表的形式链接在一起。(这个链表存放在哪里???)链表中第一个元素用super_blocks变量来表示,而s_list字段存放指向链表相邻元素的指针。S_type 文件系统类型,s_dev 设备标识符,s_root 文件系统根目录的目录项对象,s_inodes 所有索引节点的链表,s_io 等待被写入磁盘的索引节点的链表,s.files 文件对象的链表,
存放关于具体文件的一般信息。对基于磁盘的文件系统,它存放在磁盘的文件控制块(file control block)中。
文件系统处理文件所需要的所有信息都放在inode中,文件名可以随时更改,但inode是唯一的,并且随文件的存在而存在。每个inode都会复制磁盘索引节点包含的一些数据。所以在inode中需要脏标志。
仅当进程访问文件期间存在于内核内存中。
file描述进程与文件的交互。file是在文件被打开时创建的。file 在磁盘上没有对应的映像,所以在file中无需脏标志。file中的主要信息是文件指针,指向文件的当前位置。由于几个进程可能同时访问同一个文件,因此文件指针必须存放在文件对象而不是索引节点对象中(思考一下)。文件对象通过一个名为filp的 slab 高速缓存分配。
一旦目录项被读入内存,VFS就把它转换成基于dentry结构的一个目录项对象。对于进程查找路径中的每个分量,内核都为其创建一个目录项对象。目录项对象将每个分量与其对应的索引节点相联系。dentry对象在磁盘上并没有对应的映像,所以在dentry结构中无需脏标志。目录项对象存放在dentry_cache 的slab分配器高速缓存中。目录项对象的创建和删除通过调用kmem_cache_alloc和kmem_cache_free 实现。
一个有效的dentry结构必定有一个inode结构,这是因为一个目录项要么代表着一个文件,要么代表着一个目录,而目录实际上也是文件。
所以,只要dentry结构是有效的,则其指针d_inode必定指向一个inode结构。可是,反过来则不然,一个inode却可能对应着不止一个dentry结构;也就是说,一个文件可以有不止一个文件名或路径名。这是因为一个已经建立的文件可以被连接(link)到其他文件名。所以在inode结构中有一个队列i_dentry,凡是代表着同一个文件的所有目录项都通过其dentry结构中的d_alias域挂入相应inode结构中的i_dentry队列。
在内核中有一个哈希表dentry_hashtable ,是一个list_head的指针数组。一旦在内存中建立起一个目录节点的dentry 结构,该dentry结构就通过其d_hash域链入哈希表中的某个队列中。
内核中还有一个队列dentry_unused,凡是已经没有用户(count域为0)使用的dentry结构就通过其d_lru域挂入这个队列。
Dentry结构中除了d_alias 、d_hash、d_lru三个队列外,还有d_vfsmnt、d_child及d_subdir三个队列。其中d_vfsmnt仅在该dentry为一个安装点时才使用。另外,当该目录节点有父目录时,则其dentry结构就通过d_child挂入其父节点的d_subdirs队列中,同时又通过指针d_parent指向其父目录的dentry结构,而它自己各个子目录的dentry结构则挂在其d_subdirs域指向的队列中。
dentry 下面的d_name 是一个qstr 结构,而qstr 结构中包括char* name。所以可以通过dentry.d_name.name 得到dentry 的字符型名字。
dentry 中的d_subdirs 字段是指向它的子目录项链表的d_subdirs 。而并不是整个子目录项dentry 。在内核中所有的链表都是通过这种方式实现的,用的是list_head 结构。Dentry的层次结构是通过d_subdirs 和 d_child 来实现的,d_subdirs 实现了纵向(父子级)的链接,d_child实现了横向(兄弟级)的链接。
路径解析过程对dentry的操作
dentry组织结构结构
和文件路径解析有关的数据结构:nameidata , dentry (代表的是逻辑意义上的文件,记录的是其逻辑上的属性) , inode (代表的是其物理上的文件,记录的是其物理上的属性) , qstr 。nameidata 和 qstr 的结构体如下:
struct nameidata {
struct path path;
struct qstr last;
unsigned int flags;
int last_type;
unsigned depth;
char *saved_names[MAX_NESTED_LINKS + 1];
/* Intent data */
union {
struct open_intent open;
} intent;
};
struct qstr {
unsigned int hash;
unsigned int len;
const unsigned char *name;
};