inode
inode
用来代表一个文件,这里的文件很宽泛,可以是我们熟知的普通文件,可以是目录,可以是管道等。磁盘的inode表里就存着inode信息。
磁盘上的inode结构是
struct d_inode {
unsigned short i_mode;
unsigned short i_uid;
unsigned long i_size;
unsigned long i_time;
unsigned char i_gid;
unsigned char i_nlinks;
unsigned short i_zone[9]; //直接块(0-6)、间接(7)或双重间接(8)逻辑块号
};
在内存中的inode结构是
struct m_inode {
//与d_inode一致
unsigned short i_mode;
unsigned short i_uid;
unsigned long i_size;
unsigned long i_mtime;
unsigned char i_gid;
unsigned char i_nlinks;
unsigned short i_zone[9]; //直接块(0-6)、间接(7)或双重间接(8)逻辑块号
/* these are in memory also */
struct task_struct * i_wait;
unsigned long i_atime;
unsigned long i_ctime;
unsigned short i_dev;
unsigned short i_num;
unsigned short i_count;
unsigned char i_lock;
unsigned char i_dirt;
unsigned char i_pipe;
unsigned char i_mount;
unsigned char i_seek;
unsigned char i_update;
};
iput
inode如果是一个管道,则唤醒等待该管道的进程,然后把inode的引用数减去1,如果为0表示没有引用了,可以释放管道占用的内存了。如果是块设备先把缓冲区、inode、设备三个之间数据同步。
fs/inode.c
void iput(struct m_inode * inode)
{
if (!inode)
return;
//如果inode被锁,就等待
wait_on_inode(inode);
if (!inode->i_count) //inode还在使用
panic("iput: trying to free free inode");
// 如果是管道i节点,则唤醒等待该管道的进程,引用次数减1,如果还有引用则返回。
// 否则释放管道占用的内存页面,并复位该节点的引用计数值、已修改标志和管道标志,
// 并返回。对于管道节点,inode->i_size存放这内存也地址。
if (inode->i_pipe) {
wake_up(&inode->i_wait);
if (--inode->i_count)
return;
free_page(inode->i_size);
inode->i_count=0;
inode->i_dirt=0;
inode->i_pipe=0;
return;
}
// 如果i节点对应的设备号 = 0,则将此节点的引用计数递减1,返回。例如用于管道操作
// 的i节点,其i节点的设备号为0.
if (!inode->i_dev) {
inode->i_count--;
return;
}
// 如果是块设备文件的i节点,此时逻辑块字段0(i_zone[0])中是设备号,则刷新该设备。
// 并等待i节点解锁。
if (S_ISBLK(inode->i_mode)) {
sync_dev(inode->i_zone[0]);
wait_on_inode(inode);
}
// 如果i节点的引用计数大于1,则计数递减1后就直接返回(因为该i节点还有人在用,不能
// 释放),否则就说明i节点的引用计数值为1。如果i节点的链接数为0,则说明i节点对应文件
// 被删除。于是释放该i节点的所有逻辑块,并释放该i节点。函数free_inode()用于实际释
// 放i节点操作,即复位i节点对应的i节点位图bit位,清空i节点结构内容。
repeat:
if (inode->i_count>1) {
inode->i_count--; //如果i节点的引用计数大于1,则计数递减1后就直接返回,因为该i节点还有人在用,不能释放
return;
}
// i节点的引用计数值为1
if (!inode->i_nlinks) { // 如果i节点的链接数为0,则说明i节点对应文件被删除。于是释放该i节点的所有逻辑块,并释放该i节点。
truncate(inode);
free_inode(inode);
return;
}
// 如果该i节点已做过修改,则回写更新该i节点,并等待该i节点解锁。由于这里在写i节点
// 时需要等待睡眠,此时其他进程有可能修改i节点,因此在进程被唤醒后需要再次重复进行
// 上述判断过程(repeat)。
if (inode->i_dirt) {
write_inode(inode); //把inode信息所对应的块读取到缓冲区,使用inode信息更新缓冲区中的数据,然后缓冲区等待回写到磁盘
wait_on_inode(inode);
goto repeat;
}
// 程序若能执行到此,则说明该i节点的引用计数值i_count是1、链接数不为零,并且内容
// 没有被修改过。因此此时只要把i节点引用计数递减1,返回。此时该i节点的i_count=0,
// 表示已释放。
inode->i_count--;
return;
}
read_inode
根据inode节点号计算块号,当然这个块可能不止这一个inode信息,然后把指定块号的数据读取到缓冲区中。
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");
// 该i节点所在的设备逻辑块号=(启动块+超级块)+i节点位图占用的块数+逻辑块位图占用的块数
// +(i节点号-1)/每块含有的i节点数。虽然i节点号从0开始编号,但第i个0号i节点不用,并且
// 磁盘上也不保存对应的0号i节点结构。因此存放i节点的盘块的第i块上保存的是i节点号是1--16
// 的i节点结构而不是0--15的。因此在上面计算i节点号对应的i节点结构所在盘块时需要减1,即:
// B=(i节点号-1)/每块含有i节点结构数。例如,节点号16的i节点结构应该在B=(16-1)/16 = 0的
// 块上。这里我们从设备上读取该i节点所在的逻辑块,并复制指定i节点内容到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);
}
write_inode
先根据inode节点号就可以计算出块号,把这个设备的块号读取到缓冲区中,然后把缓冲区中相应的的inode信息更新,修改b_dirt
置为1,等待回写到磁盘。唤醒等待在缓冲区上的进程
static void write_inode(struct m_inode * inode)
{
struct super_block * sb;
struct buffer_head * bh;
int block;
// 首先锁定该i节点,如果该i节点没有被修改或者该i节点的设备号等于零,则解锁该i节点,并退出。
// 对于没有被修改过的i节点,其内容与缓冲区中或设备中的相同。然后获取该i节点的超级块。
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");
// 该i节点所在的设备逻辑块号=(启动块+超级块)+i节点位图占用的块数+逻辑块位图占用的块数
// +(i节点号-1)/每块含有的i节点数。我们从设备上读取i节点所在的逻辑块,并将该i节点信息复制
// 到逻辑块对应i节点的项位置处。
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");
//找到数据块中inode所属的位置,写到高速缓存等待回写到硬盘
((struct d_inode *)bh->b_data)[(inode->i_num-1)%INODES_PER_BLOCK] =*(struct d_inode *)inode;
// 然后置缓冲区已修改标志,而i节点内容已经与缓冲区中的一致,因此修改标志置零。然后释放该
// 含有i节点的缓冲区,并解锁该i节点。
bh->b_dirt=1; // 等待回写硬盘
inode->i_dirt=0;
brelse(bh); //把bh的引用数减去1,唤醒等待在缓冲区上的进程
unlock_inode(inode);
}
// 每个块有几个inode,硬盘里是d_inode,内存是m_inode结构
#define INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct d_inode)))
// 每个块块包含的目录项数
#define DIR_ENTRIES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct dir_entry)))
当块大小是1024字节时,0号块保留,1号块是超级块
sync_dev
先把缓冲区中的数据同步到指定设备,腾出缓冲区空间,给后面inode信息同步提供空间,然后把inode_table表中的inode信息同步到设备中,再把因为同步inode信息时变脏的缓冲区再次回写到设备中。 这样缓冲区是最新的数据,inode信息也是最新的了。
int sync_dev(int dev)
{
int i;
struct buffer_head * bh;
bh = start_buffer;
for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
if (bh->b_dev != dev) // 不是设备dev的缓冲块则继续
continue;
wait_on_buffer(bh);
if (bh->b_dev == dev && bh->b_dirt)
ll_rw_block(WRITE,bh); //把缓冲区数据写到磁盘
}
sync_inodes(); // 把inode_table表中的inode信息同步到磁盘
// 把因为同步inode的缓冲区回写到设备中
bh = start_buffer;
for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
if (bh->b_dev != dev)
continue;
wait_on_buffer(bh);
if (bh->b_dev == dev && bh->b_dirt)
ll_rw_block(WRITE,bh);
}
return 0;
}
sync_inodes
从inode_table
中把非管道类型的inode信息同步到磁盘,inode_table
数组大小是32,linux0.11只能打开32个文件。 这32个inode是所有进程共享的。
void sync_inodes(void)
{
int i;
struct m_inode * inode;
inode = 0+inode_table; // inode 表
for(i=0 ; i<NR_INODE ; i++,inode++) { // NR_INODE = 32
wait_on_inode(inode);
if (inode->i_dirt && !inode->i_pipe)
write_inode(inode);
}
}
iget
fs/inode.c
根据指定节点号获取一个inode。 先从inode表中获取一个空闲的inode,这个空闲的inode不一定用的上,然后遍历inode表,从表里找到一个指定设备号与节点号的inode,如果找到,因为我们马上就要用这个inode,所以这个inode的引用数又要加1
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
// 接着扫描i节点表。寻找参数指定节点号nr的i节点。并递增该节点的引用次数。如果当
// 前扫描i节点的设备号不等于指定的设备号或者节点号不等于指定的节点号,则继续扫描。
inode = inode_table;
while (inode < NR_INODE+inode_table) {
if (inode->i_dev != dev || inode->i_num != nr) {
inode++;
continue;
}
// 如果找到指定设备号dev和节点号nr的i节点,则等待该节点解锁。在等待该节点解
// 锁过程中,i节点表可能会发生变化。所以再次进行上述相同判断。如果发生了变化,
// 则再次重新扫描整个i节点表。
wait_on_inode(inode);
if (inode->i_dev != dev || inode->i_num != nr) {
inode = inode_table;
continue;
}
// 到这里表示找到相应的i节点。于是将该i节点引用计数增1.然后再做进一步检查,看它
// 是否是另一个文件系统的安装点。若是则寻找被安装文件系统根节点并返回。如果
// 该i节点的确是其他文件系统的安装点,则在超级块表中搜寻安装在此i节点的超级块。
// 如果没有找到,则显示出错信息,并放回本函数开始时获取的空闲节点empty,
// 返回该i节点指针。
inode->i_count++;
if (inode->i_mount) {
int i;
for (i = 0 ; i<NR_SUPER ; i++)
if (super_block[i].s_imount==inode)
break;
if (i >= NR_SUPER) {
printk("Mounted inode hasn't got sb\n");
if (empty)
iput(empty);
return inode;
}
// 执行到这里表示已经找到安装到inode节点的文件系统超级块。于是将该i节点写盘
// 放回,并从安装在次i节点上的文件系统超级块中取设备号,并令i节点号为ROOT_INO,
// 即为1.然后重新扫描整个i节点表,以获取该被安装文件系统的根i节点信息。
iput(inode);
dev = super_block[i].s_dev;
nr = ROOT_INO;
inode = inode_table;
continue;
}
// 最终我们找到了相应的i节点。因此可以放弃本函数开始处临时申请的空闲的i节点,返回
// 找到的i节点指针。
if (empty)
iput(empty);
return inode;
}
// 如果我们在i节点表中没有找到指定的i节点,则利用前面申请的空闲i节点empty在i节点表中
// 建立该i节点。并从相应设备上读取该i节点信息,返回该i节点指针。
if (!empty)
return (NULL);
inode=empty;
inode->i_dev = dev;
inode->i_num = nr;
read_inode(inode);
return inode;
}
get_empty_inode
从inode_table
中找到一个引用数i_count
等于0,并且i_dirt
等于0和没有上锁的inode,如果没找到就会一直找。
struct m_inode * get_empty_inode(void)
{
struct m_inode * inode;
static struct m_inode * last_inode = inode_table; //全局的inode表
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; // 找到空闲inode了,退出for循环
}
}
// 如果没有找到空闲i节点(inode=NULL),则将i节点表打印出来供调试使用,并停机。
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);
}
//睡眠期间很可能被其他进程占用,那么这个inode就不选是空闲节点了,重新寻找
} while (inode->i_count);
// 终于从inode表里找到一个空闲的indoe,初始化内存区域为0
memset(inode,0,sizeof(*inode));
inode->i_count = 1; //初始引用数为1
return inode;
}