[关闭]
@harpsword 2015-11-26T14:37:58.000000Z 字数 14737 阅读 2197

文件系统底层操作函数部分之三(inode.c)

文件系统


简述

该程序主要包括住了i节点的函数 i_get()、input()和块映射函数 bmap(),以及一些辅助函数。

i_get()

用于从设备dev上读取指定节点号nr的i节点,并且把i节点的引用计数字段值i_count增1.操作流程如下图。

操作流程

iput()

所完成的功能正好与 iget() 相反,它主要用于把i节点引用计数值递减1,即 相当于该进程释放该i节点或文件,然后根据 该i节点的类型选择相应的动作。

若该i节点是:

在执行过以下操作之一后,内核通常应该调用 iput() 函数
1. 把i节点引用计数字段 i_count 的值增 1
2. 调用了 namei()、dir_namei()或open_namei()函数
3. 调用了 iget()、new_inode()或get_empty_inode()函数
4. 在关闭一个文件时,若已经没有其他进程使用该文件
5. 卸载一个文件系统时(需要放回设备文件名i节点等)

另外,一个进程被创建时,其当前工作目录 pwd、进程当前根目录root和可执行文件目录executable三个i节点结构指针字段都会被初始化而指向这三个i节点,并且也相应的设置了这三个i节点的引用计数字段。因此,当进程执行改变当前工作目录的系统调用时,在该系统调用的代码中就需要调用 input()函数来先放回使用的i节点,然后再设置pwd。 对于 root和 executable也一样。

bmap()

bmap()函数:把一个文件数据块映射到盘块的处理操作。或者说是对i节点的逻辑块(区块)数组i_zone[]进行处理,并且根据i_zone[]中所设置的逻辑块号来设置逻辑块位图对应bit位。

参数说明:

一些理解

内存中i节点表表项

代码分析

宏定义

  1. // 这是get_pipe_inode()函数中使用的,表示 inode.i_zone[0] 存储管道头指针
  2. // inode.i_zone[1] 存储管道尾指针
  3. #define PIPE_HEAD(inode) ((inode).i_zone[0])
  4. #define PIPE_TAIL(inode) ((inode).i_zone[1])

一些重要数据结构定义

struct m_inode inode_table[NR_INODE]={{0,},};

函数分析

常用的函数如下

重要的函数如下

这个函数有两种情况,

  1. bmap()
  1. int bmap(struct m_inode * inode,int block)
  2. {
  3. return _bmap(inode,block,0);
  4. }
  1. create_block()
  1. int create_block(struct m_inode * inode, int block)
  2. {
  3. return _bmap(inode,block,1);
  4. }

代码区

  1. /*
  2. * linux/fs/inode.c
  3. *
  4. * (C) 1991 Linus Torvalds
  5. */
  6. #include <string.h>
  7. #include <sys/stat.h>
  8. #include <linux/sched.h>
  9. #include <linux/kernel.h>
  10. #include <linux/mm.h>
  11. #include <asm/system.h>
  12. // 内存中i节点表(NR_INODE=32)
  13. struct m_inode inode_table[NR_INODE]={{0,},};
  14. // 读指定i节点号的i节点信息
  15. static void read_inode(struct m_inode * inode);
  16. // 写i节点信息到高速缓冲中
  17. static void write_inode(struct m_inode * inode);
  18. // 类似 wait_on_buffer(struct buffer_head * bh)
  19. //// 等待指定的i节点可用
  20. // 如果i节点已被锁定,则将当前任务置为不可中断的等待状态,并添加到该
  21. // i节点的等待队列i_wait中。直到该i节点解锁并明确地唤醒本地任务。
  22. static inline void wait_on_inode(struct m_inode * inode)
  23. {
  24. cli();
  25. while (inode->i_lock)
  26. sleep_on(&inode->i_wait);
  27. sti();
  28. }
  29. //// 对指定的i节点上锁(锁定指定的i节点)
  30. // 如果i节点已被锁定,则将当前任务置为不可中断的等待状态,并添加到该
  31. // i节点的等待队列i_wait中,直到该i节点解锁并明确地唤醒本地任务。然后对其上锁。
  32. // 跟当前任务有个毛关系啊? 根本没有看到 current 变量(当前任务指针)
  33. static inline void lock_inode(struct m_inode * inode)
  34. {
  35. cli();
  36. while (inode->i_lock)
  37. sleep_on(&inode->i_wait);
  38. inode->i_lock=1;
  39. sti();
  40. }
  41. //// 对指定的i节点解锁
  42. // 复位i节点的锁定标志,并明确地唤醒等待在此i节点等待i_wait上的所有进程。
  43. static inline void unlock_inode(struct m_inode * inode)
  44. {
  45. inode->i_lock=0;
  46. wake_up(&inode->i_wait);
  47. }
  48. //// 释放设备dev在内存i节点表中的所有i节点
  49. // 扫描内存中的i节点表数组,如果是指定设备使用的i节点就释放之。
  50. void invalidate_inodes(int dev)
  51. {
  52. int i;
  53. struct m_inode * inode;
  54. // 首先让指针指向内存i节点表数组首项。然后扫描i节点表指针数组中的所有i
  55. // 节点。针对其中每个i节点,先等待该i节点解锁可用,再判断是否属于指定设备
  56. // 的i节点。如果是指定设备的i节点,则看看它是否还被使用着,即其引用计数
  57. // 是否不为0.若是则显示警告信息。然后释放之,即把i节点的设备号字段i_dev置0.
  58. inode = 0+inode_table; // 指向i节点表指针数组首项
  59. for(i=0 ; i<NR_INODE ; i++,inode++) {
  60. wait_on_inode(inode); // 是否必须要等待其解锁????
  61. if (inode->i_dev == dev) {
  62. if (inode->i_count)
  63. printk("inode in use on removed disk\n\r");
  64. inode->i_dev = inode->i_dirt = 0;
  65. }
  66. }
  67. }
  68. //// 同步所有i节点
  69. // 把内存i节点表中所有i节点与设备上i节点作同步操作
  70. void sync_inodes(void)
  71. {
  72. int i;
  73. struct m_inode * inode;
  74. // 首先让内存i节点类型的指针指向i节点表首项,然后扫描整个i节点表中的节点。针对
  75. // 其中每个i节点,先等待该i节点解锁可用(若目前正被上锁的话),然后判断该i节点
  76. // 是否已被修改并且不是管道节点。若是这种情况则将该i节点写入高速缓冲区中。
  77. // 缓冲区管理程序buffer.c会在适当时机将他们写入盘中。
  78. inode = 0+inode_table;
  79. for(i=0 ; i<NR_INODE ; i++,inode++) {
  80. wait_on_inode(inode);
  81. if (inode->i_dirt && !inode->i_pipe)
  82. write_inode(inode);
  83. }
  84. }
  85. //// 文件数据块映射到盘块的处理操作。(block位图处理函数,bmap - block map)
  86. // 参数:inode - 文件的i节点指针;block - 文件中的数据块号;create - 创建块标志。
  87. // 该函数把指定的文件数据块block对应到设备上逻辑块上,并返回逻辑块号。如果创建标志
  88. // 置位,则在设备上对应逻辑块不存在时就申请新磁盘块,返回文件数据块block对应在设备
  89. // 上的逻辑块号(盘块号)。
  90. static int _bmap(struct m_inode * inode,int block,int create)
  91. {
  92. struct buffer_head * bh;
  93. int i;
  94. // 首先判断参数文件数据块号block的有效性。如果块号小于0,则停机。如果块号大于
  95. // 直接块数+间接块数+二次间接块数,超出文件系统表示范围,则停机。
  96. if (block<0)
  97. panic("_bmap: block<0");
  98. if (block >= 7+512+512*512)
  99. panic("_bmap: block>big");
  100. // 然后根据文件块号的大小值和是否设置了创建标志分别进行处理。如果该块号小于7,
  101. // 则使用直接块表示。如果创建标志置位,并且i节点中对应块的逻辑块(区段)字段为0,
  102. // 则相应设备申请一磁盘块(逻辑块),并且将磁盘上逻辑块号(盘块号)填入逻辑块
  103. // 字段中。然后设置i节点改变时间,置i节点已修改标志。然后返回逻辑块号。
  104. if (block<7) {
  105. if (create && !inode->i_zone[block])
  106. if ((inode->i_zone[block]=new_block(inode->i_dev))) {
  107. inode->i_ctime=CURRENT_TIME;
  108. inode->i_dirt=1;
  109. }
  110. return inode->i_zone[block];
  111. }
  112. // 如果该块号>=7,且小于7+512,则说明使用的是一次间接块。下面对一次间接块进行处理。
  113. // 如果是创建,并且该i节点中对应间接块字段i_zone[7]是0,表明文件是首次使用间接块,
  114. // 则需申请一磁盘块用于存放间接块信息,并将此实际磁盘块号填入间接块字段中。然后
  115. // 设置i节点修改标志和修改时间。如果创建时申请磁盘块失败,则此时i节点间接块字段
  116. // i_zone[7] = 0,则返回0.或者不创建,但i_zone[7]原来就为0,表明i节点中没有间接块,
  117. // 于是映射磁盘是吧,则返回0退出。
  118. block -= 7;
  119. if (block<512) {
  120. if (create && !inode->i_zone[7])
  121. if ((inode->i_zone[7]=new_block(inode->i_dev))) {
  122. inode->i_dirt=1;
  123. inode->i_ctime=CURRENT_TIME;
  124. }
  125. if (!inode->i_zone[7])
  126. return 0;
  127. // 现在读取设备上该i节点的一次间接块。并取该间接块上第block项中的逻辑块号(盘块
  128. // 号)i。每一项占2个字节。如果是创建并且间接块的第block项中的逻辑块号为0的话,
  129. // 则申请一磁盘块,并让间接块中的第block项等于该新逻辑块块号。然后置位间接块的
  130. // 已修改标志。如果不是创建,则i就是需要映射(寻找)的逻辑块号。
  131. if (!(bh = bread(inode->i_dev,inode->i_zone[7])))
  132. return 0;
  133. i = ((unsigned short *) (bh->b_data))[block];
  134. if (create && !i)
  135. if ((i=new_block(inode->i_dev))) {
  136. ((unsigned short *) (bh->b_data))[block]=i;
  137. bh->b_dirt=1;
  138. }
  139. // 最后释放该间接块占用的缓冲块,并返回磁盘上新申请或原有的对应block的逻辑块号。
  140. brelse(bh);
  141. return i;
  142. }
  143. // 若程序运行到此,则表明数据块属于二次间接块。其处理过程与一次间接块类似。下面是对
  144. // 二次间接块的处理。首先将block再减去间接块所容纳的块数(512),然后根据是否设置了
  145. // 创建标志进行创建或寻找处理。如果是新创建并且i节点的二次间接块字段为0,则序申请一
  146. // 磁盘块用于存放二次间接块的一级信息,并将此实际磁盘块号填入二次间接块字段中。之后,
  147. // 置i节点已修改标志和修改时间。同样地,如果创建时申请磁盘块失败,则此时i节点二次
  148. // 间接块字段i_zone[8]为0,则返回0.或者不是创建,但i_zone[8]原来为0,表明i节点中没有
  149. // 间接块,于是映射磁盘块失败,返回0退出。
  150. block -= 512;
  151. if (create && !inode->i_zone[8])
  152. if ((inode->i_zone[8]=new_block(inode->i_dev))) {
  153. inode->i_dirt=1;
  154. inode->i_ctime=CURRENT_TIME;
  155. }
  156. if (!inode->i_zone[8])
  157. return 0;
  158. // 现在读取设备上该i节点的二次间接块。并取该二次间接块的一级块上第 block/512 项中
  159. // 的逻辑块号i。如果是创建并且二次间接块的一级块上第 block/512 项中的逻辑块号为0的
  160. // 话,则需申请一磁盘块(逻辑块)作为二次间接块的二级快i,并让二次间接块的一级块中
  161. // 第block/512 项等于二级块的块号i。然后置位二次间接块的一级块已修改标志。并释放
  162. // 二次间接块的一级块。如果不是创建,则i就是需要映射的逻辑块号。
  163. if (!(bh=bread(inode->i_dev,inode->i_zone[8])))
  164. return 0;
  165. i = ((unsigned short *)bh->b_data)[block>>9];
  166. if (create && !i)
  167. if ((i=new_block(inode->i_dev))) {
  168. ((unsigned short *) (bh->b_data))[block>>9]=i;
  169. bh->b_dirt=1;
  170. }
  171. brelse(bh);
  172. // 如果二次间接块的二级块块号为0,表示申请磁盘块失败或者原来对应块号就为0,则返回
  173. // 0退出。否则就从设备上读取二次间接块的二级块,并取该二级块上第block项中的逻辑块号。
  174. if (!i)
  175. return 0;
  176. if (!(bh=bread(inode->i_dev,i)))
  177. return 0;
  178. i = ((unsigned short *)bh->b_data)[block&511];
  179. // 如果是创建并且二级块的第block项中逻辑块号为0的话,则申请一磁盘块(逻辑块),作为
  180. // 最终存放数据信息的块。并让二级块中的第block项等于该新逻辑块块号(i)。然后置位二级块
  181. // 的已修改标志。
  182. if (create && !i)
  183. if ((i=new_block(inode->i_dev))) {
  184. ((unsigned short *) (bh->b_data))[block&511]=i;
  185. bh->b_dirt=1;
  186. }
  187. // 最后释放该二次间接块的二级块,返回磁盘上新申请的或原有的对应block的逻辑块号。
  188. brelse(bh);
  189. return i;
  190. }
  191. //// 取文件数据块block在设备上对应的逻辑块号。
  192. // 参数:inode - 文件的内存i节点指针;block - 文件中的数据块号。
  193. // 若操作成功则返回对应的逻辑块号,否则返回0.
  194. int bmap(struct m_inode * inode,int block)
  195. {
  196. return _bmap(inode,block,0);
  197. }
  198. //// 取文件数据块block在设备上对应的逻辑块号。
  199. // 如果对应的逻辑块不存在就创建一块。返回设备上对应的已存在或新建的逻辑块号。
  200. // 参数:inode - 文件内存i节点指针;block - 文件中的数据块号。
  201. int create_block(struct m_inode * inode, int block)
  202. {
  203. return _bmap(inode,block,1);
  204. }
  205. //// 放回(放置)一个i节点引用计数值递减1,并且若是管道i节点,则唤醒等待的进程。
  206. // 若是块设备文件i节点则刷新设备。并且若i节点的链接计数为0,则释放该i节点占用
  207. // 的所有磁盘逻辑块,并释放该i节点。
  208. // 操作的是块设备
  209. void iput(struct m_inode * inode)
  210. {
  211. // 首先判断参数给出的i节点的有效性,并等待inode节点解锁,如果i节点的引用计数
  212. // 为0,表示该i节点已经是空闲的。内核再要求对其进行放回操作,说明内核中其他
  213. // 代码有问题。于是显示错误信息并停机。
  214. if (!inode)
  215. return;
  216. wait_on_inode(inode);
  217. if (!inode->i_count)
  218. panic("iput: trying to free free inode");
  219. // 如果是管道i节点,则唤醒等待该管道的进程,引用次数减1,如果还有引用则返回。
  220. // 否则释放管道占用的内存页面,并复位该节点的引用计数值、已修改标志和管道标志,
  221. // 并返回。对于管道节点,inode->i_size存放这内存也地址。
  222. if (inode->i_pipe) {
  223. wake_up(&inode->i_wait);
  224. if (--inode->i_count)
  225. return;
  226. free_page(inode->i_size);// 对于管道节点,inode->i_size存放内存页地址
  227. inode->i_count=0;
  228. inode->i_dirt=0;
  229. inode->i_pipe=0;
  230. return;
  231. }
  232. // 如果i节点对应的设备号 = 0,则将此节点的引用计数递减1,返回。例如用于管道
  233. // 操作的i节点,其i节点的设备号为0.
  234. if (!inode->i_dev) {
  235. inode->i_count--;
  236. return;
  237. }
  238. // 如果是块设备文件的i节点,此时逻辑块字段0(i_zone[0])中是设备号,则刷新该设备。
  239. // 并等待i节点解锁。
  240. if (S_ISBLK(inode->i_mode)) {
  241. // sync_dev(inode->i_dev); 觉得应该使用这一句
  242. sync_dev(inode->i_zone[0]);
  243. wait_on_inode(inode);
  244. }
  245. // 如果i节点的引用计数大于1,则计数递减1后就直接返回(因为该i节点还有人在用,不能
  246. // 释放),否则就说明i节点的引用计数值为1。如果i节点的链接数为0,则说明i节点对应文件
  247. // 被删除。于是释放该i节点的所有逻辑块,并释放该i节点。函数free_inode()用于实际释
  248. // 放i节点操作,即复位i节点对应的i节点位图bit位,清空i节点结构内容。
  249. repeat:
  250. if (inode->i_count>1) {
  251. inode->i_count--;
  252. return;
  253. }
  254. // 下面开始 删除该i节点的所有逻辑块,但是是在 inode->i_nlinks = 0的条件下
  255. if (!inode->i_nlinks) {
  256. truncate(inode); // 删除文件
  257. free_inode(inode); // 释放i节点
  258. return;
  259. }
  260. // 这里表示没有删除该i节点后的操作
  261. // 如果该i节点已做过修改,则回写更新该i节点,并等待该i节点解锁。由于这里在写i节点
  262. // 时需要等待睡眠,此时其他进程有可能修改i节点,因此在进程被唤醒后需要再次重复进行
  263. // 上述判断过程(repeat)。
  264. if (inode->i_dirt) {
  265. write_inode(inode); /* we can sleep - so do again */
  266. wait_on_inode(inode);
  267. goto repeat;
  268. }
  269. // 程序若能执行到此,则说明该i节点的引用计数值i_count是1、链接数不为零,并且内容
  270. // 没有被修改过。因此此时只要把i节点引用计数递减1,返回。此时该i节点的i_count=0,
  271. // 表示已释放。 在内存中i节点已经被释放,或者可以认为是空闲状态。
  272. inode->i_count--;
  273. return;
  274. }
  275. //// 从i节点表(inode_table)中获取一个空闲i节点项。
  276. // 寻找引用计数count为0的i节点,并将其写盘后清零,返回指针。引用计数被置1.
  277. struct m_inode * get_empty_inode(void)
  278. {
  279. struct m_inode * inode;
  280. static struct m_inode * last_inode = inode_table;
  281. int i;
  282. do {
  283. // 在初始化last_inode指针指向i节点表头一项后循环扫描整个i节点表。如果last_inode
  284. // 已经指向i节点表的最后一项之后,则让其重新指向i节点表开始处,以继续循环寻找空闲
  285. // i节点项。如果last_inode所指向的i节点的计数值为0,则说明可能找到空闲i节点项。
  286. // 让inode指向该i节点。如果该i节点的已修改标志和锁定标志均为0,则我们可以使用该i
  287. // 节点,于是退出for循环。
  288. inode = NULL;
  289. for (i = NR_INODE; i ; i--) {
  290. if (++last_inode >= inode_table + NR_INODE)
  291. last_inode = inode_table;
  292. if (!last_inode->i_count) {
  293. inode = last_inode;
  294. if (!inode->i_dirt && !inode->i_lock)
  295. // 找到了需要的i节点
  296. break;
  297. }
  298. }
  299. // 如果没有找到空闲i节点(inode=NULL),则将i节点表打印出来供调试使用,并停机。
  300. if (!inode) {
  301. for (i=0 ; i<NR_INODE ; i++)
  302. printk("%04x: %6d\t",inode_table[i].i_dev,
  303. inode_table[i].i_num);
  304. panic("No free inodes in mem");
  305. }
  306. // 等待该i节点解锁,如果该i节点已修改标志被置位的话,则将该i节点刷新,因为刷新时
  307. // 可能会睡眠,因此需要再次循环等待该i节点解锁。
  308. wait_on_inode(inode);
  309. while (inode->i_dirt) {
  310. write_inode(inode);
  311. wait_on_inode(inode);
  312. }
  313. // 如果i节点又被其他占用的话(i节点的计数值不为0了),则重新寻找空闲i节点。否则
  314. // 说明已找到符合要求的空闲i节点项。则将该i节点项内容清零,并置引用计数为1,
  315. // 返回该i节点指针。
  316. } while (inode->i_count);
  317. memset(inode,0,sizeof(*inode)); // 清零
  318. inode->i_count = 1;
  319. return inode;
  320. }
  321. //// 获取管道节点。
  322. // 首先扫描i节点表,寻找一个空闲i节点项,然后取得一页空闲内存供管道使用。然后将得
  323. // 到的i节点的引用计数置为2,初始化管道头和尾,置i节点的管道类型表示。
  324. // 返回为i节点指针,如果失败则返回NULL。
  325. struct m_inode * get_pipe_inode(void)
  326. {
  327. struct m_inode * inode;
  328. // 首先从内存i节点表中取得一个空闲i节点。如果找不到空闲i节点则返回NULL。然后为
  329. // 该i节点申请一页内存,并让节点的i_size字段指向该页面。如果已没有空闲内存,
  330. // 则释放该i节点,并返回NULL
  331. if (!(inode = get_empty_inode()))
  332. return NULL;
  333. if (!(inode->i_size=get_free_page())) {
  334. inode->i_count = 0;
  335. return NULL;
  336. }
  337. // 然后设置该i节点的引用计数为2,并复位管道头尾指针。i节点逻辑块号数组i_zone[]
  338. // 的i_zone[0]和i_zone[1]中分别用来存放管道头和管道尾指针。最后设置i节点是管
  339. // 道i节点标志并返回该i节点号。
  340. inode->i_count = 2; /* sum of readers/writers */
  341. //
  342. PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0;
  343. inode->i_pipe = 1;
  344. return inode;
  345. }
  346. //// 获得一个i节点,从设备上读取
  347. // 参数:dev - 设备号; nr - i 节点号。
  348. // 从设备上读取指定节点号i节点到内存i节点表中,并返回该i节点指针。
  349. // 首先在位于高速缓冲区中的i节点表中搜寻,若找到指定节点号的i节点则在经过一些判断
  350. // 处理后返回该i节点指针。否则从设备dev上读取指定i节点号的i节点信息放入i节点表中,
  351. // 并返回该i节点指针。
  352. struct m_inode * iget(int dev,int nr)
  353. {
  354. struct m_inode * inode, * empty;
  355. // 首先判断参数的有效性。若设备号是0,则表明内核代码有问题,显示出错信息并停机。
  356. // 然后预先从i节点表中取一个空闲i节点备用。
  357. if (!dev)
  358. panic("iget with dev==0");
  359. empty = get_empty_inode();
  360. // 接着扫描i节点表。寻找参数指定节点号nr的i节点。并递增该节点的引用次数。如果当
  361. // 前扫描i节点的设备号不等于指定的设备号或者节点号不等于指定的节点号,则继续扫描。
  362. inode = inode_table;
  363. while (inode < NR_INODE+inode_table) {
  364. if (inode->i_dev != dev || inode->i_num != nr) {
  365. inode++;
  366. continue;
  367. }
  368. // 如果找到指定设备号dev和节点号nr的i节点,则等待该节点解锁。在等待该节点解
  369. // 锁过程中,i节点表可能会发生变化。所以再次进行上述相同判断。如果发生了变化,
  370. // 则再次重新扫描整个i节点表。
  371. wait_on_inode(inode);
  372. if (inode->i_dev != dev || inode->i_num != nr) {
  373. inode = inode_table;
  374. continue;
  375. }
  376. // 到这里表示找到相应的i节点。于是将该i节点引用计数增1.然后再做进一步检查,看它
  377. // 是否是另一个文件系统的安装点。若是则寻找被安装文件系统根节点并返回。如果
  378. // 该i节点的确是其他文件系统的安装点,则在超级块表中搜寻安装在此i节点的超级块。
  379. // 如果没有找到,则显示出错信息,并放回本函数开始时获取的空闲节点empty,
  380. // 返回该i节点指针。
  381. inode->i_count++;
  382. if (inode->i_mount) {
  383. int i;
  384. for (i = 0 ; i<NR_SUPER ; i++)
  385. if (super_block[i].s_imount==inode)
  386. break;
  387. if (i >= NR_SUPER) {
  388. printk("Mounted inode hasn't got sb\n");
  389. if (empty)
  390. iput(empty);
  391. return inode;
  392. }
  393. // 执行到这里表示已经找到安装到inode节点的文件系统超级块。于是将该i节点写盘
  394. // 放回,并从安装在次i节点上的文件系统超级块中取设备号,并令i节点号为ROOT_INO,
  395. // 即为1.然后重新扫描整个i节点表,以获取该被安装文件系统的根i节点信息。
  396. iput(inode);
  397. dev = super_block[i].s_dev;
  398. nr = ROOT_INO;
  399. inode = inode_table;
  400. continue;
  401. }
  402. // 最终我们找到了相应的i节点。因此可以放弃本函数开始处临时申请的空闲的i节点,返回
  403. // 找到的i节点指针。
  404. if (empty)
  405. iput(empty);
  406. return inode;
  407. }
  408. // 如果我们在i节点表中没有找到指定的i节点,则利用前面申请的空闲i节点empty在i节点表中
  409. // 建立该i节点。并从相应设备上读取该i节点信息,返回该i节点指针。
  410. if (!empty)
  411. return (NULL);
  412. inode=empty;
  413. inode->i_dev = dev;
  414. inode->i_num = nr;
  415. read_inode(inode);
  416. return inode;
  417. }
  418. //// 读取指定i节点信息。
  419. // 从设备上读取含有指定i节点信息的i节点盘块,然后复制到指定的i节点结构中。为了确定i节点
  420. // 所在的设备逻辑块号(或缓冲块),必须首先读取相应设备上的超级块,以获取用于计算逻辑
  421. // 块号的每块i节点数信息INODES_PER_BLOCK.在计算出i节点所在的逻辑块号后,就把该逻辑块读入
  422. // 一缓冲块中。然后把缓冲块中相应位置处的i节点内容复制到参数指定的位置处。
  423. static void read_inode(struct m_inode * inode)
  424. {
  425. struct super_block * sb;
  426. struct buffer_head * bh;
  427. int block;
  428. // 首先锁定该i节点,并取该节点所在设备的超级块。
  429. lock_inode(inode);
  430. if (!(sb=get_super(inode->i_dev)))
  431. panic("trying to read inode without dev");
  432. // 该i节点所在的设备逻辑块号=(启动块+超级块)+i节点位图占用的块数+逻辑块位图占用的块数
  433. // +(i节点号-1)/每块含有的i节点数。虽然i节点号从0开始编号,但第i个0号i节点不用,并且
  434. // 磁盘上也不保存对应的0号i节点结构。因此存放i节点的盘块的第i块上保存的是i节点号是1--16
  435. // 的i节点结构而不是0--15的。因此在上面计算i节点号对应的i节点结构所在盘块时需要减1,即:
  436. // B=(i节点号-1)/每块含有i节点结构数。例如,节点号16的i节点结构应该在B=(16-1)/16 = 0
  437. // 的块上。
  438. block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks +
  439. (inode->i_num-1)/INODES_PER_BLOCK;
  440. // 这里我们从设备上读取该i节点所在的逻辑块,并复制指定i节点内容到inode指针所指位置处。
  441. if (!(bh=bread(inode->i_dev,block)))
  442. panic("unable to read i-node block");
  443. *(struct d_inode *)inode =
  444. ((struct d_inode *)bh->b_data)
  445. [(inode->i_num-1)%INODES_PER_BLOCK];
  446. // 最后释放读入的缓冲块,并解锁该i节点。
  447. brelse(bh);
  448. unlock_inode(inode);
  449. }
  450. //// 将i节点信息写入缓冲区中。
  451. // 该函数把参数指定的i节点写入缓冲区相应的缓冲块中,待缓冲区刷新时会写入盘中。为了确定i节点
  452. // 所在的设备逻辑块号(或缓冲块),必须首先读取相应设备上的超级块,以获取用于计算逻辑块号的
  453. // 每块i节点数信息INODES_PER_BLOCK。在计算出i节点所在的逻辑块号后,就把该逻辑块读入一缓冲块
  454. // 中。然后把i节点内容复制到缓冲块的相应位置处。
  455. static void write_inode(struct m_inode * inode)
  456. {
  457. struct super_block * sb;
  458. struct buffer_head * bh;
  459. int block;
  460. // 首先锁定该i节点,如果该i节点没有被修改或者该i节点的设备号等于零,则解锁该i节点,并退出。
  461. lock_inode(inode);
  462. if (!inode->i_dirt || !inode->i_dev) {
  463. unlock_inode(inode);
  464. // 不需要把i节点信息写入到缓冲区
  465. return;
  466. }
  467. // 取超级块,之后可以根据超级块中的信息得到 要写的i节点
  468. if (!(sb=get_super(inode->i_dev)))
  469. panic("trying to write inode without device");
  470. // 该i节点所在的设备逻辑块号=(启动块+超级块)+i节点位图占用的块数+逻辑块位图占用的块数
  471. // +(i节点号-1)/每块含有的i节点数。我们从设备上读取i节点所在的逻辑块,并将该i节点信息复制
  472. // 到逻辑块对应i节点的项位置处。
  473. block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks +
  474. (inode->i_num-1)/INODES_PER_BLOCK;
  475. if (!(bh=bread(inode->i_dev,block)))
  476. panic("unable to read i-node block");
  477. ((struct d_inode *)bh->b_data)
  478. [(inode->i_num-1)%INODES_PER_BLOCK] =
  479. *(struct d_inode *)inode;
  480. // 然后置缓冲区已修改标志,而i节点内容已经与缓冲区中的一致,因此修改标志置零。然后释放该
  481. // 含有i节点的缓冲区,并解锁该i节点。
  482. bh->b_dirt=1;
  483. inode->i_dirt=0;
  484. brelse(bh); // 现在虽然释放了bh缓冲块,而且没有进行同步,同步操作在getblk()中
  485. unlock_inode(inode);
  486. }

疑问

问题 解答
invalidate_inodes()函数中是否必须要等待i节点解锁才释放i节点 是,直接释放后果很严重,会使正在使用这个i节点的进程出现问题
invalidate_inodes()函数中是否必须先等待i节点解锁,才查看设备号是否符合,而不是先查设备号,符合的再等待该i节点解锁
存在和 buffer.c 中 wait_on_buffer() 函数相同的问题,具体的解答有待商榷 解释类 bufffer.c中 wait_on_buffer()的解释

- 问题:
关于缓冲块与块设备之间的同步问题,释放缓冲块的函数brelse中没有进行同步,就可能会被其他进程使用?
- 解答:
经过仔细的阅读,发现 只有缓冲块被使用时对缓冲块位置的处理,而没有缓冲块被释放时关于位置的处理。实际上,它采用的方式是使用缓冲块的时候把该缓冲块加入到hash表中,并且加入到空闲队列的尾部。当它不再使用时,别的进程还是通过 getblk()函数来获取缓冲块,这个不再使用的缓冲块就有可能被使用。而在getblk()函数中,在寻找可用的空闲缓冲块的过程中会对 缓冲块进行同步操作。

想法

  1. 在这个源文件中,主要是对i节点的操作,包括获取内存中空闲i节点项、释放i节点(只是一个进程释放)、读取指定i节点号的i节点信息、把i节点信息写入等操作。
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注