天天看点

linux 0.11 内核学习 -- inode.c

#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 节点表

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)

{

cli();

while (inode->i_lock) // // 如果i 节点已被锁定

sleep_on(&inode->i_wait); // 不可中断的等待状态

sti();

}

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; // 复位i 节点的锁定标志

wake_up(&inode->i_wait); // 唤醒等待此i 节点的进程

}

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); // 等待该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 节点,只是一个标记

}

}

}

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); // 等待该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");

// 如果该块号小于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;

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;

}

int bmap(struct m_inode * inode,int block)

{

return _bmap(inode,block,0);

}

int create_block(struct m_inode * inode, int block)

{

return _bmap(inode,block,1);

}

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);

wait_on_inode(inode); // 等待该i 节点解锁

goto repeat;

}

// i 节点引用计数递减1

inode->i_count--;

return;

}

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<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 节点解锁

// 如果该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;

// 复位管道头尾指针

PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0;

inode->i_pipe = 1; // 置节点为管道使用的标志

return inode;

}

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

if (inode->i_mount) // 如果该i 节点是其它文件系统的安装点

{

int i;

// 在超级块表中搜寻安装在此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) // 如果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节点的值

}

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);

}

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);

}

参考《linux内核完全注释》和网上相关文章

继续阅读