[关闭]
@harpsword 2015-11-14T23:19:10.000000Z 字数 8147 阅读 1544

文件系统底层操作函数部分之一

文件系统


简介

底层操作函数是文件系统的第2个部分(我自己分的),这个部分共包括5个文件,5个文件的介绍如下表

文件名 内容
super.c 对文件系统超级块进行访问和管理的函数
bitmap.c 处理文件系统的逻辑块位图和i节点位图
truncate.c 仅有一个把文件数据长度截为0的函数 truncate()
inode.c 文件系统i节点信息的访问和管理
namei.c 用于完成从一个给定文件路径名寻找并加载其对应i节点信息的功能

细节

  1. set_bit(nr,addr),置位(addr,nr)的bit位(设为1),返回原bit位值
  2. clear_bit(nr,addr),复位(addr,nr)的bit位(设为0),返回原bit位值的反码。

bitmap.c

功能描述

功能
主要用于对文件系统的i节点位图和逻辑块位图进行占用和释放操作处理。i节点位图的操作函数是free_inode()和new_inode(),操作逻辑块位图的函数是 free_block() 和 new_block()。
  1. free_inode(): 用于释放指定设备dev上数据区中的逻辑块block.具体操作是复位指定逻辑块block对应逻辑块位图中的比特位。
Created with Raphaël 2.1.2free_inode()取指定设备dev的超级块根据超级块判断逻辑块号block的有效性在高速缓冲区中寻找是否存在该块释放该缓冲块计算得到数据逻辑块号操作逻辑块位图来释放该逻辑块设置该逻辑块位图对应的缓冲块的已修改标志endyesnoyesno
  1. new_block():用于向设备dev申请一个逻辑块,返回逻辑块号,并且置位指定逻辑块block对应的逻辑块位图比特位。
Created with Raphaël 2.1.2new_inode()取指定设备dev的超级块对整个逻辑块位图进行搜索,寻找首个是0的比特位是否找到将找到的比特位置1并置位对应缓冲块的已修改标志计算得到数据逻辑块的盘块号申请相应的缓冲块并清零设置已更新和已修改标志释放该缓冲块(why???)返回盘块号返回0yesno
  1. free_inode():用于释放指定的i节点,并复位对应的i节点位图比特位。
  2. new_inode():用于为设备dev建立一个新i节点,并返回该新i节点的指针。这两个函数的主要操作与之前的两个函数类似,只是由逻辑块位图换成了i节点位图。

代码分析

  1. /*
  2. * linux/fs/bitmap.c
  3. *
  4. * (C) 1991 Linus Torvalds
  5. */
  6. /* bitmap.c contains the code that handles the inode and block bitmaps */
  7. #include <string.h>
  8. #include <linux/sched.h>
  9. #include <linux/kernel.h>
  10. //// 将指定地址(addr)处的一块1024字节内存清零
  11. // 输入:eax = 0; ecx = 以字节为单位的数据块长度(BLOCK_SIZE/4);edi = 指定
  12. // 起始地址addr。
  13. // rep stosl:Fill (E)CX dwords(双字,4 bytes) at ES:[(E)DI] with EAX
  14. #define clear_block(addr) \
  15. __asm__ __volatile__ ("cld\n\t" \ // 清方向位
  16. "rep\n\t" \ // 重复执行存储数据(0).
  17. "stosl" \
  18. ::"a" (0),"c" (BLOCK_SIZE/4),"D" ((long) (addr)))
  19. //// 把指定地址开始的第nr个位偏移处的bit位置位(nr可大于321).返回原bit位值。
  20. // 输入:%0-eax(返回值):%1 -eax(0);%2-nr,位偏移值;%3-(addr),addr的内容。
  21. // res是一个局部寄存器变量。该变量将被保存在指定的eax寄存器中,以便于高效
  22. // 访问和操作。这种定义变量的方法主要用于内嵌汇编程序中。详细说明可以参考
  23. // gcc手册”在指定寄存器中的变量“。整个宏是一个语句表达式(即圆括号括住的组合句),
  24. // 其值是组合语句中最后一条表达式语句res的值。
  25. // btsl指令用于测试并设置bit位。把基地址(%3)和bit位偏移值(%2)所指定的bit位值
  26. // 先保存到进位标志CF中,然后设置该bit位为1.指令setb用于根据进位标志CF设置
  27. // 操作数(%al)。如果CF=1则%al = 1,否则%al = 0。
  28. #define set_bit(nr,addr) ({\
  29. register int res ; \
  30. __asm__ __volatile__("btsl %2,%3\n\tsetb %%al": \
  31. "=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \
  32. res;})
  33. // 复位指定地址开始的第nr位偏移处的bit位。返回原bit位值的反码。
  34. // 输入:%0-eax(返回值);%1-eax(0);%2-nr,位偏移值;%3-(addr),addr的内容。
  35. // btrl指令用于测试并复位bit位。其作用与上面的btsl类似,但是复位指定bit位。
  36. // 指令setnb用于根据进位标志CF设置操作数(%al).如果CF=1则%al=0,否则%al=1.
  37. #define clear_bit(nr,addr) ({\
  38. register int res ; \
  39. __asm__ __volatile__("btrl %2,%3\n\tsetnb %%al": \
  40. "=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \
  41. res;})
  42. // 不是很懂 32bit 汇编 !!!!!!
  43. // 从addr开始寻找第1个0值bit位。
  44. // 输入:%0-ecx(返回值);%1-ecx(0); %2-esi(addr).
  45. // 在addr指定地址开始的位图中寻找第1个是0的bit位,并将其距离addr的bit位偏移
  46. // 值返回。addr是缓冲块数据区的地址,扫描寻找的范围是1024字节(8192bit位)。
  47. #define find_first_zero(addr) ({ \
  48. int __res; \
  49. __asm__ __volatile__ ("cld\n" \ // 清方向位,往高地址方向寻找
  50. "1:\tlodsl\n\t" \ // 取[esi]→eax.
  51. "notl %%eax\n\t" \ // eax中每位取反。
  52. "bsfl %%eax,%%edx\n\t" \ // 从位0扫描eax中是1的第1个位,其偏移值→edx
  53. "je 2f\n\t" \ // 如果eax中全是0,则向前跳转到标号2处。
  54. "addl %%edx,%%ecx\n\t" \ // 偏移值加入ecx(ecx是位图首个0值位的偏移值)
  55. "jmp 3f\n" \ // 向前跳转到标号3处
  56. "2:\taddl $32,%%ecx\n\t" \ // 未找到0值位,则将ecx加1个字长的位偏移量32
  57. "cmpl $8192,%%ecx\n\t" \ // 已经扫描了8192bit位(1024字节)
  58. "jl 1b\n" \ // 若还没有扫描完1块数据,则向前跳转到标号1处
  59. "3:" \ // 结束。此时ecx中是位偏移量。
  60. :"=c" (__res):"c" (0),"S" (addr)); \
  61. __res;})
  62. //// 释放设备dev上数据区中的逻辑块block.
  63. // 复位指定逻辑块block对应的逻辑块位图bit位
  64. // 参数:dev是设备号,block是逻辑块号(盘块号)包括超级块、各种位图,即不是从数据区开始
  65. void free_block(int dev, int block)
  66. {
  67. struct super_block * sb;
  68. struct buffer_head * bh;
  69. // 首先取设备dev上文件系统的超级块信息,根据其中数据区开始逻辑块号和文件系统中逻辑
  70. // 块总数信息判断参数block的有效性。如果指定设备超级块不存在,则出错当机。若逻辑块
  71. // 号小于盘上面数据区第一个逻辑块的块号或者大于设备上总逻辑块数,也出错当机。
  72. if (!(sb = get_super(dev)))
  73. panic("trying to free block on nonexistent device");
  74. if (block < sb->s_firstdatazone || block >= sb->s_nzones)
  75. panic("trying to free block not in datazone");
  76. // 然后从hash表中寻找该块数据。若找到了则判断其有效性,并清已修改和更新标志,释放
  77. // 该数据块。该段代码的主要用途是如果该逻辑块目前存在于高速缓冲区中,就释放对应
  78. // 的缓冲块。
  79. bh = get_hash_table(dev,block);
  80. // 下面的代码会造成数据块不能释放。因为当b_count > 1时,这段代码会仅打印一段信息而
  81. // 没有执行释放操作。
  82. if (bh) {
  83. // 因为有多个 进程使用该缓冲块或者没有进程使用即该缓冲块应该不存在,所以不释放
  84. if (bh->b_count != 1) {
  85. printk("trying to free block (%04x:%d), count=%d\n",
  86. dev,block,bh->b_count);
  87. return;
  88. }
  89. bh->b_dirt=0;
  90. bh->b_uptodate=0;
  91. brelse(bh);
  92. }
  93. // 接着我们复位block在逻辑块位图中的bit(置0),先计算block在数据区开始算起的数据
  94. // 逻辑块号(从1开始计数)。然后对逻辑块(区块)位图进行操作,复位对应的bit位。如果对应
  95. // bit位原来就是0,则出错停机。由于1个缓冲块有1024字节,即8192比特位,因此block/8192
  96. // 即可计算出指定块block在逻辑位图中的哪个块上。而block&8192可以得到block在逻辑块位图
  97. // 当前块中的bit偏移位置。
  98. block -= sb->s_firstdatazone - 1 ;
  99. // #define set_bit(nr,addr)
  100. // sb->s_zmap[block/8192] 是一个指向逻辑块缓冲区的指针
  101. // sb->s_zmap[block/8192]->b_data 是一个指向数据区的指针,指针即地址
  102. if (clear_bit(block&8191,sb->s_zmap[block/8192]->b_data)) {
  103. printk("block (%04x:%d) ",dev,block+sb->s_firstdatazone-1);
  104. panic("free_block: bit already cleared");
  105. }
  106. // 最后置相应逻辑块位图所在缓冲区已修改标志。
  107. sb->s_zmap[block/8192]->b_dirt = 1;
  108. }
  109. //// 向设备申请一个逻辑块。
  110. // 函数首先取得设备的超级块,并在超级块中的逻辑块位图中寻找第一个0值bit位(代表一个
  111. // 空闲逻辑块)。然后位置对应逻辑块在逻辑块位图中的bit位。接着为该逻辑块在缓冲区中取得
  112. // 一块对应缓冲块。最后将该缓冲块清零,并设置其已更新标志和已修改标志。并返回逻辑块
  113. // 号。函数执行成功则返回逻辑块号,否则返回0.
  114. int new_block(int dev)
  115. {
  116. struct buffer_head * bh;
  117. struct super_block * sb;
  118. int i,j;
  119. // 首先获取设备dev的超级块。如果指定设备的超级块不存在,则出错当机。然后扫描
  120. // 文件系统的8块逻辑位图,寻找首个0值bit位,以寻找空闲逻辑块,获取放置该逻辑块的
  121. // 块号。如果全部扫描完8块逻辑块位图的所有bit位(i >= 8 或 j >= 8192)还没找到0值
  122. // bit位或者位图所在的缓冲块指针无效(bh=NULL)则返回0退出(没有空闲逻辑块)。
  123. if (!(sb = get_super(dev)))
  124. panic("trying to get new block from nonexistant device");
  125. j = 8192;
  126. for (i=0 ; i<8 ; i++) // 最多8个逻辑块位图
  127. if ((bh=sb->s_zmap[i]))
  128. if ((j=find_first_zero(bh->b_data))<8192)
  129. break;
  130. if (i>=8 || !bh || j>=8192)
  131. return 0;
  132. // 接着设置找到的新逻辑块j对应逻辑块位图中的bit位。若对应bit位已经置位,则出错
  133. // 停机。否则置存放位图的对应缓冲区块已修改标志。因为逻辑块位图仅表示盘上数据区
  134. // 中逻辑块的占用情况,则逻辑块位图中bit位偏移值表示从数据区开始处算起的块号,
  135. // 因此这里需要加上数据区第1个逻辑块的块号,把j转换成逻辑块号。此时如果新逻辑块
  136. // 大于该设备上的总逻辑块数,则说明指定逻辑块在对应设备上不存在。申请失败,返回0退出。
  137. if (set_bit(j,bh->b_data))
  138. panic("new_block: bit already set");
  139. bh->b_dirt = 1;
  140. j += i*8192 + sb->s_firstdatazone-1;
  141. if (j >= sb->s_nzones)
  142. return 0;
  143. // 然后在高速缓冲区中为该设备上指定的逻辑块号取得一个缓冲块,并返回缓冲块头指针。
  144. // 因为刚取得的逻辑块其引用次数一定为1(getblk()中会设置),因此若不为1则停机。
  145. // 最后将新逻辑块清零,并设置其已更新标志和已修改标志。然后释放对应缓冲块,返回
  146. // 逻辑块号。
  147. if (!(bh=getblk(dev,j)))
  148. panic("new_block: cannot get block");
  149. if (bh->b_count != 1)
  150. panic("new block: count is != 1");
  151. clear_block(bh->b_data);
  152. bh->b_uptodate = 1;
  153. bh->b_dirt = 1;
  154. brelse(bh); // why?
  155. return j;
  156. }
  157. //// 释放指定的i节点
  158. // 该函数首先判断参数给出的i节点号的有效性和可释放性。若i节点仍然在使用中则不能
  159. // 被释放。然后利用超级块信息对i节点位图进行操作,复位i节点号对应的i节点位图中
  160. // bit位,并清空i节点结构。
  161. void free_inode(struct m_inode * inode)
  162. {
  163. struct super_block * sb;
  164. struct buffer_head * bh;
  165. // 首先判断参数给出的需要释放的i节点有效性或合法性。如果i节点指针=NULL,则
  166. // 退出。如果i节点上的设备号字段为0,则说明该节点没有使用。于是用0清空对应i
  167. // 节点所占内存区并返回。memset()定义在include/string.h中,这里表示用0填写
  168. // inode指针指定处、长度是sizeof(*inode)的内存快。
  169. if (!inode)
  170. return;
  171. // why not using inode->i_count or inode->i_nlinks ????????
  172. if (!inode->i_dev) {
  173. memset(inode,0,sizeof(*inode));
  174. return;
  175. }
  176. // 如果此i节点还有其他程序引用,则不能释放,说明内核有问题,停机。如果文件
  177. // 连接数不为0,则表示还有其他文件目录项在使用该节点,因此也不应释放,而应该放回等。
  178. if (inode->i_count>1) {
  179. printk("trying to free inode with count=%d\n",inode->i_count);
  180. panic("free_inode");
  181. }
  182. if (inode->i_nlinks)
  183. panic("trying to free inode with links");
  184. // 在判断完i节点的合理性之后,我们开始利用超级块信息对其中的i节点位图进行
  185. // 操作。首先取i节点所在设备的超级块,测试设备是否存在。然后判断i节点号的
  186. // 范围是否正确,因为一个缓冲块的i节点位图有8192 bit。因此i_num>>13(即
  187. // i_num/8192)可以得到当前i节点所在的s_imap[]项,即所在盘块。
  188. // 合法性判断
  189. if (!(sb = get_super(inode->i_dev)))
  190. panic("trying to free inode on nonexistent device");
  191. if (inode->i_num < 1 || inode->i_num > sb->s_ninodes)
  192. panic("trying to free inode 0 or nonexistant inode");
  193. if (!(bh=sb->s_imap[inode->i_num>>13]))
  194. panic("nonexistent imap in superblock");
  195. // 现在我们复位i节点对应的节点位图中的bit位。如果该bit位已经等于0,则显示
  196. // 出错警告信息。最后置i节点位图所在缓冲区已修改标志,并清空该i节点结构
  197. // 所占内存区。
  198. if (clear_bit(inode->i_num&8191,bh->b_data))
  199. printk("free_inode: bit already cleared.\n\r");
  200. bh->b_dirt = 1;
  201. memset(inode,0,sizeof(*inode));
  202. }
  203. //// 为设备dev建立一个新i节点。初始化并返回该新i节点的指针。
  204. // 在内存i节点表中获取一个空闲i节点表项,并从i节点位图中找一个空闲i节点。
  205. struct m_inode * new_inode(int dev)
  206. {
  207. struct m_inode * inode;
  208. struct super_block * sb;
  209. struct buffer_head * bh;
  210. int i,j;
  211. // 首先从内存i节点表(inode_table)中获取一个空闲i节点项,并读取指定设备的
  212. // 超级块结构。然后扫描超级块中8块i节点位图,寻找首个0bit位,寻找空闲节点,
  213. // 获取放置该i节点的节点号。如果全部扫描完还没找到,或者位图所在的缓冲块无效
  214. // (bh=NULL),则放回先前申请的i节点表中的i节点,并返回NULL退出(没有空闲的i节点)。
  215. if (!(inode=get_empty_inode()))
  216. return NULL;
  217. if (!(sb = get_super(dev)))
  218. panic("new_inode with unknown device");
  219. j = 8192;
  220. for (i=0 ; i<8 ; i++)
  221. if ((bh=sb->s_imap[i]))
  222. if ((j=find_first_zero(bh->b_data))<8192)
  223. break;
  224. if (!bh || j >= 8192 || j+i*8192 > sb->s_ninodes) {
  225. iput(inode); // 放回之前申请的i节点表中的i节点
  226. return NULL;
  227. }
  228. // 现在我们已经找到了还未使用的i节点号j。于是置位i节点j对应的i节点位图相应bit位。
  229. // 然后置i节点位图所在缓冲块已修改标志。最后初始化该i节点结构(i_ctime是i节点内容
  230. // 改变时间)。
  231. if (set_bit(j,bh->b_data))
  232. panic("new_inode: bit already set");
  233. bh->b_dirt = 1;
  234. inode->i_count=1; // 引用计数
  235. inode->i_nlinks=1; // 文件目录项连接数
  236. inode->i_dev=dev; // i节点所在的设备号
  237. inode->i_uid=current->euid; // i节点所属用户ID
  238. inode->i_gid=current->egid; // 组id
  239. inode->i_dirt=1; // 已修改标志置位
  240. inode->i_num = j + i*8192; // 对应设备中的i节点号
  241. inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
  242. return inode;
  243. }

问题

问题 解答
new_block()申请了cache之后,为什么要释放cache? 已经在stackoverflow上询问
clear_bit(),set_bit()函数中32bit汇编的指令 学习中
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注