/*
* linux/fs/bitmap.c
*
* (C) 1991 Linus Torvalds
*/
/* bitmap.c contains the code that handles the inode and block bitmaps */
#include <string.h>
#include <linux/sched.h>
#include <linux/kernel.h> // 一些内核常用函数的原形定义
/* 将指定地址(addr)处的一块内存清零 */
#define clear_block(addr) \
__asm__("cld\n\t" \
"rep\n\t" \
"stosl" \
::"a" (0),"c" (BLOCK_SIZE/4),"D" ((long) (addr)):"cx","di")
/* 置位指定地址开始的第nr 个位偏移处的比特位 */
#define set_bit(nr,addr) ({\
register int res __asm__("ax"); \
__asm__ __volatile__("btsl %2,%3\n\tsetb %%al": \
"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \
res;})
/* 复位指定地址addr开始的第nr 位偏移处的比特位 */
#define clear_bit(nr,addr) ({\
register int res __asm__("ax"); \
__asm__ __volatile__("btrl %2,%3\n\tsetnb %%al": \
"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \
res;})
/* 从addr 开始寻找第1 个0 值比特位,并将其距离addr 的比特位偏移值返回 */
#define find_first_zero(addr) ({ \
int __res; \
__asm__("cld\n" \
"1:\tlodsl\n\t" \
"notl %%eax\n\t" \
"bsfl %%eax,%%edx\n\t" \
"je 2f\n\t" \
"addl %%edx,%%ecx\n\t" \
"jmp 3f\n" \
"2:\taddl $32,%%ecx\n\t" \
"cmpl $8192,%%ecx\n\t" \
"jl 1b\n" \
"3:" \
:"=c" (__res):"c" (0),"S" (addr):"ax","dx","si"); \
__res;})
/* 复位指定逻辑块block (相对于磁盘而言)的逻辑块位图比特位 */
void free_block(int dev, int block)
{
struct super_block * sb;
struct buffer_head * bh;
if (!(sb = get_super(dev))) // 取指定设备dev 的超级块
panic("trying to free block on nonexistent device"); // 设备不存在
// 若逻辑块号小于首个逻辑块号或者大于设备上总逻辑块数
if (block < sb->s_firstdatazone || block >= sb->s_nzones)
panic("trying to free block not in datazone");
bh = get_hash_table(dev,block); // 从hash 表中寻找该块数据
if (bh) // 合法?
{
if (bh->b_count != 1) // 只有该进程占据block
{
printk("trying to free block (%04x:%d), count=%d\n",
dev,block,bh->b_count);
return;
}
// 清已修改和更新标志
bh->b_dirt=0;
bh->b_uptodate=0;
brelse(bh);
}
/*
* 磁盘的结构如下:
* |---|----|----|-----------------------* * *----------|
* | | | |
* 引导块超级块i节点 数据
* 在磁盘被mount上之后,linux会首先读取super block,填充super
* block,而super block就是整个磁盘信息的缩影。系统在需要磁盘
* 操作时,首先需要参考该结构。在函数read_super中读取磁盘上的
* super block。注意的是磁盘上的i节点位图和逻辑块位图都加在到
* 内存中的缓冲区中(是由buffer.c来维护)
*
*/
/*
* 到目前为止,应该对超级块,i节点,逻辑块,缓冲块有了一定的
* 认识了。设备上的物理部分都有内存的表示,比如超级块在内存中
* 有超级块数组 super_block[NR_SUPER],i节点和逻辑块位图有s_imap
* [8]和s_zmap[8]指向的缓冲区表示,i节点在内存中有i节点数组
* inode_table[NR_INODE],逻辑块有缓冲区中的缓冲块。分配inode和
* 逻辑块只需要分配内存中保留的空闲块,并设置对应位图和修改位,
* 内核会在适当时间刷新到设备上。inode只是在读取该文件时读取该
* inode到内存中去
*/
// 计算block 在数据区开始算起的数据逻辑块号(从1 开始计数)
block -= sb->s_firstdatazone - 1 ;
// 对逻辑块(区块)位图进行操作
if (clear_bit(block&8191,sb->s_zmap[block/8192]->b_data))
{
printk("block (%04x:%d) ",dev,block+sb->s_firstdatazone-1);
panic("free_block: bit already cleared");
}
// 置相应逻辑块位图所在缓冲区已修改标志
sb->s_zmap[block/8192]->b_dirt = 1;
}
/* 向设备dev 申请一个逻辑块(盘块,区块)。返回逻辑块号(盘块号) */
int new_block(int dev)
{
struct buffer_head * bh;
struct super_block * sb;
int i,j;
if (!(sb = get_super(dev))) // 从设备dev 取超级块
panic("trying to get new block from nonexistant device");
j = 8192;
/*
* block map
* /- b_data
* buffer_head[8] -- * * *
* \- b_data
*/
for (i=0 ; i<8 ; i++)
if (bh=sb->s_zmap[i])
if ((j=find_first_zero(bh->b_data))<8192)
break;
// 全部扫描完还没找到)或者位图所在的缓冲块无效
if (i>=8 || !bh || j>=8192)
return 0;
if (set_bit(j,bh->b_data)) // 逻辑块对应逻辑块位图中的比特位置位
panic("new_block: bit already set");
bh->b_dirt = 1; // 置对应缓冲区块的已修改标志
j += i*8192 + sb->s_firstdatazone-1; // 新逻辑块数
if (j >= sb->s_nzones) // 新逻辑块大于该设备上的总逻辑块数
return 0; // 指定逻辑块在对应设备上不存在
// 读取设备上的该新逻辑块(相对于软盘的)数据(验证)
if (!(bh=getblk(dev,j)))
panic("new_block: cannot get block");
// 新块的引用计数应为1
if (bh->b_count != 1)
panic("new block: count is != 1");
// 将该新逻辑块清零,并置位更新标志和已修改标志
clear_block(bh->b_data);
bh->b_uptodate = 1;
bh->b_dirt = 1;
// 释放对应缓冲区
brelse(bh);
// 逻辑块号
return j;
}
/* 释放指定的i 节点 */
void free_inode(struct m_inode * inode)
{
struct super_block * sb;
struct buffer_head * bh;
if (!inode) // 如果i 节点指针=NULL?
return;
if (!inode->i_dev) // 该节点无用
{
memset(inode,0,sizeof(*inode)); // 用0 清空对应i 节点所占内存区
return;
}
if (inode->i_count>1) // 此i 节点还有其它程序引用,则不能释放
{
printk("trying to free inode with count=%d\n",inode->i_count);
panic("free_inode");
}
// 如果文件目录项连接数不为0,则表示还有其它文件目录项在使用该节点
if (inode->i_nlinks)
panic("trying to free inode with links"); // 不应释放
if (!(sb = get_super(inode->i_dev))) // 取i 节点所在设备的超级块
panic("trying to free inode on nonexistent device"); // 设备不存在
// // 如果i 节点号=0 或大于该设备上i 节点总数
if (inode->i_num < 1 || inode->i_num > sb->s_ninodes)
panic("trying to free inode 0 or nonexistant inode");
// 如果该i 节点对应的节点位图不存在,则出错
if (!(bh=sb->s_imap[inode->i_num>>13]))
panic("nonexistent imap in superblock");
// 复位i 节点对应的节点位图中的比特位
if (clear_bit(inode->i_num&8191,bh->b_data))
// 该比特位已经等于0,则出错
printk("free_inode: bit already cleared.\n\r");
bh->b_dirt = 1; // 置i 节点位图所在缓冲区已修改标志
memset(inode,0,sizeof(*inode)); // 清空该i 节点结构所占内存区
}
/* 为设备dev 建立一个新i 节点。返回该新i 节点的指针 */
struct m_inode * new_inode(int dev)
{
struct m_inode * inode;
struct super_block * sb;
struct buffer_head * bh;
int i,j;
// // 从内存i 节点表(inode_table)中获取一个空闲i 节点项(inode数组中寻找)
if (!(inode=get_empty_inode()))
return NULL;
if (!(sb = get_super(dev))) // 读取指定设备的超级块结构
panic("new_inode with unknown device");
// 扫描i 节点位图,寻找首个0 比特位
j = 8192;
for (i=0 ; i<8 ; i++)
if (bh=sb->s_imap[i])
if ((j=find_first_zero(bh->b_data))<8192)
break;
// // 如果全部扫描完还没找到,或者位图所在的缓冲块无效(bh=NULL)则 返回0,退出
if (!bh || j >= 8192 || j+i*8192 > sb->s_ninodes)
{
iput(inode);
return NULL;
}
if (set_bit(j,bh->b_data)) // 置位对应新i 节点的i 节点位图相应比特位
panic("new_inode: bit already set");
bh->b_dirt = 1; // // 置i 节点位图所在缓冲区已修改标志
// // 初始化该i 节点结构(内存数组中的结构)
inode->i_count=1;
inode->i_nlinks=1;
inode->i_dev=dev;
inode->i_uid=current->euid;
inode->i_gid=current->egid;
inode->i_dirt=1;
inode->i_num = j + i*8192;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
return inode; // // 返回该i 节点指针
}
/*
* 该文件主要是负责维护bitmap位图。文件主要实现的是四个函数
* free_block,new_block,new_inode,free_inode。函数free_block
* 主要实现的是对于逻辑块的位图指定位复位,由于修改了内存中的
* 逻辑块位图所占据的内存区域,所以需要将该区域设置成ditry。其
* 他的几个函数同理。
*/
参考《linux内核完全注释》和网上相关文章