【Linux102】61-fs/super.c


公粽号「专注Linux」,专注Linux内核开发

Linux102系列会详细讲解Linux0.11版本中的102个文件,本文讲解linux0.11的第61个文件【Linux102】61-fs/super.c的文件源码。

1. super.c的主要作用

①函数总览

该文件描述了文件系统超级块操作函数,这些函数属于文件系统底层,供上层文件名目录操作函数使用。主要有get_super()put_super()read_super()

另外还有有关文件系统加载/卸载系统调用函数sys_umount()sys_mount(),以及根文件系统加载函数 mount_root()。其他一些辅助函数与buffer.c中的辅助函数的作用类似。超级块中主要存放了有关整个文件系统的信息,其信息结构见图9-2。

②get_super()


get_super()函数用于在指定设备的条件下,在内存超级块数组搜索对应的超级块,并返回相应超级块指针。因此,在调用该函数时,该相应的文件系统必须已经被加载(mount),或者起码该超级块已经占用超级块数组中的一项,否则返回NULL。

③put_super()


put_super()用于释放指定设备的超级块。 它把该超级块对应的文件系统i节点位图逻辑块位图所占用的缓冲块都释放掉。在调用umount() 卸载一个文件系统或者更换磁盘将会调用该函数。

④read_super()


read_super()用于把指定设备的文件系统超级块读入到缓冲区中,并登记超级块数组中,同时也把文件系统的i节点位图逻辑块位图读入内存超级块结构的相应数组中。 最后返回该超级块结构的指针

⑤sys_umount()


sys_umount()系统调用用于卸载一个指定设备文件名文件系统,而sys_mount()则用于往一个目录名加载一个文件系统。

⑥mount_root()


程序中最后一个函数mount_root()是用于安装系统根文件系统,并将在系统初始化时被调用。 其具体操作流程如图9-16所示。


2.源码用到的文件

#include <linux/config.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/system.h>
  
#include <errno.h>
#include <sys/stat.h>  
文件作用
😉【Linux102】42-include/linux/config.hconfig.h 是一个配置文件,用于设置键盘布局和硬盘参数(备用)。设计目的:提供可配置的选项,适配不同的硬件环境。
😉【Linux102】15-include/linux/sched.h本程序主要定义了进程调度的相关数据结构,如任务数据结构等等,理解这些数据结构是理解进程调度机制的关键,也是理解内核的关键。一句话:**我们常说的进程是XXX,但是进程究竟是什么?**就定义在这个文件里面!源码面前了,没有秘密!
😉【Linux102】40-kernel/blk_drv/blk.h这是有关硬盘块设备参数的头文件,因为只用于块设备,所以与块设备代码放在同一个地方。其中主要定义了请求等待队列的数据结构request,用宏语句定义了电梯搜索算法,并对内核目前支持的虚拟盘硬盘软盘三种块设备,根据它们各自的主设备号分别设定了常数值。
😉【Linux102】36-include/asm/system.h该文件中定义了设置或修改描述符/中断门等嵌入式汇编宏。其中,函数 move_to_user_mode() 用于内核在初始化结束时 “切换”到初始进程(任务0)。所使用的方法是模拟中断调用返回过程,即利用指令iret运行初始任务0。
😉【Linux102】89-include/sys/stat.hinclude/sys/stat.h 头文件定义了与文件状态权限相关的数据结构,以及操作文件状态的函数声明。它是 POSIX 标准的一部分,在类 Unix 系统中被广泛使用,提供了查询修改文件属性的基础接口

3.源码版

/*
 *  linux/fs/super.c
 *
 *  (C) 1991  Linus Torvalds
 */

/*
 * super.c contains code to handle the super-block tables.
 */
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/system.h>

#include <errno.h>
#include <sys/stat.h>

int sync_dev(int dev);
void wait_for_keypress(void);

/* set_bit uses setb, as gas doesn't recognize setc */
#define set_bit(bitnr, addr) ({ \
register int __res ; \
__asm__("bt %2,%3;setb %%al":"=a" (__res):"a" (0),"r" (bitnr),"m" (*(addr))); \
__res; })

struct super_block super_block[NR_SUPER];
/* this is initialized in init/main.c */
int ROOT_DEV = 0;

static void lock_super(struct super_block *sb)
{
    cli();
    while (sb->s_lock)
        sleep_on(&(sb->s_wait));
    sb->s_lock = 1;
    sti();
}

static void free_super(struct super_block *sb)
{
    cli();
    sb->s_lock = 0;
    wake_up(&(sb->s_wait));
    sti();
}

static void wait_on_super(struct super_block *sb)
{
    cli();
    while (sb->s_lock)
        sleep_on(&(sb->s_wait));
    sti();
}

struct super_block *get_super(int dev)
{
    struct super_block *s;

    if (!dev)
        return NULL;
    s = 0 + super_block;
    while (s < NR_SUPER + super_block)
        if (s->s_dev == dev)
        {
            wait_on_super(s);
            if (s->s_dev == dev)
                return s;
            s = 0 + super_block;
        }
        else
            s++;
    return NULL;
}

void put_super(int dev)
{
    struct super_block *sb;
    /* struct m_inode * inode;*/
    int i;

    if (dev == ROOT_DEV)
    {
        printk("root diskette changed: prepare for armageddon\n\r");
        return;
    }
    if (!(sb = get_super(dev)))
        return;
    if (sb->s_imount)
    {
        printk("Mounted disk changed - tssk, tssk\n\r");
        return;
    }
    lock_super(sb);
    sb->s_dev = 0;
    for (i = 0; i < I_MAP_SLOTS; i++)
        brelse(sb->s_imap[i]);
    for (i = 0; i < Z_MAP_SLOTS; i++)
        brelse(sb->s_zmap[i]);
    free_super(sb);
    return;
}

static struct super_block *read_super(int dev)
{
    struct super_block *s;
    struct buffer_head *bh;
    int i, block;

    if (!dev)
        return NULL;
    check_disk_change(dev);
    if ((s = get_super(dev)))
        return s;
    for (s = 0 + super_block;; s++)
    {
        if (s >= NR_SUPER + super_block)
            return NULL;
        if (!s->s_dev)
            break;
    }
    s->s_dev = dev;
    s->s_isup = NULL;
    s->s_imount = NULL;
    s->s_time = 0;
    s->s_rd_only = 0;
    s->s_dirt = 0;
    lock_super(s);
    if (!(bh = bread(dev, 1)))
    {
        s->s_dev = 0;
        free_super(s);
        return NULL;
    }
    *((struct d_super_block *)s) =
        *((struct d_super_block *)bh->b_data);
    brelse(bh);
    if (s->s_magic != SUPER_MAGIC)
    {
        s->s_dev = 0;
        free_super(s);
        return NULL;
    }
    for (i = 0; i < I_MAP_SLOTS; i++)
        s->s_imap[i] = NULL;
    for (i = 0; i < Z_MAP_SLOTS; i++)
        s->s_zmap[i] = NULL;
    block = 2;
    for (i = 0; i < s->s_imap_blocks; i++)
        if ((s->s_imap[i] = bread(dev, block)))
            block++;
        else
            break;
    for (i = 0; i < s->s_zmap_blocks; i++)
        if ((s->s_zmap[i] = bread(dev, block)))
            block++;
        else
            break;
    if (block != 2 + s->s_imap_blocks + s->s_zmap_blocks)
    {
        for (i = 0; i < I_MAP_SLOTS; i++)
            brelse(s->s_imap[i]);
        for (i = 0; i < Z_MAP_SLOTS; i++)
            brelse(s->s_zmap[i]);
        s->s_dev = 0;
        free_super(s);
        return NULL;
    }
    s->s_imap[0]->b_data[0] |= 1;
    s->s_zmap[0]->b_data[0] |= 1;
    free_super(s);
    return s;
}

int sys_umount(char *dev_name)
{
    struct m_inode *inode;
    struct super_block *sb;
    int dev;

    if (!(inode = namei(dev_name)))
        return -ENOENT;
    dev = inode->i_zone[0];
    if (!S_ISBLK(inode->i_mode))
    {
        iput(inode);
        return -ENOTBLK;
    }
    iput(inode);
    if (dev == ROOT_DEV)
        return -EBUSY;
    if (!(sb = get_super(dev)) || !(sb->s_imount))
        return -ENOENT;
    if (!sb->s_imount->i_mount)
        printk("Mounted inode has i_mount=0\n");
    for (inode = inode_table + 0; inode < inode_table + NR_INODE; inode++)
        if (inode->i_dev == dev && inode->i_count)
            return -EBUSY;
    sb->s_imount->i_mount = 0;
    iput(sb->s_imount);
    sb->s_imount = NULL;
    iput(sb->s_isup);
    sb->s_isup = NULL;
    put_super(dev);
    sync_dev(dev);
    return 0;
}

int sys_mount(char *dev_name, char *dir_name, int rw_flag)
{
    struct m_inode *dev_i, *dir_i;
    struct super_block *sb;
    int dev;

    if (!(dev_i = namei(dev_name)))
        return -ENOENT;
    dev = dev_i->i_zone[0];
    if (!S_ISBLK(dev_i->i_mode))
    {
        iput(dev_i);
        return -EPERM;
    }
    iput(dev_i);
    if (!(dir_i = namei(dir_name)))
        return -ENOENT;
    if (dir_i->i_count != 1 || dir_i->i_num == ROOT_INO)
    {
        iput(dir_i);
        return -EBUSY;
    }
    if (!S_ISDIR(dir_i->i_mode))
    {
        iput(dir_i);
        return -EPERM;
    }
    if (!(sb = read_super(dev)))
    {
        iput(dir_i);
        return -EBUSY;
    }
    if (sb->s_imount)
    {
        iput(dir_i);
        return -EBUSY;
    }
    if (dir_i->i_mount)
    {
        iput(dir_i);
        return -EPERM;
    }
    sb->s_imount = dir_i;
    dir_i->i_mount = 1;
    dir_i->i_dirt = 1; /* NOTE! we don't iput(dir_i) */
    return 0;          /* we do that in umount */
}

void mount_root(void)
{
    int i, free;
    struct super_block *p;
    struct m_inode *mi;

    if (32 != sizeof(struct d_inode))
        panic("bad i-node size");
    for (i = 0; i < NR_FILE; i++)
        file_table[i].f_count = 0;
    if (MAJOR(ROOT_DEV) == 2)
    {
        printk("Insert root floppy and press ENTER");
        wait_for_keypress();
    }
    for (p = &super_block[0]; p < &super_block[NR_SUPER]; p++)
    {
        p->s_dev = 0;
        p->s_lock = 0;
        p->s_wait = NULL;
    }
    if (!(p = read_super(ROOT_DEV)))
        panic("Unable to mount root");
    if (!(mi = iget(ROOT_DEV, ROOT_INO)))
        panic("Unable to read root i-node");
    mi->i_count += 3; /* NOTE! it is logically used 4 times, not 1 */
    p->s_isup = p->s_imount = mi;
    current->pwd = mi;
    current->root = mi;
    free = 0;
    i = p->s_nzones;
    while (--i >= 0)
        if (!set_bit(i & 8191, p->s_zmap[i >> 13]->b_data))
            free++;
    printk("%d/%d free blocks\n\r", free, p->s_nzones);
    free = 0;
    i = p->s_ninodes + 1;
    while (--i >= 0)
        if (!set_bit(i & 8191, p->s_imap[i >> 13]->b_data))
            free++;
    printk("%d/%d free inodes\n\r", free, p->s_ninodes);
}

4.源码注释版本


/*
 *  linux/fs/super.c
 *
 *  (C) 1991  Linus Torvalds
 */

/*
 * 文件头注释:说明文件名、作者和版权信息
 * super.c 包含处理超级块表的代码。
 */
#include <linux/config.h> // 内核配置头文件,包含各种配置宏
#include <linux/sched.h>  // 调度程序头文件,定义任务结构task_struct、睡眠函数等
#include <linux/kernel.h> // 内核头文件,包含内核函数声明如printk
#include <asm/system.h>   // 系统头文件,包含cli、sti等汇编指令定义

#include <errno.h>    // 错误号定义头文件
#include <sys/stat.h> // 文件状态头文件,定义S_ISBLK、S_ISDIR等宏

// 声明外部函数
int sync_dev(int dev);        // 同步设备缓冲区函数(在buffer.c中定义)
void wait_for_keypress(void); // 等待按键函数(在console.c中定义)

/*
 * set_bit 使用 setb 指令,因为 gas 汇编器不识别 setc
 * 内联汇编:设置指定位
 * 参数:bitnr - 位号,addr - 地址
 * 返回值:设置前该位的值(0或1)
 */
#define set_bit(bitnr, addr) ({ \
register int __res ; \  // 定义寄存器变量__res存储结果
__asm__("bt %2,%3;setb %%al" : "=a"(__res) : "a"(0), "r"(bitnr), "m"(*(addr)));
__res;
})  // 汇编指令:bt测试位,setb根据进位标志设置al寄存器

// 定义超级块数组,NR_SUPER是系统允许的最大超级块数量(通常为8)
struct super_block super_block[NR_SUPER];

/* 根设备号,在init/main.c中初始化 */
int ROOT_DEV = 0;

/*
 * 锁定超级块
 * 参数:sb - 要锁定的超级块指针
 */
static void lock_super(struct super_block *sb)
{
    cli();                       // 关中断,进入临界区
    while (sb->s_lock)           // 如果超级块已被锁定
        sleep_on(&(sb->s_wait)); // 当前进程睡眠在超级块的等待队列上
    sb->s_lock = 1;              // 设置锁定标志
    sti();                       // 开中断
}

/*
 * 释放超级块锁
 * 参数:sb - 要释放的超级块指针
 */
static void free_super(struct super_block *sb)
{
    cli();                  // 关中断
    sb->s_lock = 0;         // 清除锁定标志
    wake_up(&(sb->s_wait)); // 唤醒等待该超级块的所有进程
    sti();                  // 开中断
}

/*
 * 等待超级块解锁
 * 参数:sb - 要等待的超级块指针
 */
static void wait_on_super(struct super_block *sb)
{
    cli();                       // 关中断
    while (sb->s_lock)           // 如果超级块被锁定
        sleep_on(&(sb->s_wait)); // 当前进程睡眠等待
    sti();                       // 开中断
}

/*
 * 获取指定设备的超级块
 * 参数:dev - 设备号
 * 返回:超级块指针或NULL
 */
struct super_block *get_super(int dev)
{
    struct super_block *s;

    if (!dev) // 如果设备号为0,返回NULL
        return NULL;
    s = 0 + super_block;               // s指向超级块数组起始地址(等价于s = super_block)
    while (s < NR_SUPER + super_block) // 遍历超级块数组
        if (s->s_dev == dev)           // 找到指定设备的超级块
        {
            wait_on_super(s);    // 等待超级块解锁
            if (s->s_dev == dev) // 再次检查设备号(防止等待期间超级块被释放)
                return s;
            s = 0 + super_block; // 如果设备号改变,重新开始搜索
        }
        else
            s++; // 检查下一个超级块
    return NULL; // 未找到
}

/*
 * 释放指定设备的超级块资源
 * 参数:dev - 设备号
 */
void put_super(int dev)
{
    struct super_block *sb;
    int i;

    if (dev == ROOT_DEV) // 根设备不能释放
    {
        printk("root diskette changed: prepare for armageddon\n\r");
        return;
    }
    if (!(sb = get_super(dev))) // 获取超级块,如果不存在则返回
        return;
    if (sb->s_imount) // 如果超级块有挂载的文件系统
    {
        printk("Mounted disk changed - tssk, tssk\n\r");
        return;
    }
    lock_super(sb); // 锁定超级块
    sb->s_dev = 0;  // 清除设备号,标记该超级块空闲
    // 释放i节点位图缓冲区
    for (i = 0; i < I_MAP_SLOTS; i++)
        brelse(sb->s_imap[i]);
    // 释放块位图缓冲区
    for (i = 0; i < Z_MAP_SLOTS; i++)
        brelse(sb->s_zmap[i]);
    free_super(sb); // 释放超级块锁
    return;
}

/*
 * 从设备读取超级块到内存
 * 参数:dev - 设备号
 * 返回:超级块指针或NULL
 */
static struct super_block *read_super(int dev)
{
    struct super_block *s;
    struct buffer_head *bh;
    int i, block;

    if (!dev) // 设备号为0则返回NULL
        return NULL;
    check_disk_change(dev);   // 检查磁盘是否更换(如软盘)
    if ((s = get_super(dev))) // 如果超级块已在内存中,直接返回
        return s;
    // 寻找一个空闲的超级块槽位
    for (s = 0 + super_block;; s++)
    {
        if (s >= NR_SUPER + super_block) // 遍历完所有槽位
            return NULL;
        if (!s->s_dev) // 找到空闲槽位(s_dev为0)
            break;
    }
    s->s_dev = dev;     // 设置设备号
    s->s_isup = NULL;   // 初始化挂载的根i节点指针
    s->s_imount = NULL; // 初始化挂载点i节点指针
    s->s_time = 0;      // 初始化超级块修改时间
    s->s_rd_only = 0;   // 初始化只读标志
    s->s_dirt = 0;      // 初始化脏标志
    lock_super(s);      // 锁定超级块
    // 读取磁盘上第1块(超级块所在块,0是引导块)
    if (!(bh = bread(dev, 1)))
    {
        s->s_dev = 0; // 读取失败,释放超级块
        free_super(s);
        return NULL;
    }
    // 将磁盘超级块数据复制到内存超级块(强制类型转换)
    *((struct d_super_block *)s) =
        *((struct d_super_block *)bh->b_data);
    brelse(bh); // 释放缓冲区
    // 检查魔数,验证是否是有效的文件系统
    if (s->s_magic != SUPER_MAGIC)
    {
        s->s_dev = 0;
        free_super(s);
        return NULL;
    }
    // 初始化位图指针数组为NULL
    for (i = 0; i < I_MAP_SLOTS; i++)
        s->s_imap[i] = NULL;
    for (i = 0; i < Z_MAP_SLOTS; i++)
        s->s_zmap[i] = NULL;
    // 读取i节点位图块(从块2开始)
    block = 2;
    for (i = 0; i < s->s_imap_blocks; i++)
        if ((s->s_imap[i] = bread(dev, block)))
            block++;
        else
            break;
    // 读取块位图块
    for (i = 0; i < s->s_zmap_blocks; i++)
        if ((s->s_zmap[i] = bread(dev, block)))
            block++;
        else
            break;
    // 检查是否成功读取所有位图块
    if (block != 2 + s->s_imap_blocks + s->s_zmap_blocks)
    {
        // 读取失败,释放已分配的位图缓冲区
        for (i = 0; i < I_MAP_SLOTS; i++)
            brelse(s->s_imap[i]);
        for (i = 0; i < Z_MAP_SLOTS; i++)
            brelse(s->s_zmap[i]);
        s->s_dev = 0;
        free_super(s);
        return NULL;
    }
    // 设置位图的第0位(保留,不使用0号i节点和0号块)
    s->s_imap[0]->b_data[0] |= 1;
    s->s_zmap[0]->b_data[0] |= 1;
    free_super(s); // 释放超级块锁
    return s;
}

/*
 * 系统调用 - 卸载文件系统
 * 参数:dev_name - 设备文件名
 * 返回:0成功,负的错误码
 */
int sys_umount(char *dev_name)
{
    struct m_inode *inode;
    struct super_block *sb;
    int dev;

    // 获取设备文件对应的i节点
    if (!(inode = namei(dev_name)))
        return -ENOENT;          // 设备文件不存在
    dev = inode->i_zone[0];      // 从i节点中获取设备号(块设备的i_zone[0]存储设备号)
    if (!S_ISBLK(inode->i_mode)) // 检查是否是块设备
    {
        iput(inode);
        return -ENOTBLK; // 不是块设备
    }
    iput(inode);         // 释放设备文件i节点
    if (dev == ROOT_DEV) // 不能卸载根设备
        return -EBUSY;
    // 获取超级块,检查是否有挂载的文件系统
    if (!(sb = get_super(dev)) || !(sb->s_imount))
        return -ENOENT;
    if (!sb->s_imount->i_mount) // 一致性检查(应该为1)
        printk("Mounted inode has i_mount=0\n");
    // 检查是否有进程正在使用该设备上的文件
    for (inode = inode_table + 0; inode < inode_table + NR_INODE; inode++)
        if (inode->i_dev == dev && inode->i_count)
            return -EBUSY; // 设备忙,有i节点正在使用
    // 清除挂载标志
    sb->s_imount->i_mount = 0;
    iput(sb->s_imount); // 释放挂载点i节点
    sb->s_imount = NULL;
    iput(sb->s_isup); // 释放文件系统根i节点
    sb->s_isup = NULL;
    put_super(dev); // 释放超级块资源
    sync_dev(dev);  // 同步设备缓冲区
    return 0;
}

/*
 * 系统调用 - 挂载文件系统
 * 参数:dev_name - 设备文件名,dir_name - 挂载目录名,rw_flag - 读写标志
 * 返回:0成功,负的错误码
 */
int sys_mount(char *dev_name, char *dir_name, int rw_flag)
{
    struct m_inode *dev_i, *dir_i;
    struct super_block *sb;
    int dev;

    // 获取设备文件i节点
    if (!(dev_i = namei(dev_name)))
        return -ENOENT;
    dev = dev_i->i_zone[0];      // 获取设备号
    if (!S_ISBLK(dev_i->i_mode)) // 检查是否是块设备
    {
        iput(dev_i);
        return -EPERM; // 权限错误
    }
    iput(dev_i); // 释放设备文件i节点
    // 获取挂载目录i节点
    if (!(dir_i = namei(dir_name)))
        return -ENOENT;
    // 检查挂载目录:引用计数必须为1(只有当前引用),且不能是根i节点
    if (dir_i->i_count != 1 || dir_i->i_num == ROOT_INO)
    {
        iput(dir_i);
        return -EBUSY; // 目录忙
    }
    if (!S_ISDIR(dir_i->i_mode)) // 检查是否是目录
    {
        iput(dir_i);
        return -EPERM; // 权限错误
    }
    // 读取设备超级块
    if (!(sb = read_super(dev)))
    {
        iput(dir_i);
        return -EBUSY; // 设备忙或无效
    }
    if (sb->s_imount) // 检查文件系统是否已挂载
    {
        iput(dir_i);
        return -EBUSY;
    }
    if (dir_i->i_mount) // 检查目录是否已是挂载点
    {
        iput(dir_i);
        return -EPERM;
    }
    sb->s_imount = dir_i; // 设置超级块的挂载点i节点
    dir_i->i_mount = 1;   // 设置目录的挂载标志
    dir_i->i_dirt = 1;    // 标记目录i节点为脏(需要写回磁盘)
    // 注意:这里没有调用iput(dir_i),将在umount时释放
    return 0;
}

/*
 * 挂载根文件系统(系统初始化时调用)
 */
void mount_root(void)
{
    int i, free;
    struct super_block *p;
    struct m_inode *mi;

    // 检查i节点结构大小是否正确(编译时检查)
    if (32 != sizeof(struct d_inode))
        panic("bad i-node size");
    // 初始化文件表(文件描述符表)
    for (i = 0; i < NR_FILE; i++)
        file_table[i].f_count = 0;
    // 如果是软驱设备,提示插入根文件系统软盘
    if (MAJOR(ROOT_DEV) == 2) // 主设备号2是软驱
    {
        printk("Insert root floppy and press ENTER");
        wait_for_keypress(); // 等待按键
    }
    // 初始化所有超级块
    for (p = &super_block[0]; p < &super_block[NR_SUPER]; p++)
    {
        p->s_dev = 0;     // 标记为空闲
        p->s_lock = 0;    // 未锁定
        p->s_wait = NULL; // 等待队列为空
    }
    // 读取根设备超级块
    if (!(p = read_super(ROOT_DEV)))
        panic("Unable to mount root"); // 失败则宕机
    // 获取根i节点(ROOT_INO通常为1)
    if (!(mi = iget(ROOT_DEV, ROOT_INO)))
        panic("Unable to read root i-node");
    mi->i_count += 3;             // 增加引用计数:当前进程、根目录、工作目录
    p->s_isup = p->s_imount = mi; // 设置超级块的根i节点和挂载点
    current->pwd = mi;            // 设置当前进程的工作目录
    current->root = mi;           // 设置当前进程的根目录
    // 计算空闲块数量
    free = 0;
    i = p->s_nzones; // 文件系统总块数
    while (--i >= 0)
        if (!set_bit(i & 8191, p->s_zmap[i >> 13]->b_data))
            free++; // 如果位为0,表示空闲块
    printk("%d/%d free blocks\n\r", free, p->s_nzones);
    // 计算空闲i节点数量
    free = 0;
    i = p->s_ninodes + 1; // 文件系统总i节点数+1
    while (--i >= 0)
        if (!set_bit(i & 8191, p->s_imap[i >> 13]->b_data))
            free++; // 如果位为0,表示空闲i节点
    printk("%d/%d free inodes\n\r", free, p->s_ninodes);
}

5.源码图像版

【Linux102】61-fs/super.c-1

【Linux102】61-fs/super.c-2

6.源码注释版图像

【Linux102】61-fs/super.c-1

【Linux102】61-fs/super.c-2



Linux102系列

本系列将精讲Linux0.11内核中的每一个文件,共计会发布100+文章。


0.一些辅助文件

😉【Linux102】1-Makefile

😉【Linux102】2-Makefile.header

😉【Linux102】3-system.map


1.内核引导启动程序

😉【Linux102】4-bootsect.s

😉【Linux102】5-setup.s

😉【Linux102】6-head.s

😉【Linux102-D】/boot


2.内核初始化过程

😉【Linux102】7-main.c


3.进程调度与系统调用

😉【Linux102】8-kernel/asm.s

😉【Linux102】9-kernel/traps.c

😉【Linux102】10-kernel/printk.c

😉【Linux102】11-kernel/vsprintf.c

😉【Linux102】12-include/stdarg.h

😉【Linux102】13-kernel/mktime.c

😉【Linux102】14-kernel/system_call.s

😉【Linux102】15-include/linux/sched.h

😉【Linux102】16-kernel/sched.c

😉【Linux102】17-kernel/signal.c

😉【Linux102】18-include/signal.h

😉【Linux102】19-include/sys/types.h

😉【Linux102】20-include/linux/kernel.h

😉【Linux102】21-include/asm/segment.h

😉【Linux102】22-include/linux/head.h

😉【Linux102】23-include/linux/mm.h

😉【Linux102】24-include/linux/fs.h

😉【Linux102】25-include/errno.h

😉【Linux102】26-include/sys/wait.h

😉【Linux102】27-include/inux/tty.h

😉【Linux102】28-include/termios.h

😉【Linux102】29-kernel/panic.c

😉【Linux102】30-include/sys/times.h

😉【Linux102】31-include/sys/utsname.h

😉【Linux102】32-include/stddef.h

😉【Linux102】33-include/linux/sys.h

😉【Linux102】34-kernel/sys.c

😉【Linux102】35-kernel/fork.c

😉【Linux102】36-include/asm/system.h

😉【Linux102】37-kernel/exit.c

😉【Linux102】38-include/linux/fdreg.h

😉【Linux102】39-include/asm/io.h


4.输入输出系统--块设备驱动程序

😉【Linux102】40-kernel/blk_drv/blk.h

😉【Linux102】41-kernel/blk_drv/hd.c

😉【Linux102】42-include/linux/config.h

😉【Linux102】43-include/linux/hdreg.h

😉【Linux102】45-kernel/blk_drv/ramdisk.c

😉【Linux102】46-include/asm/memory.h

😉【Linux102】47-include/string.h

😉【Linux102】48-kernel/blk_drv/floppy.c


5.输入输出系统——字符设备驱动程序

😉【Linux102】49-kernel/chr_drv/keyboard.S

😉【Linux102】50-kernel/chr_drv/console.c

😉【Linux102】51-kernel/chr_drv/serial.c

😉【Linux102】52-kernel/chr_drv/rs_io.s

😉【Linux102】53-kernel/chr_drv/tty_io.c

😉【Linux102】54-include/ctype.h

😉【Linux102】55-lib/ctype.c

😉【Linux102】56-kernel/chr_drv/tty_ioctl.c


Linux内核精讲系列

和Linux内核102系列不同,本系列将会从全局描绘Linux内核的各个模块,而非逐行源码分析,适合想对Linux系统有宏观了解的家人阅读。

😉【Linux】学习Linux前必备的知识点

😉【Linux】Linux内核对进程的内存抽象

😉【Linux】Linux概述1-linux对物理内存的使用

😉【Linux】软件从写下到运行的全部流程

😉【Linux】CPU的三寻址:实模式、保护模式、长模式

😉【Linux】实模式与保护模式的寻址, 这次讲明白了!

😉【Linux】linux0.11的源码目录架构

😉【Linux】Makefile机制及基础详解

😉【Linux】编译并运行Linux0.11

😉【Linux】“进进内网文”—Linux的内核结构全貌

😉【Linux】linux的中断机制

😉【Linux】linux进程描述


汇编语言

本系列将带领大家从0开始循序渐进学习汇编语言,直至完全掌握这门底层语言。同时给出学习平台DOSBox的使用教程。

😉【汇编语言】1—基础硬件知识

😉【汇编语言】2—寄存器基础知识

😉【汇编语言】3-寄存器与内存的交互

😉【汇编语言】4-第一个完整的汇编程序

😉【汇编语言】5-[BX]和loop指令

😉【汇编语言】6-包含多个段的程序

😉【汇编语言】7-灵活的5大寻址方式

😉【汇编语言】8-1-数据的位置

😉【汇编语言】8-2-数据的长度

😉【汇编语言】8-数据处理的两个基本问题(位置与长度)

😉【DOSBox】1-debug

😉【DOSBox】2-debug可执行文件

😉【DOSBox】3-完整开发流程


C语言

本系列将直击C语言的本质基础,流利处理出各个易错、实用的实战点,并非从零开始学习C。

😉【C语言】C Token(C89 C99 C11)

😉【C语言】指针基础

😉【C语言】数组基础

😉【C语言】结构体对齐

😉【C语言】华为C语言进阶测试

😉【C语言】触发刷新到磁盘的方式总结

😉【C语言】C语言文件操作的mode详解

😉【C语言】C语言文件知识全讲解

😉【C语言】从extern到头文件包含的最优实践

😉【C语言】C语言的关键字与重载机制

😉【C语言】长字符串的2种处理方式

😉【C语言】C语言嵌入汇编程序

😉【C语言】指针数组 VS 数组指针 原来这么简单!

😉【C语言】深浅拷贝、传参、赋值 本质剖析

😉【C语言】find-in-linux递归搜索文件名函数

😉【C陷阱与缺陷】-1-词法陷阱

😉【C陷阱与缺陷】-2-语法陷阱

😉【C陷阱与缺陷】-3-语义陷阱


关于小希

😉嘿嘿嘿,我是小希,专注Linux内核领域,同时讲解C语言汇编等知识。

我的微信:C_Linux_Cloud,期待与您学习交流!

加微信请备注哦


小希的座右铭:别看简单,简单也是难。别看难,难也是简单。我的文章都是讲述简单的知识,如果你喜欢这种风格:

不妨关注、评论、转发,让更多朋友看到哦~~~🙈

下一期想看什么?在评论区留言吧!我们下期见!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值