i节点
来源:互联网 发布:淘宝被骗了有办法吗 编辑:程序博客网 时间:2024/06/07 11:44
以Linux 0.11为实例,兴趣所至,个人总结,不保证正确性。。。
[i节点]
i节点是表征文件的方式,因此在linux 0.11内核中,有一系列专门操作i节点的函数,在fs/inode.c中。与i节点进程同步相关的函数,主要是wait_on_inode、lock_inode和unlock_inode,这几个函数都比较简单。以lock_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();
}
在进入函数时,先用cli关闭中断。如果这个i节点已经被加锁,那让当前进程睡眠在这个i节点上。等待这个i节点被解锁,然后当前进程将这个i节点加锁。最后退出函数之前,打开中断。unlock_inode与lock_inode执行的动作相反,将这个i节点解锁,然后唤醒等待在这个i节点上的进程。
系统在启动的时候,会初始化一个i节点的数组inode_table,这个数组中的i节点就是系统全部可用的i节点。iget是根据设备号和i节点号获取i节点的入口函数,get_empty_inode是从inode_table数组中获取一个空的i节点的入口。get_pipe_inode是获取一个管道设备的i节点,可以把管道看成一个虚拟的设备,所以也需要一个i节点来表示。读写i节点的入口函数是read_inode和write_inode,在进行i节点的读写之前,要先设置好待读写的i节点的设备号和i节点号。对于文件的i节点,还需要保存相应的文件数据,通过_bmap来实现。
[get_empty_inode]
get_empty_inode的作用是从inode_table中获取一个空的inode。如果有必要,就将数据同步到磁盘上去。并且设置i节点各个字段的初值。实现原理就是对inode_table做一次遍历,找到空闲的i节点。代码如下
struct m_inode * get_empty_inode(void )
{
struct m_inode * inode;
static struct m_inode * last_inode = inode_table; //inode_table在inode.c中定义,struct m_inode inode_table[NR_INODE]={{0,},};
int i;
do {
inode = NULL;
for (i = NR_INODE; i ; i--) {
/*
如果last_inode已经指向了inode_table数组的最后一项,就让它重新指向inode_table的第一项
保证外围的for语句在最坏的情况下能够将数组整个遍历一遍
*/
if (++last_inode >= inode_table + NR_INODE)
last_inode = inode_table;
/*
虽然说,找到一个空闲的i节点只需要i_count为0即可。i_dirt和i_lock都为0的话,就更好了。i_dirt为0,表明在
使用这个i节点之前不需要就i节点数据写入磁盘。i_lock为0,说明不需要等待其它进程释放这个i节点
*/
if (!last_inode->i_count) {
inode = last_inode;
if (!inode->i_dirt && !inode->i_lock)
break;
}
}
if (!inode) { //遍历完之后没有找到空闲的i节点,表明系统中的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");
}
/*
能运行到这里说明能找到空闲的i节点,剩下的工作就是看是否需要先把i节点数据写入磁盘,等待解锁了
*/
wait_on_inode(inode);
while (inode->i_dirt) {
write_inode(inode);
wait_on_inode(inode);
}
} while (inode->i_count); //在进程睡眠期间,如果这个i节点又被其它进程所用。那就需要重新寻找了
/*
到此,找到了一个空的i节点,没有被加锁,也不再需要写入磁盘了。剩下就做一些初始化字段值的工作即可
*/
memset(inode,0, sizeof(*inode));
inode->i_count = 1;
return inode;
}
[iget]
iget函数需要两个参数,设备号和i节点号。它的作用是根据设备号和i节点号,获取相应的i节点信息。在寻找时,如果存在设备和i节点号的i节点,就使用这个i节点。否则,就使用一个空的i节点。
struct m_inode * iget(int dev, int nr)
{
struct m_inode * inode, * empty;
if (!dev)
panic( "iget with dev==0" );
empty = get_empty_inode(); //先获取一个空的i节点
inode = inode_table;
while (inode < NR_INODE+inode_table) {
if (inode->i_dev != dev || inode->i_num != nr) {
inode++;
continue ;
}
/*
如果能找到已有的i节点的设备号和i节点号都对应,那么就等待这个i节点解锁
在等待解锁的过程中,这个i节点的内容可能发生变化
所以解锁后,需要再次确认i节点的设备号和i节点号是否就是我们所需要的
*/
wait_on_inode(inode);
if (inode->i_dev != dev || inode->i_num != nr) {
inode = inode_table;
continue ;
}
inode->i_count++; //here!找到了设备号和i节点号都对应的i节点,就是所需要的
/*
i_mout为1表示这个i节点是否是其它设备/文件系统的挂载节点。如果是其它设备/文件系统的挂载节点,就需要用这个设备超级块
上的设备号来重新获取i节点,并且这是要获取的i节点号为1,获取这个文件系统的根节点。
*/
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;
}
iput(inode); //前面找到的i节点被释放,重新寻找
dev = super_block[i].s_dev;
nr = ROOT_INO; // #define ROOT_INO 1
inode = inode_table;
continue ; //
} //end of if
if (empty) // 如果找到了设备号和i节点号都对应的i节点,最开始获取的空的i节点就要被释放
iput(empty);
return inode;
} //end of while
/*
没有找到设备号和i节点号都对应的i节点,就是用那个空的i节点,设置好初值
*/
if (!empty)
return (NULL);
inode=empty;
inode->i_dev = dev;
inode->i_num = nr;
read_inode(inode); //从磁盘读取i节点信息
return inode;
}
[iput]
iput释放一个i节点。根据实际情况,可能需要将数据写回磁盘,或者释放磁盘空间
void iput(struct m_inode * inode)
{
if (!inode)
return ;
wait_on_inode(inode);
if (!inode->i_count)
panic( "iput: trying to free free inode" );
/*
管道i节点比较特殊,它涉及到两个进程。读进程和写进程。由于管道是一个虚拟设备,管道i节点的i_size字段表示的是这个管道i节点实际使
用的物理页的其实地址。
*/
if (inode->i_pipe) {
wake_up(&inode->i_wait);
/*
管道i节点涉及到两个进程,所以引用次数减一之后,如果不为零,就不用释放内存空间
*/
if (--inode->i_count)
return ;
free_page(inode->i_size);
inode->i_count=0;
inode->i_dirt=0;
inode->i_pipe=0;
return ;
}
if (!inode->i_dev) {
inode->i_count--;
return ;
}
/*
S_ISBLC宏表示这个i节点是否是设备的i节点。即这个i节点用来表示一个“设备文件”。如果是,那么i_zone[0]中就存放的是设备号
*/
if (S_ISBLK(inode->i_mode)) {
sync_dev(inode->i_zone[0]);
wait_on_inode(inode);
}
repeat:
if (inode->i_count>1) {
inode->i_count--;
return ;
}
/*
i_nlinks为0,就要释放i节点,如果有需要就要释放磁盘空间(当这个i节点代表的是一般的文件或目录时。存储文件数据和目录数据的逻辑块需要被释放掉。truncate函数实现释放磁盘的功能。它会首先进行是否为一般文件或目录的i节点检查
*/
if (!inode->i_nlinks) {
truncate(inode);
free_inode(inode);
return ;
}
/*
i节点的内容被修改,那么就需要把内容同步到磁盘上
*/
if (inode->i_dirt) {
write_inode(inode);
wait_on_inode(inode);
goto repeat;
}
inode->i_count--;
return ;
}
[读写i节点]
读写i节点的函数分别是read_inode和write_inode。它们的作用分别是根据i节点的设备号和i节点号,将磁盘上的数据读入到i节点中,或者把i节点的数据写入磁盘。当然,都是通过中间的高速缓存来实现的。
在对一个i节点进行read_inode调用之前,需要首先设定好这个i节点的设备号和i节点号。同理也是write_inode。
static void read_inode( struct m_inode * inode)
{
struct super_block * sb;
struct buffer_head * bh;
int block;
lock_inode(inode);
/*
get_super是根据设备号,获取这个设备的超级快的数据。
linux 0.11中定义了一个超级快数据struct super_block super_block[NR_SUPER];
设备在挂载的时候,会把超级快的数据加入到这个数组中。get_super的实现就是遍历这个数组,找到所需的超级块为止
*/
if (!(sb=get_super(inode->i_dev)))
panic( "trying to read inode without dev" );
/*
这里是根据i节点号计算这个i节点所在的逻辑块的块号。因为磁盘是块设备,所以,对磁盘数据的读写都是以块为单位的。在逻逻辑块的编号上,从小到大是:引导块、超级块、i节点位图区、逻辑块节点位图区。然后才是i节点区。所以是2(超级块+引导块)+s_imap_blocks(i节点位图区所占的逻辑块的快数)+s_zmap_blocks(逻辑块位图区所占的逻辑块的块数)+(i_num-1)/每个逻辑块的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" );
/*
磁盘上存放的i节点数据是磁盘上i节点,对应的数据结构是d_inode。而内存中的i节点要比磁盘上i节点的数据结构多一些数据项,但在前面部分是一样的。将整个磁盘块看成磁盘i节点数组,根据i节点号计算索引,读取数据。最后,数据读取完成后,释放缓存,解锁这个i节点
*/
*( struct d_inode *)inode =
(( struct d_inode *)bh->b_data)
[(inode->i_num-1)%INODES_PER_BLOCK];
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);
/*
i_dirt为0表明i节点读入内存后,内容没有被修改,所以不需要重新写回磁盘
*/
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节点所在的逻辑块的编号
*/
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" );
/*
写入i节点数据,实际上是写到高速缓存
*/
(( struct d_inode *)bh->b_data)
[(inode->i_num-1)%INODES_PER_BLOCK] =
*( struct d_inode *)inode;
/*
b_dirt设为1,表明内容被修改。如果有需要,就将数据同步到磁盘
*/
bh->b_dirt=1;
inode->i_dirt=0;
brelse(bh);
unlock_inode(inode);
}
- i节点
- Linux的I节点
- 理解i节点
- linux i 节点
- linux i节点
- I节点介绍
- 关于i节点
- linux I节点
- 理解Linux i节点
- linux i节点(inode)
- ros-i节点代码
- 关于i节点的问题
- Linux中的文件i节点
- unix文件中i节点
- 深入理解linux i节点
- Linux中的文件i节点
- 删除单链表中第i个节点
- 目录、目录项、i节点
- SecureCRT颜色设置
- [python]wxPython学习记录1
- mkyaffs2image命令
- Android VideoView如何播放RTSP的流
- CSS HACK IE6、IE7、IE8、Firefox解决兼容性问题
- i节点
- 杜比TrueHD(Dolby TrueHD)音频编码解析
- 别让自己的孩子成为这样的马和鹰
- ajax post传递参数中文乱码问题
- php学习笔记(三十二)ajax结合pageView类实现页面无刷新请求
- CMD打开IIS,重启iis等
- Tornado 安装
- windows下opengl开发
- ADO.NET对象模型