/* * linux/fs/inode.c * * (C) 1991 Linus Torvalds */
#include <string.h> #include <sys/stat.h>
#include <linux/sched.h> #include <linux/kernel.h> #include <linux/mm.h> #include <asm/system.h>
struct m_inode inode_table[NR_INODE]={{0,},};// 内存中i 节点表(NR_INODE=32 项)
static void read_inode(struct m_inode * inode);//读节点号的相关信息
static void write_inode(struct m_inode * inode);//写节点号的相关信息到高速缓冲区
static inline void wait_on_inode(struct m_inode * inode)// 等待指定的i 节点可用(解锁)
{ cli();//关中断
while (inode->i_lock)//节点上锁
sleep_on(&inode->i_wait);//将当前任务置为不可中断的等待状态,并将此放入睡眼队列inode->i_wait中
sti();//开中断
}
static inline void lock_inode(struct m_inode * inode)// 对指定的i 节点上锁(锁定指定的i 节点)
{ cli();//关中断
while (inode->i_lock)//节点上锁
sleep_on(&inode->i_wait);//将当前任务置为不可中断的等待状态,并将此放入睡眼队列inode->i_wait中
inode->i_lock=1; sti();//开中断
}
static inline void unlock_inode(struct m_inode * inode)// 对指定的i 节点解锁
{ inode->i_lock=0;//解锁
wake_up(&inode->i_wait);//唤醒inode->i_wait所指向的任务。inode->i_wait为任务等待队列头指针,由于新等待任务是插入等待队列头指针处的,因此唤醒的是最后进入等待队列的任务。
}
void invalidate_inodes(int dev)// 释放内存中设备dev 的所有i 节点,扫描内存中的i 节点表数组,如果是指定设备使用的i 节点就释放之
{ int i; struct m_inode * inode;
inode = 0+inode_table;// 让指针首先指向i 节点表指针数组首项
for(i=0 ; i<NR_INODE ; i++,inode++) { // 扫描i 节点表指针数组中的所有i 节点
wait_on_inode(inode);// 等待该i 节点可用(解锁)
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 节点(置设备号为0 )
} } }
void sync_inodes(void)// 同步所有i 节点,同步内存与设备上的所有i 节点信息
{ int i; struct m_inode * inode;
inode = 0+inode_table;// 让指针首先指向i 节点表指针数组首项
for(i=0 ; i<NR_INODE ; i++,inode++) { wait_on_inode(inode);// 等待指定的i 节点可用(解锁)
if (inode->i_dirt && !inode->i_pipe)// 如果该i 节点已修改且不是管道节点
write_inode(inode);// 则写盘(写入高速缓冲区中)
} }
static int _bmap(struct m_inode * inode,int block,int create)// 把指定 的文件数据块block对应到设备上逻辑块上,文件数据块映射到盘块的处理操作。(block 位图处理函数,bmap - block map),inode – 文件的i 节点;block – 文件中的数据块号;create - 创建标志
{//返回block 数据块对应在设备上的逻辑块号(盘块号)
struct buffer_head * bh; int i;
if (block<0)// 如果块号小于0,则死机
panic("_bmap: block<0"); if (block >= 7+512+512*512)// 如果块号大于直接块数 + 间接块数 + 二次间接块数,超出文件系统表示范围,则死机
panic("_bmap: block>big"); if (block<7) {// 如果该块号小于7,则使用直接块表示
if (create && !inode->i_zone[block])//如果create为真且对应 的逻辑块号为0,则向相应设备申请一逻辑块
if (inode->i_zone[block]=new_block(inode->i_dev)) { inode->i_ctime=CURRENT_TIME; inode->i_dirt=1; } return inode->i_zone[block];//回对应的逻辑块号
} block -= 7; if (block<512) {// 如果该块号>=7,并且小于7+512,则说明是一次间接块
if (create && !inode->i_zone[7])//如果create为真且首次使用一级间接块,则向相应设备申请一逻辑块来存放间接块信息
if (inode->i_zone[7]=new_block(inode->i_dev)) { inode->i_dirt=1; inode->i_ctime=CURRENT_TIME; } if (!inode->i_zone[7])// 若此时i 节点间接块字段中为0,表明申请磁盘块失败,返回0 退出
return 0; if (!(bh = bread(inode->i_dev,inode->i_zone[7])))// 读取设备上的一次间接块信息,(里面放的是逻辑块号数)
return 0; i = ((unsigned short *) (bh->b_data))[block];// 取该间接块上第block 项中的逻辑块号
if (create && !i)//如果create为真且读取的逻辑块号为0,则向相应设备申请一逻辑块
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])//如果create为真且首次使用二级间接块,则向相应设备申请一逻辑块来存放二级间接块的一级间接块信息
if (inode->i_zone[8]=new_block(inode->i_dev)) { inode->i_dirt=1; inode->i_ctime=CURRENT_TIME; } if (!inode->i_zone[8])// 若此时i 节点二次间接块字段为0,表明申请磁盘块失败,返回0 退出
return 0; if (!(bh=bread(inode->i_dev,inode->i_zone[8])))// 读取该二次间接块的一级块。
return 0; i = ((unsigned short *)bh->b_data)[block>>9];// 取该二次间接块的一级块上第(block/512)项中的逻辑块号,,(要记准用这种方式的,来取二级间接块的信息)
if (create && !i)//如果create为真且对应 的二次间接块的一级块上第(block/512)项中的逻辑块号为0,则向相应设备申请一逻辑块,来存放间接块信息
if (i=new_block(inode->i_dev)) { ((unsigned short *) (bh->b_data))[block>>9]=i; bh->b_dirt=1; } brelse(bh); if (!i)/// 如果二次间接块的二级块块号为0,表示申请磁盘块失败,返回0 退出
return 0; if (!(bh=bread(inode->i_dev,i)))// 读取二次间接块的二级块
return 0; i = ((unsigned short *)bh->b_data)[block&511];// 取该二级块上第block 项中的逻辑块号。(与上511 是为了找在这个二级块中对应的block值,同时限定block 值不超过511)
if (create && !i)//如果create为真且对应 的逻辑块号为0,则向相应设备申请一逻辑块
if (i=new_block(inode->i_dev)) { ((unsigned short *) (bh->b_data))[block&511]=i; bh->b_dirt=1; } brelse(bh); return i; }
int bmap(struct m_inode * inode,int block)// 根据i 节点信息取文件数据块block 在设备上对应的逻辑块号
{ return _bmap(inode,block,0); }
int create_block(struct m_inode * inode, int block)// 创建文件数据块block 在设备上对应的逻辑块,并返回设备上对应的逻辑块号
{ return _bmap(inode,block,1); } void iput(struct m_inode * inode)// 释放一个i 节点(写入设备)
{ if (!inode) return; wait_on_inode(inode);// 等待inode 节点解锁(如果已上锁的话)
if (!inode->i_count)// ,此节点没有 被引用,i 节点被使用的次数,0 表示该i 节点空闲。
panic("iput: trying to free free inode"); if (inode->i_pipe) {//管道标志,如果是管道设备
wake_up(&inode->i_wait);//则唤醒等待该管道的进程
if (--inode->i_count)//引用次数减1,如果还有引用则返回
return; free_page(inode->i_size);//释放 管道占用的内存页面
inode->i_count=0;// i 节点被使用的次数,0 表示该i 节点空闲。
inode->i_dirt=0;// 已修改(脏)标志。
inode->i_pipe=0;// 管道标志。
return; } if (!inode->i_dev) {// 如果i 节点对应的设备号=0,则将此节点的引用计数递减1,返回
inode->i_count--; return; } if (S_ISBLK(inode->i_mode)) {// 如果是块设备文件的i 节点,此时逻辑块字段0 中是设备号,则刷新该设备。并等待i 节点解锁
sync_dev(inode->i_zone[0]);//inode->i_zone[0]存放的是设备号,对指定设备进行高速缓冲数据与设备上数据的同步操作
wait_on_inode(inode);// 等待指定的i 节点可用(解锁)
} repeat: if (inode->i_count>1) {// 如果i 节点的引用计数大于1,则递减1,后返回
inode->i_count--; return; } if (!inode->i_nlinks) {// 如果i 节点的链接数为0,则释放该i 节点的所有逻辑块,并释放该i 节点
truncate(inode);//截断文件数据函数,将节点对应的文件长度截为0,并释放占用的设备空间
free_inode(inode);// 释放指定的i 节点
return; } if (inode->i_dirt) {// 如果该i 节点已作过修改,则更新该i 节点,并等待该i 节点解锁
write_inode(inode); /* we can sleep - so do again *///将指定i 节点信息写入设备(写入缓冲区相应的缓冲块中,待缓冲区刷新时会写入盘中)
wait_on_inode(inode);// 等待指定的i 节点可用(解锁)
goto repeat; } inode->i_count--;// i 节点引用计数递减1
return; }
struct m_inode * get_empty_inode(void)//从内存i 节点表(inode_table)中获取一个空闲i 节点项,寻找引用计数count 为0 的i 节点,并将其写盘后清零,返回其指针
{ struct m_inode * inode; static struct m_inode * last_inode = inode_table;// last_inode 指向i 节点表第一项
int i;
do { inode = NULL; for (i = NR_INODE; i ; i--) { if (++last_inode >= inode_table + NR_INODE)// 如果last_inode 已经指向i 节点表的最后1 项之后,则让其重新指向i 节点表开始处
last_inode = inode_table; if (!last_inode->i_count) {// 如果last_inode 所指向的i 节点的计数值为0,则说明可能找到空闲i 节点项。让inode 指向 该i 节点。如果该i 节点的已修改标志和锁定标志均为0,则我们可以使用该i 节点,于是退出循环
inode = last_inode; if (!inode->i_dirt && !inode->i_lock) break; } } if (!inode) {// 如果没有找到空闲i 节点(inode=NULL),则将整个i 节点表打印出来供调试使用,并死机
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);// 等待该i 节点解锁(如果又被上锁的话
while (inode->i_dirt) {// 如果该i 节点已修改标志被置位的话,则将该i 节点刷新,并等待该i 节点解锁
write_inode(inode);//将指定i 节点信息写入设备(写入缓冲区相应的缓冲块中,待缓冲区刷新时会写入盘中)
wait_on_inode(inode);// 等待指定的i 节点可用(解锁)
} } while (inode->i_count);// 如果i 节点又被其它占用的话,则重新寻找空闲i 节点
memset(inode,0,sizeof(*inode));// 已找到空闲i 节点项。则将该i 节点项内容清零,
inode->i_count = 1;//并置引用标志为1
return inode;//,返回该i 节点指针
}
struct m_inode * get_pipe_inode(void)// 获取管道节点。返回为i 节点指针(如果是NULL 则失败)
{ struct m_inode * inode;
if (!(inode = get_empty_inode()))//从内存i 节点表(inode_table)中获取一个空闲i 节点项
return NULL; if (!(inode->i_size=get_free_page())) {//// 文件大小(字节数),取一页空闲内存,
inode->i_count = 0;// i 节点被使用的次数,0 表示该i 节点空闲。
return NULL; } inode->i_count = 2; /* sum of readers/writers *///读/写两者总计
PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0;// 复位管道头((inode).i_zone[0])尾((inode).i_zone[1])指针
inode->i_pipe = 1;// 置节点为管道使用的标志
return inode;// 返回i 节点指针
}
struct m_inode * iget(int dev,int nr)//取得一个i节点,dev 设备号,nr节点号,从设备上读取指定节点号的i 节点到内存i 节点表中
{//先从高速缓冲区内存节点表中查找,如查找到则用,如果找不到,则从设备上读取
struct m_inode * inode, * empty;
if (!dev) panic("iget with dev==0"); empty = get_empty_inode();//从内存i 节点表中取一个空闲i 节点。
inode = inode_table;//指向内存i 节点表的第一项
while (inode < NR_INODE+inode_table) {// 扫描i 节点表。寻找指定节点号的i 节点。并递增该节点的引用次数
if (inode->i_dev != dev || inode->i_num != nr) {// 如果当前扫描的i 节点的设备号不等于指定的设备号或者节点号不等于指定的节点号,则继续扫描
inode++; continue; } wait_on_inode(inode);// 找到指定设备号和节点号的i 节点,等待该节点解锁(如果已上锁的话)
if (inode->i_dev != dev || inode->i_num != nr) {// 在等待该节点解锁的阶段,节点表可能会发生变化,所以再次判断,如果发生了变化,则再次重新扫描整个i 节点表
inode = inode_table; continue; } inode->i_count++;// 找到要找的inode节点,将该i 节点引用计数增1
if (inode->i_mount) {// 如果该i 节点是其它文件系统的安装点,则在超级块表中搜寻安装在此i 节点的超级块
int i;
for (i = 0 ; i<NR_SUPER ; i++) if (super_block[i].s_imount==inode) break; if (i >= NR_SUPER) {//如果没有找到超级块,则显示出错信息,并释放函数开始获取的空闲节点,返回该i 节点指针
printk("Mounted inode hasn't got sb\n"); if (empty) iput(empty); return inode; } iput(inode);//找到超级块,则说明这个节点不能用,于是释放这个inode节点
dev = super_block[i].s_dev;//在安装在此i 节点文件系统的超级块上取设备号
nr = ROOT_INO;//并令i 节点号为1,ROOT_INO==1
inode = inode_table;//然后重新扫描整个i 节点表,取该被安装文件系统的根节点,,,为什么要这么做呢??
continue; } if (empty)// 已经找到相应的i 节点,因此放弃临时申请的空闲节点,返回该找到的i 节点
iput(empty);// 释放一个i 节点
return inode; } if (!empty) return (NULL); inode=empty;// 如果在i 节点表中没有找到指定的i 节点,则利用前面申请的空闲i 节点在i 节点表中建立该节点
inode->i_dev = dev; inode->i_num = nr; read_inode(inode);//并从相应设备上读取该i 节点信息。返回该i 节点
return inode; }
static void read_inode(struct m_inode * inode)//从设备上读取指定i 节点的信息到内存中(缓冲区中)
{ struct super_block * sb; struct buffer_head * bh; int block;
lock_inode(inode);// 对指定的i 节点上锁(锁定指定的i 节点)
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;// 由节点号换算成逻辑块号,该i 节点所在的逻辑块号 = (启动块+超级块) + i 节点位图占用的块数 + 逻辑块位图占用的块数 + (i 节点号-1)/每块含有的i 节点数
if (!(bh=bread(inode->i_dev,block)))// 从设备上读取该i 节点信息所在的逻辑块,
panic("unable to read i-node block"); *(struct d_inode *)inode = ((struct d_inode *)bh->b_data) [(inode->i_num-1)%INODES_PER_BLOCK];//并将该inode 指针指向对应i 节点信息
brelse(bh);//最后释放读入的缓冲区
unlock_inode(inode);// 对指定的i 节点解锁
}
static void write_inode(struct m_inode * inode)// 将指定i 节点信息写入设备(写入缓冲区相应的缓冲块中,待缓冲区刷新时会写入盘中)
{ struct super_block * sb; struct buffer_head * bh; int block;
lock_inode(inode);// 首先锁定该i 节点,
if (!inode->i_dirt || !inode->i_dev) {//如果该i 节点没有被修改过或者该i 节点的设备号等于零,
unlock_inode(inode);//则解锁该i 节点
return;//并退出
} if (!(sb=get_super(inode->i_dev)))// 获取该i 节点的超级块。
panic("trying to write inode without device"); block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks + (inode->i_num-1)/INODES_PER_BLOCK;// 由节点号换算成逻辑块号,该i 节点所在的逻辑块号 = (启动块+超级块) + i 节点位图占用的块数 + 逻辑块位图占用的块数 + (i 节点号-1)/每块含有的i 节点数
if (!(bh=bread(inode->i_dev,block)))// 从设备上读取该i 节点信息所在的逻辑块,
panic("unable to read i-node block"); ((struct d_inode *)bh->b_data) [(inode->i_num-1)%INODES_PER_BLOCK] = *(struct d_inode *)inode;//并将该inode 指针指向对应i 节点信息
bh->b_dirt=1;// 置缓冲区已修改标志
inode->i_dirt=0;//而i 节点修改标志置零
brelse(bh);//然后释放该含有i 节点的缓冲区
unlock_inode(inode);//并解锁该i 节点
}
|