/*
* linux/fs/inode.c
*/
#include
#include
#include
#include
#include
#include
struct m_inode inode_table[NR_INODE]={{0,},}; // 内存中i 节点表static void read_inode(struct m_inode * inode);
static void write_inode(struct m_inode * inode);
/* 等待指定的i 节点可用*/
static inline void wait_on_inode(struct m_inode * inode)
{
cli();
while (inode->i_lock) // // 如果i 节点已被锁定
sleep_on(&inode->i_wait); // 不可中断的等待状态
sti();
}
/* 对指定的i 节点上锁*/
static inline void lock_inode(struct m_inode * inode)
{
cli();
while (inode->i_lock)
sleep_on(&inode->i_wait);
inode->i_lock=1; // 置锁定标志
sti();
}
/* 对指定的i 节点解锁*/
static inline void unlock_inode(struct m_inode * inode)
{
inode->i_lock=0; // 复位i 节点的锁定标志
wake_up(&inode->i_wait); // 唤醒等待此i 节点的进程
}
/* 释放内存中设备dev 的所有i 节点*/
void invalidate_inodes(int dev)
{
int i;
struct m_inode * inode;
inode = 0+inode_table;
for(i=0 ; i { wait_on_inode(inode); // 等待该i 节点可用 if (inode->i_dev == dev) { if (inode->i_count) // 如果其引用数不为0 printk("inode in use on removed disk\n\r"); inode->i_dev = inode->i_dirt = 0; // 释放该i 节点,只是一个标记 } } } /* 同步内存(inode数组)与设备上的所有i 节点信息*/ void sync_inodes(void) { int i; struct m_inode * inode; inode = 0+inode_table; // 第一项 for(i=0 ; i { wait_on_inode(inode); // 等待该i 节点可用 // 如果该i 节点已修改且不是管道节点 if (inode->i_dirt && !inode->i_pipe) write_inode(inode); // 写盘 } } /* 文件数据块映射到盘块的处理操作,可能需要建立新的逻辑块*/ static int _bmap(struct m_inode * inode,int block,int create) { // inode 文件的i 节点;block (相对于文件而言)文件中的数据块号;create 创建标志 // block是相对于文件而言,但是i_zone[block]则是相对于设备而言的。相对于文件的 // block可能不止i_zone数组大小,所以如果block的值大于7时,需要使用间接来寻址。// 如下的映射: // |-----------| |------------------------------| // |file block | ---> | disk block | // |-----------| |------------------------------| // 如果create = 1,需要建立新的逻辑块, struct buffer_head * bh; int i; if (block<0) // 块号小于0 ? panic("_bmap: block<0"); if (block >= 7+512+512*512) // 超出文件系统表示范围 panic("_bmap: block>big"); /* * 文件在磁盘上存储时,并不是在连续空间存储的。一个文件可能对应的是 * 几个设备逻辑块号(设备的逻辑块号同样决定了系统支持的最大的文件长度) * 这些信息存储在i_zone中。 */ // 如果该块号小于7,则使用直接块表示 if (block<7) { // 创建标志置位, 并且i 节点中对应该块的逻辑块(区段)字段为0 if (create && !inode->i_zone[block]) // 向相应设备申请一磁盘块 if (inode->i_zone[block]=new_block(inode->i_dev)) { inode->i_ctime=CURRENT_TIME; // 设置i 节点修改时间 inode->i_dirt=1; // 置i 节点已修改标志 } return inode->i_zone[block]; // 返回逻辑块号 } // 如果该块号>=7,并且小于7+512,则说明是一次间接块 block -= 7; if (block<512) { // 如果是创建,并且该i 节点中对应间接块字段为0 if (create && !inode->i_zone[7]) // 首次使用间接块 // 需申请一磁盘块用于存放间接块信息,inode->i_zone[7] // 中存储的是相对于软盘的逻辑块号,在以后读取该块时, // 使用下面的据对软盘号 if (inode->i_zone[7]=new_block(inode->i_dev)) { // 设置i 节点已修改标志和修改时间 inode->i_dirt=1; inode->i_ctime=CURRENT_TIME; } // 若此时i 节点间接块字段中为0,表明申请磁盘块失败 if (!inode->i_zone[7]) return 0; /* 读取设备上的一次间接块*/ /* * i_zone[7] -- 逻辑块号-- 将该逻辑块号对应内容读入到 * 内存中-- 在内存中查找(间接)查找逻辑块号 * */ if (!(bh = bread(inode->i_dev,inode->i_zone[7]))) return 0; // 取该间接块上第block 项中的逻辑块号(盘块号) i = ((unsigned short *) (bh->b_data))[block]; // 如果是创建并且间接块的第block 项中的逻辑块号为0 的话 // 该初始化是在函数new_block中使用函数clear_block来讲整个 // 分配的缓冲区域置0 if (create && !i) if (i=new_block(inode->i_dev)) // 申请一磁盘块(逻辑块) { // 间接块中的第block 项等于该新逻辑块块号 ((unsigned short *) (bh->b_data))[block]=i; bh->b_dirt=1; // 置位间接块的已修改标志 } brelse(bh); // 最后释放该间接块 return i; // 返回磁盘上新申请的对应block 的逻辑块的块号 } // 程序运行到此,表明数据块是二次间接块 block -= 512; // 如果是新创建并且i 节点的二次间接块字段为0 if (create && !inode->i_zone[8]) // 需申请一磁盘块用于存放二次间接块的一级块信息 // 将此实际磁盘块号填入二次间接块字段中 if (inode->i_zone[8]=new_block(inode->i_dev)) { // 置i 节点已修改编制和修改时间 inode->i_dirt=1; inode->i_ctime=CURRENT_TIME; } // 若此时i 节点二次间接块字段为0,表明申请磁盘块失败 if (!inode->i_zone[8]) return 0; // 读取该二次间接块的一级块(读取设备上的数据)。即是说明的是间接 // 寻找逻辑块的信息时存储在设备上 if (!(bh=bread(inode->i_dev,inode->i_zone[8]))) return 0; // 取该二次间接块的一级块上第(block/512)项中的逻辑块号 i = ((unsigned short *)bh->b_data)[block>>9]; // 如果是创建并且二次间接块的一级块上第(block/512)项中的逻辑块号为0 的话if (create && !i) // 申请一磁盘块(逻辑块)作为二次间接块的二级块 if (i=new_block(inode->i_dev)) { ((unsigned short *) (bh->b_data))[block>>9]=i; bh->b_dirt=1; // 置位二次间接块的一级块已修改标志 } brelse(bh); // 释放二次间接块的一级块 // 如果二次间接块的二级块块号为0,表示申请磁盘块失败 if (!i) return 0; // 读取二次间接块的二级块 if (!(bh=bread(inode->i_dev,i))) return 0; // 取该二级块上第block 项中的逻辑块号 i = ((unsigned short *)bh->b_data)[block&511]; // 如果是创建并且二级块的第block 项中的逻辑块号为0 的话,则申请一磁盘块(逻辑块)if (create && !i) if (i=new_block(inode->i_dev)) { ((unsigned short *) (bh->b_data))[block&511]=i; bh->b_dirt=1; } brelse(bh); // 最后释放该二次间接块的二级块 // 返回磁盘上新申请的对应block 的逻辑块的块号 return i; } /* 根据文件的块偏移量来得到设备上的逻辑块号*/ /* 根据文件的m_inode节点信息,和在文件中的偏移量block来影射到实际的*/ /* 逻辑块号,有返回值int返回*/ int bmap(struct m_inode * inode,int block) { /* * 在该函数调用时,create = 0,该函数只是返回的是return inode->i_zone[block]; * 只是返回相应的信息 */ return _bmap(inode,block,0); } /* 创建文件数据块block 在设备上对应的逻辑块,并返回设备上对应的逻辑块号*/ int create_block(struct m_inode * inode, int block) { return _bmap(inode,block,1); } /* 释放一个i 节点(从内存数组回写入设备文件) */ void iput(struct m_inode * inode) { if (!inode) return; wait_on_inode(inode); // 等待inode 节点解锁 if (!inode->i_count) panic("iput: trying to free free inode"); if (inode->i_pipe) // 管道i 节点 { wake_up(&inode->i_wait); // 唤醒等待该管道的进程 if (--inode->i_count) // 引用次数减1, 还有引用则返回 return; // 释放管道占用的内存页面 free_page(inode->i_size); // 复位该节点的引用计数值 inode->i_count=0; inode->i_dirt=0; // 已修改标志 inode->i_pipe=0; // 管道标志 return; } if (!inode->i_dev) // 如果i 节点对应的设备号0 { inode->i_count--; // 此节点的引用计数递减1 return; } if (S_ISBLK(inode->i_mode)) // 块设备文件的i节点? { sync_dev(inode->i_zone[0]); // 刷新该设备 wait_on_inode(inode); // 等待i 节点解锁 } repeat: if (inode->i_count>1) // // 如果i 节点的引用计数大于1 { inode->i_count--; // 递减1 return; } if (!inode->i_nlinks) // 如果i 节点的链接数为0 { truncate(inode); // 释放该i 节点的所有逻辑块 free_inode(inode); // 释放该i 节点 return; } if (inode->i_dirt) // 该i 节点已作过修改,没有修改的话,不需要写入文件{ // 更新该i 节点 write_inode(inode); /* we can sleep - so do again */ wait_on_inode(inode); // 等待该i 节点解锁 goto repeat; } // i 节点引用计数递减1 inode->i_count--; return; } /* 从i 节点表(inode_table)中获取一个空闲i 节点项*/ struct m_inode * get_empty_inode(void) { struct m_inode * inode; // last_inode 指向i 节点表第一项 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; // 计数值为0,则说明可能找到空闲i 节点项 if (!last_inode->i_count) { inode = last_inode; // 如果该i 节点的已修改标志和锁定标志均为0,则我们可以使用该i 节点if (!inode->i_dirt && !inode->i_lock) break; } } // 此时inode就是上面寻找到的空闲的inode项 if (!inode) // 如果没有找到空闲i 节点(inode=NULL) { for (i=0 ; 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 节点解锁 // 如果该i 节点已修改标志被置位的话 while (inode->i_dirt) { write_inode(inode); // 将该i 节点刷新 wait_on_inode(inode); // 等待该i 节点解锁 } // 如果i 节点又被其它占用的话,则重新寻找空闲i 节点 } while (inode->i_count); memset(inode,0,sizeof(*inode)); // 该i节点项内容清零 inode->i_count = 1; // 引用标志为1 return inode; } /* 获取管道节点*/ struct m_inode * get_pipe_inode(void) { struct m_inode * inode; // 寻找一个空闲i 节点项 if (!(inode = get_empty_inode())) return NULL; // 节点的i_size 字段指向缓冲区 if (!(inode->i_size=get_free_page())) { inode->i_count = 0; return NULL; } inode->i_count = 2; /* sum of readers/writers */ // 复位管道头尾指针 PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0; inode->i_pipe = 1; // 置节点为管道使用的标志 return inode; } /* 从设备上dev读取指定节点号的i 节点nr */ struct m_inode * iget(int dev,int nr) { struct m_inode * inode, * empty; if (!dev) panic("iget with dev==0"); empty = get_empty_inode(); inode = inode_table; // 扫描i 节点表。寻找指定节点号的i 节点 while (inode < NR_INODE+inode_table) { if (inode->i_dev != dev || inode->i_num != nr) { inode++; continue; } // 此时inode->i_dev == dev && inode->i_num == nr wait_on_inode(inode); // 等待该节点解锁 // 在等待该节点解锁的阶段,节点表可能会发生变化 if (inode->i_dev != dev || inode->i_num != nr) { // 如果发生了变化,则再次重新扫描整个i 节点表。 inode = inode_table; continue; } // 没有变化 inode->i_count++; // 将该i 节点引用计数增1 /**************************************************************/ /* 是目录inode?*/ if (inode->i_mount) // 如果该i节点是其它文件系统的安装点 { int i; // 在超级块表中搜寻安装在此i 节点的超级块 for (i = 0 ; i if (super_block[i].s_imount==inode) break; if (i >= NR_SUPER) // 没有找到 { // 显示出错信息 printk("Mounted inode hasn't got sb\n"); if (empty) // 如果empty不空,清空inode内存数组 iput(empty); return inode; } iput(inode); // 将该i 节点写盘 dev = super_block[i].s_dev; // 从安装在此i 节点文件系统的超级块上取设备号 // 扫描整个i 节点表,取该被安装文件系统的根节点 nr = ROOT_INO; // nr = ROOT_INO = 1 inode = inode_table; continue; } /***************************************************************/ // 已经找到相应的i 节点,因此放弃临时申请的空闲节点,返回该找到的i 节点if (empty) iput(empty); return inode; } // 程序运行到这,说明在i 节点表中没有找到指定的i 节点 if (!empty) return (NULL); inode=empty; inode->i_dev = dev; inode->i_num = nr; // 从相应设备上读取该i 节点信息 read_inode(inode); return inode; // 返回该i节点的值 } /* 从设备上读取指定i 节点的信息到内存中(缓冲区中)*/ static void read_inode(struct m_inode * inode) { struct super_block * sb; struct buffer_head * bh; int block; // 首先锁定该i 节点,取该节点所在设备的超级块 lock_inode(inode); if (!(sb=get_super(inode->i_dev))) panic("trying to read inode without dev"); // 计算inode的位置(在设备文件中存在存放inode的地方,// 此位置是在设备上存放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 *)inode = ((struct d_inode *)bh->b_data) [(inode->i_num-1)%INODES_PER_BLOCK]; // 最后释放读入的缓冲区,并解锁该i 节点 brelse(bh); unlock_inode(inode); } /* 将指定i 节点信息写入设备*/ 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在设备上inode存储区域上的位置block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks + (inode->i_num-1)/INODES_PER_BLOCK; // 从设备上读取该i 节点所在的逻辑块 if (!(bh=bread(inode->i_dev,block))) panic("unable to read i-node block"); // 将i节点的信息暂时的写入bh->data中,并设置b_dirty // 标志,内核在合适的时间将bh的内容刷洗到实际的设备上((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); } /* * 这个文件主要是实现函数iget和函数iput。函数iget的主要作用是* 从设备上读取指定的inode节点号,就是实现从设备(存数介质)到* 内存inode数组的转移。函数iput则是相反的函数,该函数实现的是* 将内存inode的信息写入到设备中,在函数中调用write_inode函数* 写入。另外一个重要的函数时bmap函数,该函数实现的是根据文件* 的block来计算得到设备上的绝对逻辑块号 */