【Linux内核】---- 03 安装文件系统
3.1 从硬盘上获取hello.txt 文件的 i 节点
在文件系统中,每个文件都对应一个唯一的 i 节点。
目录文件和普通文件是有区别的,它们存储的内容不一样。目录文件存储着若干个目录项,每个目录项由两部分组成:
一个是文件名,表明该项目所对应的文件名,它可以是对应着普通文件,也可以对应着目录。
另一个是 i 节点号,表明该目录项所对应的文件在 i 节点表中的项号,通过这个i节点表中找到所对应的文件的 i 节点。
整个解析过程如下
根目录文件 i 节点 ----> 找到根目录文件 ----> mnt目录文件 i 节点 ----> 对应硬盘上超级块
----> 磁盘根目录文件 i 节点----> 找到硬盘根目录文件 ----> user目录文件 i 节点 —> 找到user 目录文件
----> hello.txt文件 i 节点 —> 找到hello.c 文件。
3.1.1 准备查找枝梢 i 节点 ---- user目录文件的 i 节点
hello.txt 的目录为 /mnt/user/hello.txt
通过路径解析出 i 节点,这是由open_namei函数完成的,所以sys_open 函数接下来要调用open_namei
// fs/open.c
int sys_open(const char * filename, int flag, int mode){
if( (i = open_namei(filename, flag, mode, &inode) ) < 0 )
...
}
进入 open_namei 函数后,调用 dir_namei 函数,通过解析路径名得到枝梢 i 节点,对于 “/mnt/user/hello.txt” 这个路径名而言,枝梢i 节点就是 user这个目录文件的 i 节点,执行代码如下:
// fs/namei.c
int open_namei(const char * pathname, int flag , int mode, struct m_inode ** res_inode){
if(!(dir = dir_namei(pathname, &namelen, &basename )))
...
}
进入 dir_namei 函数 后,调用 get_dir 函数,同时将路径名也传递下去,开始实质性的 i 节点解析工作。
// fs/namei.c
static struct m_inode * dir_namei(const char * pathname, int * namelen, const char ** name)
{
if(!(idr = get_dir(pathname)))
...
}
3.1.2 确定查找操作起点
进入 get_dir 函数后,开始对路径名进行分析,
// fs/namei.c
static struct m_inode * get_dir(const char * pathname ){
if((c = get_fs_byte(pathname)) == '/' ){
inode = current->root;
pathname++;
}
}
3.1.3 获得名为mnt的目录项
- 先解析出 mnt 这个字符串的长度。
// 代码路径: fs/namei.c
static struct m_inode * get_dir(const char * pathname){
for(namelen = 0; (c = get_fs_byte(pathname++)) && (c!='/'); namelen++)
}
- 再以mnt这个名字为参照,在根目录文件中找到名字为mnt的目录项,
// 代码路径: fs/namei.c
static struct m_inode * get_dir(const char * pathname){
if(!(bh=find_entry(&inode, thisname, namelen, &de)))
}
thisname 标识了mnt字符串的起始位置,namelen标识了该字符串的长度,这样就能锁写mnt这个 字符串。
进入find_entry函数后,开始将根目录文件读取出来,从中找到 名字为mnt的目录项,读取并分析的过程 是这样的:
先确定这个目录文件的大小,因为确定了大小就可以确定目录文件包含多少条目录项,方法是通过根 i 节点中表示根目录文件大小的 i_size 字段除以每个目录占用的字节数。
// fs/namei.c
static struct buffer_head * find_entry(struct m_inode ** dir, const char * name, int namelen, struct dir_entry ** res_dir){
entries = (*dir)->i_size / (sizeof( struct dir_entry) );
}
- 从目录文件的第一个数据块开始读取数据,将数据块读入到 缓冲块,并调用match(namelen, name, de) 函数分析这些数据中有没有mnt的目录项。
// fs/namei.c
static struct buffer_head * find_entry(struct m_inode ** dir , const char *name, int namelen, struct dir_entry ** res_dir){
if(! (bh = bread((*dir)->i_dev, block)) )
de = (struct dir_entry *)bh->b_data;
while(i < entries){
if((char *)de >= BLOCK_SIZE + bh->b_data){
breles(bh);
bh = NULL;
if(! (block = bmap(*dir, i/DIR_ENTRIES_PER_BLOCK)) || !(bh = bread((*dir)->i_dev, block))){
i += DIR_ENTRIES_PER_BLOCK;
continue;
}
de = (struct dir_entry *) bh->b_data;
}
if(match(namelen, name, de)){
*res_dir = de;
return bh;
}
de++;
i++;
}
}
- 最终找到 mnt 这个目录项,值得注意的是,根目录文件在虚拟盘上,所以此时调用bread 函数从设备上读取数据并不会产生中断,之后,find_entry 函数就返回了,