Linux-0.11 文件系统inode.c详解

news2024/11/13 10:20:08

Linux-0.11 文件系统inode.c详解

Linux-0.11中使用的文件系统为minix, inode.c中的函数和该文件系统强关联。

inode节点在文件系统中与文件相关联,一个文件的就由一个inode来管理,这个inode节点将记录文件的权限,大小, 文件具体存储的逻辑块位置等等信息。

read_inode

static void read_inode(struct m_inode * inode);

该函数的作用是通过inode的编号,在磁盘上读取inode块内存

回顾一下minix文件系统的分布,如下图所示:

在这里插入图片描述

其中引导块为1个block,超级块为1个block,inode位图的大小在超级块中定义,逻辑块位图的大小在超级块中定义。跳过这些块,就可以来到inode节点块的起点。

那么inode节点会在哪个block中呢?

inode节点的序号从0开始, 但是0号是个保留号, 实际存储并不会存0号inode, 因此inode的实际起始位置是1。

因此序号为n的inode,在磁盘上的逻辑块号为:

block = 1 + 1 + s_imap_blocks(inode位图大小) + s_zmap_blocks(逻辑块位图大小) + (n - 1)/每个磁盘块存储的inode数量。

找到了inode所在的逻辑块号, 计算inode在该块中的下标, 则通过:

index = (n - 1) % 每个磁盘块存储的inode数量

到此, 给定一个inode号,就可以从磁盘上找到对应的inode节点。

通过上面的解释,很容易理解下面的代码。

if (!(sb=get_super(inode->i_dev)))//获取超级块的内容
    panic("trying to read inode without dev");
block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks +
    (inode->i_num-1)/INODES_PER_BLOCK;//获取inode所在的逻辑块
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];//将该inode读取到内存中

write_inode

static void write_inode(struct m_inode * inode)

该函数的作用是将inode的内容回写到磁盘。

该函数在搜索inode节点的方法上和read_inode相同。

有所区别的是该函数是修改一个inode的值, 因此在找到inode节点后需要进行赋值, 并将buffer块标记为脏页,以回写到磁盘上。

((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;//inode的脏数据标记为0

iget

iget(int dev,int nr)

该函数的作用主要是通过设备号和inode的序号获取对应的inode节点。

需要注意的是,如果一个硬盘上装有多个文件系统,需要处理一个inode节点是另一个文件系统挂载点的情况。

该段代码主要是遍历inode表,查找是否存在这样的inode, 这个inode的设备号和入参的设备号相同, inode的节点号和入参的节点号相同。如果查到了, 则将该inode的引用计数增加1。

while (inode < NR_INODE+inode_table) {
    if (inode->i_dev != dev || inode->i_num != nr) {//遍历inode表, 查找inode
        inode++;
        continue;
    }
    wait_on_inode(inode);//等待该inode节点
    if (inode->i_dev != dev || inode->i_num != nr) {
        inode = inode_table;
        continue;
    }
    inode->i_count++;//引用计数增加1

如果i节点是某个文件系统的安装点, 则在超级块的表中搜索即可搜索到。 若找到了相应的超级块, 则将该i节点写盘。再从安装此i节点的文件系统上的超级块上取设备号,并令i节点为1。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AbFwIRZz-1676864332322)(null)]

判断inode节点是否是其他文件系统挂载点的代码如下:

if (inode->i_mount) {//该inode是一个挂载节点
    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);//将该inode的引用计数减1
    dev = super_block[i].s_dev;//设备号设置为超级块中的设备号
    nr = ROOT_INO;//inode节点号设置为1号
    inode = inode_table;
    continue;
}

iput

void iput(struct m_inode * inode)

该函数的作用是放回一个i节点。 该函数主要用于将i节点的引用计数递减1。

首先判断i节点的有效性:

if (!inode)
    return;
wait_on_inode(inode);//等待inode解锁
if (!inode->i_count) //如果引用计数为0, 则inode是空闲的, 内核要求放回是有问题的。
    panic("iput: trying to free free inode");

下面是对管道节点的处理:

if (inode->i_pipe) {
    wake_up(&inode->i_wait);//唤醒等待该管道节点的进程
    if (--inode->i_count)//引用计数减1
        return;
    free_page(inode->i_size);//将管道节点的内存释放, 对于管道节点inode->i_size存放着内存页的地址。
    inode->i_count=0;
    inode->i_dirt=0;
    inode->i_pipe=0;
    return;
}

如果是一个设备节点,则刷新该设备:

if (S_ISBLK(inode->i_mode)) {
    sync_dev(inode->i_zone[0]);
    wait_on_inode(inode);
}

最后一段是执行引用计数减1的操作:

repeat:
if (inode->i_count>1) {//引用计数大于1, 直接减1后返回
    inode->i_count--;
    return;
}
if (!inode->i_nlinks) {//如果连接数为0, 则该文件已经被删除
    truncate(inode);
    free_inode(inode);
    return;
}
if (inode->i_dirt) {//如果该inode为脏数据, 则写回磁盘
    write_inode(inode);	/* we can sleep - so do again */
    wait_on_inode(inode);
    goto repeat;
}
inode->i_count--;//引用计数减1

get_empty_inode

struct m_inode * get_empty_inode(void)

该函数的作用是从空闲的inode表中获取一个空闲的i节点项。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rrEWONIk-1676864332368)(null)]

_bmap

static int _bmap(struct m_inode * inode,int block,int create)

该函数的作用就是映射文件数据块到盘块的处理操作。

需要注意的是入参中的block不是指硬盘的逻辑块, 而是指文件的数据块的序号。 该block指的是文件的第block个1k的数据块。

inode的寻址主要分为直接寻址,一次间接块的寻址, 二次间接块的寻址三种。如下图所示:

在这里插入图片描述

如果block号小于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; //设置change time
            inode->i_dirt=1; //设置该inode块已经被修改,是一个脏数据
        }
    return inode->i_zone[block];
}

如果block号大于7, 小于等于7 + 512, 则通过一次间接块寻址。

block -= 7;
if (block<512) {
    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->i_zone[7])
        return 0;
    if (!(bh = bread(inode->i_dev,inode->i_zone[7])))
        return 0;
    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号大于7 + 512 , 小于等于7 + 512 + 512 * 512, 则通过二次间接块寻址。

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))) {
        ((unsigned short *) (bh->b_data))[block>>9]=i;
        bh->b_dirt=1;
    }
brelse(bh);
if (!i)
    return 0;
if (!(bh=bread(inode->i_dev,i)))
    return 0;
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);

create_block

int create_block(struct m_inode * inode, int block)

create block底层调用了_bmap函数,去磁盘上申请一个逻辑块, 并与inode关联, 通过返回该逻辑块的盘块号。

bmap

int bmap(struct m_inode * inode,int block)

该函数的作用是通过文件数据块的块号返回其磁盘上的逻辑块号。底层也是通过_bmap函数实现。

sync_inodes

void sync_inodes(void)

该函数的作用是将inode表中的脏的数据刷到磁盘上。

inode = 0+inode_table;
for(i=0 ; i<NR_INODE ; i++,inode++) {
    wait_on_inode(inode);//等待该inode解锁
    if (inode->i_dirt && !inode->i_pipe)//如果inode已经被修改, 并且不是管道节点
        write_inode(inode); //将该inode写盘
}

invalidate_inodes

void invalidate_inodes(int dev)

释放设备dev在内存i节点表中的所有i节点。

该函数的作用就是遍历inode数组, 如果inode数组的某一项的i_dev等于dev的时候, 将该inode的i_dev和i_dirt置为0。

在这里插入图片描述

inode = 0+inode_table;
for(i=0 ; i<NR_INODE ; i++,inode++) {
    wait_on_inode(inode);//如果该inode已经被加锁,则等待该inode解锁
    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; //将设备号置为0, 即释放该inode
    }
}

get_pipe_inode

struct m_inode * get_pipe_inode(void)

该函数的作用是获取管道节点

if (!(inode = get_empty_inode()))//从inode表中获取一个空闲的inode
    return NULL;
if (!(inode->i_size=get_free_page())) {//从内存中获取一个空的页,赋值给inode的i_size变量
    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;

lock_inode

static inline void lock_inode(struct m_inode * inode)

该函数的作用是锁定某个inode节点

该函数的代码较为简单,下面直接通过注释进行解释。

cli();//关中断
while (inode->i_lock)
    sleep_on(&inode->i_wait);//如果该inode被加锁, 就将当前进程状态修改为TASK_UNINTERRUPTIBLE, 切换其他进程运行
inode->i_lock=1; //将该inode的锁定状态修改为1
sti();//开中断

wait_on_inode

static inline void wait_on_inode(struct m_inode * inode)

该函数的作用是等待指定的inode节点解锁

该函数的代码较为简单,下面直接通过注释进行解释。

cli(); //关中断
while (inode->i_lock) 
    sleep_on(&inode->i_wait); //如果该inode被加锁, 就将当前进程状态修改为TASK_UNINTERRUPTIBLE, 切换其他进程运行
sti();//开中断

unlock_inode

static inline void unlock_inode(struct m_inode * inode)

该函数的作用是对指定inode解锁

该函数的代码较为简单,下面直接通过注释进行解释。

inode->i_lock=0;//将该inode的锁定状态修改为0.
wake_up(&inode->i_wait);//唤醒等待该inode的进程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/358810.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringCloud之Nacos

Nacos1. Nacos的概念2. Nacos的作用3. Nacos的下载与安装4. Nacos Client 服务端的搭建5. Nacos 的namespace 和 group6. 配置中心7. 注意点1. Nacos的概念 Nocos 是 SpringCloudAlibaba架构中最重要的组件Nacos 是一个更易于帮助构建云原生应用的动态服务发现、配置和服务挂你…

pytorch搭建手写数字识别LeNet-5网络,并用tensorRT部署

pytorch搭建手写数字识别LeNet-5网络&#xff0c;并用tensorRT部署前言1、pytorch 搭建LeNet-5&#xff0c;并转为ONNX格式1.1 LeNet-5网络介绍1.2 ONNX(Open Neural Network Exchange)介绍1.3 pytorch 搭建 LeNet5网络2、将onnx转为tensorRT2.1 tensorRT 介绍2.1 onnx 转为 te…

C++面向对象(下)

文章目录前言1.再谈构造函数1.初始化列表2.explicit关键字2. static成员1.概念3.友元1.概念2.友元函数3.友元类4. 内部类5.匿名对象6.编译器优化7.总结前言 本文是主要是将之前关于C面向对象中的一些没有归纳到的零星知识点进行补充&#xff0c;同时对C中的面向对象简单收个尾…

如何在ONLYOFFICE v7.3中使用VSTACK和HSTACK公式

大家好&#xff0c;今天来给大家讲解一下&#xff0c;怎样在ONLYOFFICE v7.3中使用VSTACK和HSTACK公式&#xff0c; 这两个新公式也是ONLYOFFICE7.3版本更新后新的计算公式&#xff0c;请大家详细阅读本文。 ONLYOFFICE ONLYOFFICE文档是一款免费开源在线办公软件&#xff0c…

实验室装修|SICOLAB实验室装修方案

实验室装修不外乎通风与洁净的设计为重中之重&#xff0c;以下由SICOLAB介绍分享。一、实验室通风系统主要包含送风系统和排风系统两部分。&#xff08;1&#xff09;送风系统的设计需要考虑以下几个方面&#xff1a;1.1空气质量要求&#xff1a;实验室内的空气需要保持一定的洁…

生物素标记试剂1869922-24-6,Alkyne-PEG3-Biotin PC,炔烃PEG3生物素PC

1、试剂基团反应特点&#xff08;Reagent group reaction characteristics&#xff09;&#xff1a;PC alkyne-PEG3-Biotin含一个炔烃和一个 PEG 链接的可光裂解生物素基团。含 3 个单元 PEG 的 ADC linker&#xff0c;生物素本身是个游离的小分子&#xff0c;在生物实验中常常…

【汽车雷达通往自动驾驶的关键技术】

本文编辑&#xff1a;调皮哥的小助理 现代汽车雷达装置比手机还小&#xff0c;能探测前方、后方或侧方的盲点位置是否存在障碍物&#xff0c;但这还不百分之百实现全自动驾驶的。传统的汽车雷达分辨率都不高&#xff0c;只能“看到”一团东西&#xff0c;可以检测到汽车周围存在…

掌握使用yum为CentOS系统安装软件掌握使用apt为Ubuntu安装软件(扩展)

1.掌握使用yum为CentOS系统安装软件2.掌握使用apt为Ubuntu安装软件&#xff08;扩展&#xff09;1、Linux系统的应用商店 操作系统安装软件有许多种方式&#xff0c;一般分为&#xff1a; •下载安装包自行安装•如win系统使用exe文件、msi文件等•如mac系统使用dmg文件、pkg文…

容联七陌:ChatGPT大模型能力为智能客服带来新方向

科技云报道原创。 近几个月来&#xff0c;大众对ChatGPT预期的持续走高&#xff0c;也影响到了智能客服领域公司的命运。 一方面&#xff0c;ChatGPT的出现为智能客服场景带来了更加“智能”的可能性&#xff1b;但另一方面&#xff0c;有人认为ChatGPT完全可以替代现有的智能…

数值方法笔记3:线性和非线性方程组求解

前置知识1&#xff1a;矩阵范数前置知识2&#xff1a;舒尔补前置知识3&#xff1a;可约矩阵前置知识4&#xff1a;谱半径1.【线性方程组】直接求解&#xff1a;高斯消元法(LULULU分解)、LDVLDVLDV分解、LDLTLDL^TLDLT分解、UDUTUDU^TUDUT分解1.1 高斯消元法(LULULU分解)1.2 LDV…

公司缺人面了8个测试员,他们都有一个相同的缺点.....

年后公司缺人&#xff0c;面了不少测试&#xff0c;结果竟然没有一个合适的。 一开始瞄准的就是中级的水准&#xff0c;也没指望来大牛&#xff0c;提供的薪资在10-20k&#xff0c;面试的人很多&#xff0c;但平均水平很让人失望。 看简历很多都是3年工作经验&#xff0c;但面…

什么是深拷贝和浅拷贝?以及怎么实现深拷贝和浅拷贝?

拷贝浅是创建一个新对象&#xff0c;这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型&#xff0c;拷贝的就是基本类型的值&#xff0c;如果属性是引用类型&#xff0c;拷贝的就是内存地址&#xff0c;所以如果其中一个对象改变了这个地址&#xff0c;就会影响到…

Excel操作-Apache-POI

Excel操作-Apache-POI一、场景需求将一些数据库信息导出为Excel表格将Excel表格数据导入数据库大量的数据的导入导出操作解决方案常用的解决方案&#xff1a;Apache POI与阿里巴巴easyExcelApache-POI介绍Apache-POI是基于office open XML标准&#xff08;OOXML&#xff09;和M…

合同审核机器人,提高合同审核效率,规避企业损失

官方网址&#xff1a;www.hanyunintel.com 在合同审核中面临的问题和挑战&#xff1a; 一、人工合同审核风险高&#xff1a; 人工合同审核依赖审核人员业务素养、精神状态、工作量、体力、工作态度及责任心&#xff0c;难免存在人工审核合同不严谨的情况&#xff0c;如果不能及…

【C++提高编程】C++全栈体系(二十一)

C提高编程 第三章 STL - 常用容器 三、deque容器 1. deque容器基本概念 功能&#xff1a; 双端数组&#xff0c;可以对头端进行插入删除操作 deque与vector区别&#xff1a; vector对于头部的插入删除效率低&#xff0c;数据量越大&#xff0c;效率越低deque相对而言&am…

简洁易用的记账小程序——微点记账

背景 由于每个月的信用卡账单太过吓人&#xff0c;记性也不是特别的好&#xff0c;加上微信支付宝账单中有些明细不是很明确。比如在京东花销的明细不会记录用户购买了什么&#xff0c;只会记录那个通道支出的。所以&#xff0c;才会有了想自己开发一款记账小程序&#xff0c;…

面试常问-Alpha测试和Beta测试

Alpha测试 Alpha测试是一种验收测试&#xff0c;在识别典型用户可能执行的任务并对其进行测试之前&#xff0c;执行该测试是为了识别所有可能的问题和错误。 尽可能简单地说&#xff0c;这种测试之所以被称为alpha&#xff0c;只是因为它是在软件开发的早期、接近开发结束时和…

使用loading动画让你的条件渲染页面更高级

前言在我们做项目的使用常常会使用条件渲染去有选择的给用户展示相关页面&#xff0c;如果渲染的数据或场景比较多比较复杂&#xff0c;那么往往需要3、4s的时间去完成&#xff0c;用户点击了之后就会陷入3、4s的空白期&#xff0c;并且这段时间屏幕是处于一种”未响应“的状态…

【欧拉筛法】洛谷 P3383 线性筛素数

3383. 线性筛素数 文章目录题目描述输入格式&#xff1a;输出格式&#xff1a;数据范围输入样例输出样例方法一&#xff1a;埃氏筛法解题思路代码复杂度分析&#xff1a;方法二&#xff1a;欧拉筛法解题思路代码复杂度分析&#xff1a;两种方法对比埃氏筛法欧拉筛法题目描述 给…