28.文件目录解析代码实现

【README】

1.本文内容总结自 B站 《操作系统-哈工大李治军老师》,内容非常棒,墙裂推荐;

背景:

  • 磁盘格式化后,其组成部分包括引导块,超级块,inode使用位图,盘块使用位图,inode数组,数据区(如上图所示);

【图解】目录解析步骤:(以解析 /my/data/a文件为例)

  • 步骤1:通过超级块计算出inode数组起始位置,inode数组的第一个inode就是根目录inode(或FCB);
  • 步骤2:通过根目录inode查找子目录项列表(形如 <my,103> <data,203>格式),在目录项列表中查找名称为my的inode编号103;
  • 步骤3:根据目录my的inode编号103到inode数组中读取目录my对应的inode103;
  • 步骤4:通过inode103查找子目录项列表,从中查找名称为 data 的inode编号203;
  • 步骤5:以此类推,找到目录或文件a的inode(如inode303)
  • 步骤6:通过inode303获取文件描述符fd(该文件在PCB数组的下标),把fd和要操作的字符位置送入到 read系统调用(函数)获取文件a的盘块号(如789)(因为文件a的内容分散在多个盘块,需要根据字符位置找到具体某个盘块);
  • 步骤7:把盘块号添加到请求队列;
  • 步骤8:磁盘驱动根据电梯算法获取盘块号,并计算出CHS(柱面号,磁头号,扇区号),通过out命令把参数送入磁盘控制器;
  • 步骤9:磁盘控制器根据CHS操作磁盘进行读写,如读操作,则读入到内存缓冲区(循环读取,每次1个字符);
  • 步骤10:读磁盘操作完成后,磁盘控制器发出中断,中断处理程序唤醒睡眠的用户线程;
  • 步骤11:用户线程把内存缓冲区的数据根据用户程序指令送入cpu进行进一步处理;

补充:目录解析过程也可以参考下图。

上面是解析目录的整个过程,接下来看下目录解析的代码实现


 【1】目录解析的代码实现

具体的,目录解析就是  open(/xx/test.c) 打开文件的具体代码实现;

1)open(...)打开文件

 【图解】

  • open_namei是目录解析函数;
  • open_namei 调用了 dir_namei 函数,dir_namei 函数调用了 get_dir(...) 函数;

2)get_dir(...) 函数

 【代码】

// 目录解析 /my/data/a 
static struct m_inode *get_dir(const char* pathname)
{
	if ((c=get_fs_byte(pathname)) == '/') {
		// 1.找到根目录的inode
		inode = current->root;
		pathname++;
	} else if (c) {
		inode = current->pwd; 
	}
	while(1) {
		if (!c) return inode; 
		// 2.find_entry 从根目录中读取给定路径名my的目录
		bh = find_entry(&inode, thisname, namelen, &de);
		// 3.获取该目录my的索引节点 
		int inr = de->inode; 
		int idev = inode->i_dev; 
		// 4.再读取目录my的下一层inode, inr是目录my的inode下标
		inode = iget(idev, inr); 
	}
}   

目录解析步骤:

  • 1.找到根目录的inode;
  • 2.find_entry :从根目录中读取给定路径名my的目录;
  • 3.获取该目录my的索引节点 ;
  • 4.iget(idev, inr)函数:再读取目录my的下一层inode, inr是目录my的inode下标;

【2】目录解析-从根目录开始-iget()

iget(dev, inr) 函数的作用是 读取当前 inode 的下一层 inode ;

 【图解】
1)setup(): 挂载硬盘;
2)mount_root() :挂载根目录; mount_root函数调用了 iget() 函数具体挂载根目录; 

4)iget() 函数读取根目录的代码实现

 【图解】
1)get_super() 获取超级块;  
2)引导块+超级块占用2个块号;

block =  2 + sb->s_imap_block + sb->s_zmap_blocks + 
(inode->i_num-1)/INODES_PER_BLOCK;
  • s_imap_block  表示i节点位图 ;
  • S_zmap_block 表示 盘块位图;
  • inode->i_num 表示 要操作的目录inode的编号; 如 my的inode下标是13;即i_num是13;

所以相加结果 block 表示的是 查找目录的盘块号(这里没太懂) ;
3)bread(inode->i_dev, block) :

  • 把block送入 bread函数从磁盘读取该盘块号的数据;

【3】 find_entry(...) 找到目录项

1)Find_entry()的作用是:

  • 查找目录项列表,以找到 给定目录my的编号103;my的目录项形如 <my, 103> ;
  • 编号103是FCB(inode)数组的下标;  

 【图解】
1)找到直接索引块,并读入到内存 ;

int block = (*dir)->i_zone[0] ;  // 查找直接索引块的盘块号
*bh = bread((*dir)->i_dev,block);  // 去磁盘读取该盘块内容

2)从内存缓冲区中把 b_data 取出来 (b_data存储了子目录项列表)

struct dir_entry *de = bh->b_data ;
While(i<entries) {
 // 遍历子目录项列表查看是否匹配给定路径的目录名
If (match( namelen, name, de )) {   
*res_dir = de;
return bh ;
}
de++; i++;
}
  • 一旦匹配完成,则目标目录项就找到了;
  • 目标目录项有了之后,就可以找到对应的inode下标如13;
  • inode下标一旦找到,送入 iget(..) 把inode读出来;
  • 根据要操作者的字符位置和 inode中的盘块索引可以计算出 盘块号;
  • 把盘块号送入 请求队列 ,磁盘驱动程序把盘块号计算出CHS;
  • 把CHS送入磁盘控制器进行磁盘读写;

 【图解】

  • block = bmap(*dir, i/DIR_ENTRIES_PER_BLOCK)  
  • 循环获取盘块号,或者在一阶间接索引,或者在二阶间接索引;  

【小结】目录解析步骤(代码层面)

  • 步骤1: 调用 iget( dev, nr ) 查询给定下标为nr的inode;
  • 步骤2: iget()函数 首先把超级块读入到内存;
  • 步骤3: 把超级块中的 s_imap_blocks 与 s_imap_blocks 相加,再加2,再加 nr 可以得到nr对应的inode的位置;如果nr等于1,则该inode就是根目录inode
  • 步骤4:把根目录inode读入到内存放入进程的PCB;
  • 步骤5:在进程创建(fork)时,所有进程互相拷贝;这个根目录 inode 在每个进程的PCB都有了
  • 步骤6:在目录解析时,如果是根目录 ”/” ,则解析根目录inode得到数据块,数据块存储了子目录项列表,形如 <my, 103> <var, 203> ;
  • 步骤7:如路径是 /my/data/a  ; 则通过 子目录项列表查找名称为my的inode下标为 103;
  • 步骤8:根据下标103到inode数组读取目录my的inode,根据字符位置和inode从而可以计算出盘块号  ;
  • 步骤9: 读取盘块号的内容,包括了PCB(inode)数组, 和数据块(存储了下一层子目录项列表);
  • 步骤10:依次类推,可以把 data目录的 inode 读取出来,进而把文件a 的盘块号找到并读入到内存

【4】操作系统全图

2个视图:

  • 多进程视图(最重要)
  • 文件视图;
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值