您的位置:首页 > 运维架构 > Linux

linux 0.11 内核学习 -- bitmap.c

2010-02-16 09:10 411 查看
/*

* 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内核完全注释》和网上相关文章
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: