linux 0.12 内核学习 (bitmap.c/ truncate.c)

728 阅读5分钟

简介

bitmap.c 位于 fs/bitmap.c路径下,其包含函数的主要作用是用于维护inode和block的数据结构。

代码浏览

1)bitmap.c

clear_bit / set_bit

该部分的汇编函数是通过汇编指令test and set指令进行并发控制

/**
 ** 备注:
 **     btsl指令: 将基地址%3 和 偏移量 %2 所对应的位存放到进位标识位CF上,然后设置该位为1.
 **     setb指令: 将CF对应的值复制到对应寄存器上。
 ** 思考:
 **     通过该两个指令可以实现一个同步的机制,比如while(set_bit(nr, addr))或if(!set_bit(nr, addr));
 **     该过程保证某一时刻只有一个进程可以通过该判断,其他进程可以继续等待或者跳过。
 **
 **/
 
#define set_bit(nr,addr) ({\
register int res __asm__("ax"); \
__asm__("btsl %2,%3\n\tsetb %%al":"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \
res;})

#define clear_bit(nr,addr) ({\
register int res __asm__("ax"); \
__asm__("btrl %2,%3\n\tsetnb %%al":"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \
res;})

find_first_zero

该函数非常常用,因为linux文件系统包含了inode位图和block位图,通过位图的index去找到对应的位图结构体,通过位图结构体找到相应的块内容。

可以看出以下代码,通过bsfl对eax寄存器进行对比获取第一位值,如果找不到,则跳转ecx并继续尝试。


#define find_first_zero(addr) ({ \
int __res; \
__asm__("cld\n" \   // 清方向位
	"1:\tlodsl\n\t" \  // 取[esi] --> eax
	"notl %%eax\n\t" \  // 将eax中每位都取反
	"bsfl %%eax,%%edx\n\t" \  // 从0位开始扫描扫描eax中是1的第一位,其偏移量 --> edx
	"je 2f\n\t" \ // 加入eax为0,则跳转到2的位置
	"addl %%edx,%%ecx\n\t" \ // 偏移量加上ecx (ecx为第一个0值位的偏移值)
	"jmp 3f\n" \ // 跳转到3位置
	"2:\taddl $32,%%ecx\n\t" \ // ecx+1个长字节大小
	"cmpl $8192,%%ecx\n\t" \ // 是否已经扫描了8192位了
	"jl 1b\n" \ // 若没有扫描完则跳回1位置
	"3:" \ 
	:"=c" (__res):"c" (0),"S" (addr):"ax","dx","si"); \
__res;})

block块的操作 (free/new)

block bitmap大小为8个8192位大小的数组。

void free_block(int dev, int block)
{
	struct super_block * sb;
	struct buffer_head * bh;
    
    // first find super_block 
	if (!(sb = get_super(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");
		
	// find dev^block ==> bh
	bh = get_hash_table(dev,block);
	if (bh) {
		if (bh->b_count != 1) {
			printk("trying to free block (%04x:%d), count=%d\n",
				dev,block,bh->b_count);
			return;
		}
		bh->b_dirt=0;
		bh->b_uptodate=0;
		// clear bh content and put it dirty
		brelse(bh);
	}
	block -= sb->s_firstdatazone - 1 ;
	
	// clear bitmap table and set block unused.
	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");
	}
	// 设置block bitmap块为dirty
	sb->s_zmap[block/8192]->b_dirt = 1;
}


int new_block(int dev)
{
	struct buffer_head * bh;
	struct super_block * sb;
	int i,j;

	if (!(sb = get_super(dev)))
		panic("trying to get new block from nonexistant device");
	j = 8192;
	
	// 遍历8次的8192位信息,找到合适的一个位
	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;
		
	// 设置block使用位
	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;
		
	// 找到j对应的block块,清空block内容,不为1则停机
	if (!(bh=getblk(dev,j)))
		panic("new_block: cannot get block");
	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;
}

inode块操作 (free/new)

void free_inode(struct m_inode * inode)
{
	struct super_block * sb;
	struct buffer_head * bh;

	if (!inode)
		return;
	if (!inode->i_dev) {
		memset(inode,0,sizeof(*inode));
		return;
	}
	if (inode->i_count>1) {
		printk("trying to free inode with count=%d\n",inode->i_count);
		panic("free_inode");
	}
	if (inode->i_nlinks)
		panic("trying to free inode with links");
	if (!(sb = get_super(inode->i_dev)))
		panic("trying to free inode on nonexistent device");
	if (inode->i_num < 1 || inode->i_num > sb->s_ninodes)
		panic("trying to free inode 0 or nonexistant inode");
	
	/* 
	**  inode 对应的i_num为inode位图对应位置的索引号,进行右移获取对应的map索引
	*/
	if (!(bh=sb->s_imap[inode->i_num>>13]))
		panic("nonexistent imap in superblock");
	if (clear_bit(inode->i_num&8191,bh->b_data))
		panic("free_inode: bit already cleared");
	bh->b_dirt = 1;
	memset(inode,0,sizeof(*inode));
}


struct m_inode * new_inode(int dev)
{
	struct m_inode * inode;
	struct super_block * sb;
	struct buffer_head * bh;
	int i,j;
    
    // 创建一个空白的inode结构
	if (!(inode=get_empty_inode()))
		return NULL;
	if (!(sb = get_super(dev)))
		panic("new_inode with unknown device");
	
	// 查找一个空闲的inode位图位置
	j = 8192;
	for (i=0 ; i<8 ; i++)
		if (bh=sb->s_imap[i])
			if ((j=find_first_zero(bh->b_data))<8192)
				break;
	if (!bh || j >= 8192 || j+i*8192 > sb->s_ninodes) {
		iput(inode);
		return NULL;
	}
	if (set_bit(j,bh->b_data))
		panic("new_inode: bit already set");
	bh->b_dirt = 1;
	inode->i_count=1;
	inode->i_nlinks=1;
	inode->i_dev=dev;
	inode->i_dirt=1;
	
	// 为inode设置i_num 以及相关属性
	inode->i_num = j + i*8192;
	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
	return inode;
}
  1. truncate.c

truncate.c 包含三个函数,该c文件的作用是清空inode指向的i_zone空间,清除分为三部,一步是清除直接block,一步是清除一级间接block,一步是清除二级间接block

void truncate(struct m_inode * inode)
{
	int i;

	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
		return;
	//遍历前6个直接block,调用free_block清除位图上对应的位数
	for (i=0;i<7;i++)
		if (inode->i_zone[i]) {
			free_block(inode->i_dev,inode->i_zone[i]);
			inode->i_zone[i]=0;
		}
	// 清除间接block
	free_ind(inode->i_dev,inode->i_zone[7]);
	free_dind(inode->i_dev,inode->i_zone[8]);
	inode->i_zone[7] = inode->i_zone[8] = 0;
	inode->i_size = 0;
	inode->i_dirt = 1;
	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
}


static void free_ind(int dev,int block)
{
	struct buffer_head * bh;
	unsigned short * p;
	int i;

	if (!block)
		return;
	
	// 读取间接块指向的内容到buffer_head上,假设高速缓存中不存在该缓存,则其会调用驱动读取文件并复制到对应的b_data上。
	if (bh=bread(dev,block)) {
	
	    // 获取buffer_head的b_data,并根据16位大小进行读取block号
		p = (unsigned short *) bh->b_data;
		for (i=0;i<512;i++,p++)
			if (*p)
				free_block(dev,*p);
		brelse(bh);
	}
	free_block(dev,block);
}

static void free_dind(int dev,int block)
{
	struct buffer_head * bh;
	unsigned short * p;
	int i;

	if (!block)
		return;
	if (bh=bread(dev,block)) {
		p = (unsigned short *) bh->b_data;
		for (i=0;i<512;i++,p++)
			if (*p)
				free_ind(dev,*p);
		brelse(bh);
	}
	free_block(dev,block);
}