linux 0.11 文件系统(二)
超级块描述了整个文件系统的信息,而文件作为存储的对象,它的信息是有inode节点来描述的。i节点位图描述了inode的使用情况。
struct m_inode{
unsigned short i_mode; //文件类型
unsigned short i_uid;//文件宿主
unsigned long i_size;//文件大小
unsigned long i_mtime;//文件修改时间
unsigned char i_guid; //文件组id
unsigned char i_nlinks; //文件目录项连接数
unsigned char i_zone[9];//文件所在的设备逻辑块号
//以下的字段在内存中
struct task_struct *i_wait; //等待该i节点的进程
unsigned long i_atime,i_ctime;
unsigned short i_dev; //文件所在的设备号
unsigned short i_num; //i节点号
unsigned short i_count;//i节点被使用的次数,0表示空闲
unsigned char i_lock;
unsigned char i_dirt;
unsigned char i_pipe;
unsigned char i_mount; //该节点是否是某个文件系统的安装节点
unsigned char i_seek;
unsigned char i_update;
}
其 中i_zone[9]很重要,它指出了文件使用的设备的逻辑块号。其中0-6为直接块,也就是文件的数据直接在相应的逻辑块上;7位1级块,1级块可以包 含512个逻辑块号;8位2级块,它可以存放512个1级块。所以文件长度在7k以内只使用前7个位指定逻辑块,再大就要使用间接块指定了。
对i节点位图以及i节点的操作有new_inode,free_inode,iget,iput,bmp,以及namei.辅助的函数有get_empty_inode,read_inode,write_inode.内核维护了一个inode节点数组:
struct m_inode inode_table[NR_INODE]={{0,},};
new_inode(int dev)从inode_table中获取一个空闲的inode节点项,然后相应设备超级块中的i节点位图中的第一个0bit,接着设置inode节点信 息,并置引用为1。free_inode则相反,进行一系列测试后,它清除位图的相应比特为,并清空inode节点占用的内存。
对 逻辑块位图的操作有new_block和free_block,前者获取一个空闲缓冲块并置位相应比特位,后者相反。以上都是对内存中的inode数组以 及缓冲区中的位图进行操作,而最终要落实到设备特定的块上。read_inode是从设备上读取指定i节点的信息到内存中,write_inode是写入 指定i节点到设备。后者是将inode写到相应缓冲块中,并置位
b_dirt。这里涉及到计算指定的inode节点在设备上的逻辑块号:
//启动块+超级块+i节点位图块数+逻辑块位图块数+(i节点号-1)/每块含有的i节点
block=1+1+sb->s_imap_blocks+sb->s_zmap_blocks+(inode->s_inum)/INODES_PER_BLOCK;
iget 函数是从设备上读取指定的i节点。如同getblk,考虑到同步问题也是个反复的过程。它首先从i节点表中申请一个空闲i节点。然后搜索i节点表,看是否 有指定的i节点,如果没有就用read_inode从设备中读取到空闲节点就可返回。如果有则等待它解锁。等待过程中如果节点表发生变化那么要重新搜索。 若该i节点是某个文件系统的安装点,则读取相应文件系统的超级块并设置节点号为该文件系统的第一个inode,然后重新开始。
iput是回写到设 备,这里根据文件的类型有几种不同的处理方法。如果文件是管道文件,则唤醒管道上等待的进程,并释放管道页面。管道的物理内存地址在inode-> i_size字段。如果文件是块设备,那么刷新设备。inode->i_zone[0]中存放的是设备号。如果链接数为0,则释放i节点所有的逻辑 块,并释放该节点。
还有一个比较重要的函数是 _bmp,它是将文件数据块映射到盘块。在前面讲过的do_no_page中用到过,计算出缺页的内存地址在程序所占的块号后,通过bmp映射到设备上的块号,并读取被交换到块设备上的页面。
下面具体讲解一下:
//inode:文件的节点,block:文件中的块号;create:创建标志。
static int _bmp(struct m_inode *inode,int block,int create)
{
struct buffer_head *bh;
int i;
if(block<0)
panic("_bmp:block<0");
//如果块号大于直接块数+间接块数+二次间接块数,则超出文件系统表示范围,死机
if(block>=7+512+512*512)
panic("_bmp:block>big");
//如果块号小于7,则使用直接块表示
if(block<7){
if(create&&!inode->i_zone[block])
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){
//如果间接块没有创建且创建标志为1,那么创建间接块
if(create&&!inode->i_zone[7])
if(inode->i_zone[7]=new_block(inode->i_dev)){
inode->i_dirt=1;
inode->i_ctime=CURRENT_TIME;
}
if(!inode->_zone[7])
return 0;
//读取设备上的一次间接块
if(!(bh=bread(inode->i_dev,inode->i_zone[7])))
return 0;
//取间接块第block上的逻辑块号
i=((unsigned short*)(bh->b_data))[block];
if(create&&!i)
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])
if(inode->i_zone[8]=new_block[inode->i_dev]){
inode->i_dirt=1;
inode->i_ctime=CURRENT_TIME;
}
if(!inode->i_zone[8])
return 0;
//读取该二次间接块的一级块
if(!(bh=bread(inode->i_dev,inode->i_zone[8])))
return 0;
i=((unsigned short*)bh->b_data)[block>>9];
if(create&&!i)
if(i=new_block(inode->i_dev){
bh->b_dirt=1;
}
brelse(bh);
if(!i)
return 0;
//读取二次间接块的二级块
if(!(bh=bread(inode->i_dev,i))
return 0;
//取二级块上的block项的逻辑号
i=((unsigned short*)bh->b_data)[block&&511];
if(create&&!i)
if(i=new_block(inode->i_dev)){
((unsigned short*)(bh->b_data))[block&511]=i;
bh->b_dirt=1;
}
brelse(bh);
return i;
}
bmp是create=0的_bmp,仅取文件中的数据块号在缓冲区的对应块号,而create_block则是create=1的_bmp,它创建对应的设备逻辑块的缓冲块。
到 目前为止,应该对超级块,i节点,逻辑块,缓冲块有了一定的认识了。设备上的物理部分都有内存的表示,比如超级块在内存中有超级块数组 super_block[NR_SUPER],i节点和逻辑块位图有s_imap[8]和s_zmap[8]指向的缓冲区表示,i节点在内存中有i节点数 组inode_table[NR_INODE],逻辑块有缓冲区中的缓冲块。分配inode和逻辑块只需要分配内存中保留的空闲块,并设置对应位图和修改 位,内核会在适当时间刷新到设备上。
原文网址:http://blog.sina.com.cn/s/blog_4a7b69aa010005rt.html~type=v5_one&label=rela_nextarticle
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?