文件与磁盘的映射结构
了解文件与磁盘是如何构建关系
深刻理解文件系统的工作方法
Linux中对文件的操作有更深的认识与方法
软件工程中的分区域管理系统 --向上设计模型 (模块化)
i节点位图中的一个bit对应一个节点 1024*8=8191个i节点 因为i节点位图的0位是不用的
逻辑块位图中的一个bit对应一个逻辑块
所有的块大小都固定,盘块 磁盘块
扇区:是一个长度为512B的数据块
在不同的文件系统中 扇区和盘块对应关系是不同的
2个扇区 对应一个盘块 1024B MINIX
4个扇区 对应一个盘块 2048B
引导块没什么用但必须有
一个设备最大的文件系统为多少M?
1024 * 8 * 1k = 8M
8M * 8 = 64M
超级块的结构体
struct super_block {
unsigned short s_ninodes; //inode节点个数
unsigned short s_nzones; //逻辑块数
unsigned short s_imap_blocks; //i节点位图块数
unsigned short s_zmap_blocks; //逻辑块位图块数
unsigned short s_firstdatazone; //第一个逻辑块号 逻辑块号是从第一个引导块开始计数的
unsigned short s_log_zone_size; //暂时不管
unsigned long s_max_size; //最大文件长度
unsigned short s_magic; //幻数
/* These are only in memory */
struct buffer_head * s_imap[8]; //i节点位图在高速缓冲区块指针数组
struct buffer_head * s_zmap[8]; //逻辑块位图在高速缓冲区块指针数组
unsigned short s_dev; //设备号
struct m_inode * s_isup; //根目录的i节点
struct m_inode * s_imount; //安装i节点
unsigned long s_time; //修改时间
struct task_struct * s_wait;
unsigned char s_lock;
unsigned char s_rd_only;
unsigned char s_dirt;
};
不管读取什么磁盘上的资源,都是先getblk(获取该资源对应的设备和块号的高速缓冲区)
然后bread的流程(确认有效数据的高速缓冲区)
最后进行区域内存的拷贝(从bh的b_date数据区域拷贝到要用的数据的内存中)
bitmap.c
操作逻辑块位图、i节点位图(销毁、创建、查找)
然后来看一看
#define clear_block(addr) \
__asm__("cld\n\t" \
"rep\n\t" \
"stosl" \
::"a" (0),"c" (BLOCK_SIZE/4),"D" ((long) (addr)):"cx","di")
嵌入式汇编宏
是非常非常高效的代码
清空给定地址的一块内存区域(块1024B)
#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;})
将指定地址的第nr个bit位置一
#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;})
清空指定地址的第nr个bit位
#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;})
着地第一个为0的地址并且返回地址的偏移量
找空闲的i节点或者逻辑块,然后返回偏移量
接下来四个函数 释放、申请逻辑块位图、inode节点位图
void free_block(int dev, int block) //释放逻辑块位图
{ //设备号、块号都得知道
struct super_block * sb;
struct buffer_head * bh;
if (!(sb = get_super(dev))) //get_super
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);
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;
brelse(bh);
}
//计算当前块是第几个数据块
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;
//对应的该块所在的逻辑块位图块的高速缓冲区
//逻辑块位图对应的块
}
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;
//在该设备的所有逻辑块位图中找到第一个为0的逻辑块位图空位
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)) //bh是找到的有空位的逻辑块对应的高速缓冲区的地址 然后data就是对应的数据的地址
panic("new_block: bit already set");
bh->b_dirt = 1; //修改更新标志置为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");
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;
}
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");
if (!(bh=sb->s_imap[inode->i_num>>13]))
panic("nonexistent imap in superblock");
if (clear_bit(inode->i_num&8191,bh->b_data))
printk("free_inode: bit already cleared.\n\r");
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;
if (!(inode=get_empty_inode()))
return NULL;
if (!(sb = get_super(dev)))
panic("new_inode with unknown device");
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_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;
}
free_block 释放块
get_super 通过设备号找出超级块,获得里面的信息
判断块号是不是在范围内
然后读取块信息,就把高速缓冲区拿出来
没用就释放掉逻辑块对应高速缓冲区
计算当前块号,然后区清空逻辑块位图上面对应的bit
逻辑块位图对应的块设为1
new_block 新建一个块的函数
获得了一个超级块
for循环的8指的是我们的逻辑块位图
找到第一个为0的位的位数
然后修改数据 修改标志啥的
然后得到block块号
然后我们需要给这个块申请高速缓冲区 getblk
清空这个block
然后给这个高速缓冲区设置各种标志
free_inode 释放指定i节点
把i节点对应的i节点位图中的bit置0
清空i节点位图信息
new_inode 创建一个新的i节点,返回该i节点的指针
get_empty_inode
通过super_block找到对应的inode信息
通过inode的操作函数找到对应的inode分配内存区
设置inode位图中对应的位为1
返回设置好的inode结构体
给整个的inode节点的管理设置了一个数组
struct m_inode inode_table[NR_INODE]={{0,},};
操作系统中我们有一个超级块数组
mount 把需要挂载的设备文件系统的super_block读到高速缓冲区中并且放到超级块数组中
inode.c主要包括处理inode节点的函数
iget获得inode节点
iput释放inode节点
bmap对文件进行磁盘映射
wait_on_inode
static inline void wait_on_inode(struct m_inode * inode)
{
cli();
while (inode->i_lock)
sleep_on(&inode->i_wait);
sti();
}
因为多线程,这些资源都是可以被共享的,就会有同步的问题,就会有解锁非解锁的问题。
lock_inode unlock_inode
static inline void lock_inode(struct m_inode * inode)
{
cli();
while (inode->i_lock)
sleep_on(&inode->i_wait);
inode->i_lock=1;
sti();
}
//如果锁了就睡眠等待,等待就是等待醒来,等待解锁
//没锁就锁了
static inline void unlock_inode(struct m_inode * inode)
{
inode->i_lock=0;
wake_up(&inode->i_wait);
}
释放所有的i节点 invalidate_inodes
void invalidate_inodes(int dev)
{
int i;
struct m_inode * inode;
inode = 0+inode_table;
for(i=0 ; i<NR_INODE ; i++,inode++) {
wait_on_inode(inode);
if (inode->i_dev == dev) {
if (inode->i_count)
printk("inode in use on removed disk\n\r");
inode->i_dev = inode->i_dirt = 0;
}
}
}
同步i节点
void sync_inodes(void)
{
int i;
struct m_inode * inode;
inode = 0+inode_table;
for(i=0 ; i<NR_INODE ; i++,inode++) {
wait_on_inode(inode);
if (inode->i_dirt && !inode->i_pipe)
write_inode(inode);
}
}
inode->i_pipe
证明文件是一个pipe管道
pipe文件会
write_inode
static void write_inode(struct m_inode * inode)
{
struct super_block * sb;
struct buffer_head * bh;
int block;
lock_inode(inode); //锁定
if (!inode->i_dirt || !inode->i_dev) {
unlock_inode(inode);
return;
}
if (!(sb=get_super(inode->i_dev)))
panic("trying to write inode without device");
//计算当前inode节点的逻辑块号
block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks +
(inode->i_num-1)/INODES_PER_BLOCK;
if (!(bh=bread(inode->i_dev,block)))
panic("unable to read i-node block");
((struct d_inode *)bh->b_data)
[(inode->i_num-1)%INODES_PER_BLOCK] =
*(struct d_inode *)inode; //一段诡异的赋值方式
bh->b_dirt=1;
inode->i_dirt=0;
brelse(bh);
unlock_inode(inode);
}
从磁盘周哥读写数据信息 inode等的流程
1、找到指定的dev
2、通过dev找到设备的super block
2、通过sb中的信息己算要读的块号
4、掉哦用bread将其读取或写入到高速缓冲区
5、读:将高速缓冲物的b_data读到你要读的内存地址 释放高速缓冲区
写:将要写入的数据写入高速缓冲区的b_data,并设置dirt修改标志位 等待系统的sys_syn进行写盘 释放高速缓冲区
struct m_inode * get_empty_inode(void)
{
struct m_inode * inode;
static struct m_inode * last_inode = inode_table;
int i;
do {
inode = NULL;
for (i = NR_INODE; i ; i--) {
if (++last_inode >= inode_table + NR_INODE)
last_inode = inode_table;
if (!last_inode->i_count) {
inode = last_inode;
if (!inode->i_dirt && !inode->i_lock)
break;
}
}
if (!inode) {
for (i=0 ; i<NR_INODE ; i++)
printk("%04x: %6d\t",inode_table[i].i_dev,
inode_table[i].i_num);
panic("No free inodes in mem");
}
wait_on_inode(inode);
while (inode->i_dirt) {
write_inode(inode);
wait_on_inode(inode);
}
} while (inode->i_count);
memset(inode,0,sizeof(*inode));
inode->i_count = 1;
return inode;
}
read_inode
static void read_inode(struct m_inode * inode) //参数是一块内存
{
struct super_block * sb;
struct buffer_head * bh;
int block;
lock_inode(inode);
if (!(sb=get_super(inode->i_dev))) //还是要拿到超级块才能怎么怎么滴
panic("trying to read inode without dev");
block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks +
(inode->i_num-1)/INODES_PER_BLOCK;
if (!(bh=bread(inode->i_dev,block)))
panic("unable to read i-node block");
*(struct d_inode *)inode =
((struct d_inode *)bh->b_data)
[(inode->i_num-1)%INODES_PER_BLOCK];
brelse(bh);
unlock_inode(inode);
}
read inode读出来的inode节点信息是不包含内存信息的。
bmap函数 块映射
static int _bmap(struct m_inode * inode,int block,int create)
{
struct buffer_head * bh;
int i;
if (block<0)
panic("_bmap: block<0");
if (block >= 7+512+512*512)
panic("_bmap: block>big");
if (block<7) {
if (create && !inode->i_zone[block])
if (inode->i_zone[block]=new_block(inode->i_dev)) {
inode->i_ctime=CURRENT_TIME;
inode->i_dirt=1;
}
return inode->i_zone[block];
}
//文件占用的号大于7,就要用到一次简介块号
block -= 7;
if (block<512) {
if (create && !inode->i_zone[7])
if (inode->i_zone[7]=new_block(inode->i_dev)) {
inode->i_dirt=1;
inode->i_ctime=CURRENT_TIME;
}
if (!inode->i_zone[7])
return 0;
if (!(bh = bread(inode->i_dev,inode->i_zone[7])))
return 0;
i = ((unsigned short *) (bh->b_data))[block];
if (create && !i)
if (i=new_block(inode->i_dev)) {
((unsigned short *) (bh->b_data))[block]=i;
bh->b_dirt=1;
}
brelse(bh);
return i;
}
block -= 512;
if (create && !inode->i_zone[8])
if (inode->i_zone[8]=new_block(inode->i_dev)) {
inode->i_dirt=1;
inode->i_ctime=CURRENT_TIME;
}
if (!inode->i_zone[8])
return 0;
if (!(bh=bread(inode->i_dev,inode->i_zone[8])))
return 0;
i = ((unsigned short *)bh->b_data)[block>>9];
if (create && !i)
if (i=new_block(inode->i_dev)) {
((unsigned short *) (bh->b_data))[block>>9]=i;
bh->b_dirt=1;
}
brelse(bh);
if (!i)
return 0;
if (!(bh=bread(inode->i_dev,i)))
return 0;
i = ((unsigned short *)bh->b_data)[block&511];
if (create && !i)
if (i=new_block(inode->i_dev)) {
((unsigned short *) (bh->b_data))[block&511]=i;
bh->b_dirt=1;
}
brelse(bh);
return i;
}
首先看一下这个函数是干嘛的
进行inode节点的磁盘块映射的
在对m_inode结构体的i_zone[9],磁盘映射变量进行赋值。
如果已有磁盘信息进行赋值即可
如果没有磁盘信息则分配新的逻辑块,然后进行赋值
iput 释放一个inode节点
iget 获得一个inode节点
get_pipe_inode
struct m_inode * get_pipe_inode(void)
{
struct m_inode * inode;
if (!(inode = get_empty_inode()))
return NULL;
//为当前inode节点分配一个内存页做为pipe
if (!(inode->i_size=get_free_page())) {
inode->i_count = 0;
return NULL;
}
//又读又写 count就是操作数
inode->i_count = 2; /* sum of readers/writers */
PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0;
inode->i_pipe = 1;
return inode;
}
icount++就是这个文件在用,我们文件关闭不了问题就在这里 count不为0.
1、对设备的超级块进行操作(获取 读取 释放)
get_super put_super read_super
2、因为超级块是设备的映射(代码中的类),超级块的操作关系到设备的文件系统的操作
文件系统的加载、卸载 mount umoint
sys_mount sys_umount
3、根文件系统的加载(/) 调用了一个mount_root函数 就在super.c里面
set_bit(bitnr,addr) ({ \
register int __res __asm__("ax"); \
__asm__("bt %2,%3;setb %%al":"=a" (__res):"a" (0),"r" (bitnr),"m" (*(addr))); \
__res; })
struct super_block super_block[NR_SUPER];
所有超级块的数组
static void lock_super(struct super_block * sb)
{
cli();
while (sb->s_lock)
sleep_on(&(sb->s_wait));
sb->s_lock = 1;
sti();
}
static void free_super(struct super_block * sb)
{
cli();
sb->s_lock = 0;
wake_up(&(sb->s_wait));
sti();
}
static void wait_on_super(struct super_block * sb)
{
cli();
while (sb->s_lock)
sleep_on(&(sb->s_wait));
sti();
}
多线程
get_super 获取指定dev的超级块
从全局的已挂载的所有文件系统设备中找到对应设备的超级块
struct super_block * get_super(int dev)
{
struct super_block * s;
if (!dev)
return NULL;
s = 0+super_block;
while (s < NR_SUPER+super_block)
if (s->s_dev == dev) {
wait_on_super(s);
if (s->s_dev == dev)
return s;
s = 0+super_block;
} else
s++;
return NULL;
}
put_super
1、锁定块并清控设备号
2、清空其i节点位图和逻辑块位图所占用的高速缓冲区
3、解锁该块,并唤醒等待该资源(系统super_bolck数组的空槽)的进程队列
void put_super(int dev)
{
struct super_block * sb;
struct m_inode * inode;
int i;
if (dev == ROOT_DEV) {
printk("root diskette changed: prepare for armageddon\n\r");
return;
}
if (!(sb = get_super(dev)))
return;
if (sb->s_imount) {
printk("Mounted disk changed - tssk, tssk\n\r");
return;
}
lock_super(sb);
sb->s_dev = 0;
for(i=0;i<I_MAP_SLOTS;i++)
brelse(sb->s_imap[i]);
for(i=0;i<Z_MAP_SLOTS;i++)
brelse(sb->s_zmap[i]);
free_super(sb);
return;
}
read_super
跟设备去做交互
读取指定设备的文件系统
static struct super_block * read_super(int dev)
{
struct super_block * s;
struct buffer_head * bh;
int i,block;
if (!dev)
return NULL;
check_disk_change(dev);
if (s = get_super(dev))
return s;
//找对应的super_block数组中的空槽
for (s = 0+super_block ;; s++) {
if (s >= NR_SUPER+super_block)
return NULL;
if (!s->s_dev)
break;
}
//设置超级块在内存中的一些动态配置项
//超级块有一些东西只在内存中出现 有些事在物理设备中被写死的
s->s_dev = dev;
s->s_isup = NULL;
s->s_imount = NULL;
s->s_time = 0;
s->s_rd_only = 0;
s->s_dirt = 0;
lock_super(s);
if (!(bh = bread(dev,1))) {
s->s_dev=0;
free_super(s);
return NULL;
}
//设置超级块中固有的超级块配置参数
*((struct d_super_block *) s) =
*((struct d_super_block *) bh->b_data);
brelse(bh);
//检测文件系统的ID号,如果不支持该文件系统则释放并返回
if (s->s_magic != SUPER_MAGIC) {
s->s_dev = 0;
free_super(s);
return NULL;
}
for (i=0;i<I_MAP_SLOTS;i++)
s->s_imap[i] = NULL;
for (i=0;i<Z_MAP_SLOTS;i++)
s->s_zmap[i] = NULL;
block=2; //根据取出的超级块信息分配该设备文件设备的i节点位图
for (i=0 ; i < s->s_imap_blocks ; i++)
if (s->s_imap[i]=bread(dev,block))
block++;
else
break;
for (i=0 ; i < s->s_zmap_blocks ; i++)
if (s->s_zmap[i]=bread(dev,block))
block++;
else
break;
//出错就还原一切
if (block != 2+s->s_imap_blocks+s->s_zmap_blocks) {
for(i=0;i<I_MAP_SLOTS;i++)
brelse(s->s_imap[i]);
for(i=0;i<Z_MAP_SLOTS;i++)
brelse(s->s_zmap[i]);
s->s_dev=0;
free_super(s);
return NULL;
}
s->s_imap[0]->b_data[0] |= 1;
s->s_zmap[0]->b_data[0] |= 1;
//不让用第0个i节点跟逻辑块
free_super(s);
return s;
}
卸载指定设备的文件系统
int sys_umount(char * dev_name)
{
struct m_inode * inode;
struct super_block * sb;
int dev;
if (!(inode=namei(dev_name)))
return -ENOENT;
dev = inode->i_zone[0];
if (!S_ISBLK(inode->i_mode)) {
iput(inode);
return -ENOTBLK;
}
iput(inode);
if (dev==ROOT_DEV)
return -EBUSY;
if (!(sb=get_super(dev)) || !(sb->s_imount))
return -ENOENT;
//s_imount 该文件系统的挂接节点 super_block中
//i_mount 挂接标志 是否有文件系统在该节点挂接 m_inode中
//如果没有被挂接或者读取失败返回错误
if (!sb->s_imount->i_mount)
printk("Mounted inode has i_mount=0\n");
//如果该文件系统的挂接节点的挂接标志位空,返回错误
for (inode=inode_table+0 ; inode<inode_table+NR_INODE ; inode++)
if (inode->i_dev==dev && inode->i_count)
return -EBUSY;
//检索系统的i节点表,如果有进程正在使用该设备上的文件,则返回错误
//就是umount的时候报错就是仅为有东西在使用它
sb->s_imount->i_mount=0;
iput(sb->s_imount);
sb->s_imount = NULL;
iput(sb->s_isup);
sb->s_isup = NULL;
put_super(dev);
sync_dev(dev);
return 0;
}
mount_root
void mount_root(void)
{
int i,free;
struct super_block * p;
struct m_inode * mi;
if (32 != sizeof (struct d_inode))
panic("bad i-node size");
//文件表数组的初始化
for(i=0;i<NR_FILE;i++)
file_table[i].f_count=0;
if (MAJOR(ROOT_DEV) == 2) {
printk("Insert root floppy and press ENTER");
wait_for_keypress();
}
//验证根文件系统是否在软盘上
for(p = &super_block[0] ; p < &super_block[NR_SUPER] ; p++) {
p->s_dev = 0;
p->s_lock = 0;
p->s_wait = NULL;
}
if (!(p=read_super(ROOT_DEV)))
panic("Unable to mount root");
if (!(mi=iget(ROOT_DEV,ROOT_INO)))
panic("Unable to read root i-node");
//给根文件系统的inode节点引用数加3 因为被三个地方用到了
mi->i_count += 3 ; /* NOTE! it is logically used 4 times, not 1 */
p->s_isup = p->s_imount = mi;
current->pwd = mi;
current->root = mi;
free=0;
i=p->s_nzones;
while (-- i >= 0)
//监测逻辑块位图的空闲个数
if (!set_bit(i&8191,p->s_zmap[i>>13]->b_data))
free++;
printk("%d/%d free blocks\n\r",free,p->s_nzones);
free=0;
i=p->s_ninodes+1;
while (-- i >= 0)
if (!set_bit(i&8191,p->s_imap[i>>13]->b_data))
free++;
printk("%d/%d free inodes\n\r",free,p->s_ninodes);
}
sys_set_up调用的根文件的挂载 这是个0号系统调用