【Linux102】59-fs/bitmap.c


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

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

1. bitmap.c的主要作用

bitmap.c程序 的功能和作用既简单清晰,主要用于对i节点位图逻辑位图进行释放占用处理。

①总体函数介绍

操作i节点位图的函数是free_inode()new_inode(),操作逻辑块位图的函数是free_block()new_block()

②freeblock()

函数freeblock()用于释放指定设备dev上数据区中的逻辑块block。具体操作是复位指定逻辑块block对应逻辑块位图中的比特位。它首先取指定设备dev的超级块,并根据超级块上给出的设备数据逻辑块的范围,判断逻辑块号block的有效性。 然后在高速缓冲区中进行查找,看看指定的逻辑块现在是否正在高速缓冲区中,若是,则将对应的缓冲块释放掉。 接着计算block从数据区开始算起的数据逻辑块号(从1开始计数),并对逻辑块(区段)位图进行操作,复位对应的比特位。最后根据逻辑块号设置相应逻辑块位图在缓冲区中对应的缓冲块的已修改标志。

③new_block()

函数new_block()用于向设备dev申请一个逻辑块,返回逻辑块号。并置位指定逻辑块 block对应的逻辑块位图比特位。 它首先取指定设备dev的超级块。 然后对整个逻辑块位图进行搜索,寻找第一个是0的比特位。若没有找到,则说明盘设备空间已用完,返回0。否则将该比特位置为1,表示占用对应的数据逻辑块。并将该比特位所在缓冲块的已修改标志置位。接着计算出数据逻辑块盘块号,并在高速缓冲区中申请相应的缓冲块,并把该缓冲块清零。 然后设置该缓冲块的已更新和已修改标志。 最后释放该缓冲块,以便其他程序使用,并返回盘块号逻辑块号)。

④free_inode()

函数free_inode() 用于释放指定的i节点,并复位对应的i节点位图比特位

⑤new_inode()

new_inode()用于为设备dev建立一个新i节点。 返回该新i节点的指针。主要操作过程是在内存i节点表中获取一个空闲i节点表项,并从i节点位图中找一个空闲i节点。 这两个函数的处理过程与上述两个函数类似,因此这里就不再赘述。


2.源码用到的文件

#include <string.h>
#include <linux/sched.h>
#include <linux/kernel.h>

文件作用
😉【Linux102】47-include/string.h声明了一系列标准的字符串操作函数和内存块操作函数。
😉【Linux102】15-include/linux/sched.h本程序主要定义了进程调度的相关数据结构,如任务数据结构等等,理解这些数据结构是理解进程调度机制的关键,也是理解内核的关键。一句话:**我们常说的进程是XXX,但是进程究竟是什么?**就定义在这个文件里面!源码面前了,没有秘密!
😉【Linux102】40-kernel/blk_drv/blk.h这是有关硬盘块设备参数的头文件,因为只用于块设备,所以与块设备代码放在同一个地方。其中主要定义了请求等待队列的数据结构request,用宏语句定义了电梯搜索算法,并对内核目前支持的虚拟盘硬盘软盘三种块设备,根据它们各自的主设备号分别设定了常数值。

3.源码版

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

/* bitmap.c contains the code that handles the inode and block bitmaps */
#include <string.h>

#include <linux/sched.h>
#include <linux/kernel.h>

#define clear_block(addr)                  \
    __asm__ __volatile__("cld\n\t"         \
                         "rep\n\t"         \
                         "stosl" ::"a"(0), \
                         "c"(BLOCK_SIZE / 4), "D"((long)(addr)))

#define set_bit(nr, addr) ({\
register int res ; \
__asm__ __volatile__("btsl %2,%3\n\tsetb %%al": \
"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \
res; })

#define clear_bit(nr, addr) ({\
register int res ; \
__asm__ __volatile__("btrl %2,%3\n\tsetnb %%al": \
"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \
res; })

#define find_first_zero(addr) ({ \
int __res; \
__asm__ __volatile__ ("cld\n" \
	"1:\tlodsl\n\t" \
	"notl %%eax\n\t" \
	"bsfl %%eax,%%edx\n\t" \
	"je 2f\n\t" \
	"addl %%edx,%%ecx\n\t" \
	"jmp 3f\n" \
	"2:\taddl $32,%%ecx\n\t" \
	"cmpl $8192,%%ecx\n\t" \
	"jl 1b\n" \
	"3:" \
	:"=c" (__res):"c" (0),"S" (addr)); \
__res; })

void free_block(int dev, int block)
{
    struct super_block *sb;
    struct buffer_head *bh;

    if (!(sb = get_super(dev)))
        panic("trying to free block on nonexistent device");
    if (block < sb->s_firstdatazone || block >= sb->s_nzones)
        panic("trying to free block not in datazone");
    bh = get_hash_table(dev, block);
    if (bh)
    {
        if (bh->b_count != 1)
        {
            printk("trying to free block (%04x:%d), count=%d\n",
                   dev, block, bh->b_count);
            return;
        }
        bh->b_dirt = 0;
        bh->b_uptodate = 0;
        brelse(bh);
    }
    block -= sb->s_firstdatazone - 1;
    if (clear_bit(block & 8191, sb->s_zmap[block / 8192]->b_data))
    {
        printk("block (%04x:%d) ", dev, block + sb->s_firstdatazone - 1);
        panic("free_block: bit already cleared");
    }
    sb->s_zmap[block / 8192]->b_dirt = 1;
}

int new_block(int dev)
{
    struct buffer_head *bh;
    struct super_block *sb;
    int i, j;

    if (!(sb = get_super(dev)))
        panic("trying to get new block from nonexistant device");
    j = 8192;
    for (i = 0; i < 8; i++)
        if ((bh = sb->s_zmap[i]))
            if ((j = find_first_zero(bh->b_data)) < 8192)
                break;
    if (i >= 8 || !bh || j >= 8192)
        return 0;
    if (set_bit(j, bh->b_data))
        panic("new_block: bit already set");
    bh->b_dirt = 1;
    j += i * 8192 + sb->s_firstdatazone - 1;
    if (j >= sb->s_nzones)
        return 0;
    if (!(bh = getblk(dev, j)))
        panic("new_block: cannot get block");
    if (bh->b_count != 1)
        panic("new block: count is != 1");
    clear_block(bh->b_data);
    bh->b_uptodate = 1;
    bh->b_dirt = 1;
    brelse(bh);
    return j;
}

void free_inode(struct m_inode *inode)
{
    struct super_block *sb;
    struct buffer_head *bh;

    if (!inode)
        return;
    if (!inode->i_dev)
    {
        memset(inode, 0, sizeof(*inode));
        return;
    }
    if (inode->i_count > 1)
    {
        printk("trying to free inode with count=%d\n", inode->i_count);
        panic("free_inode");
    }
    if (inode->i_nlinks)
        panic("trying to free inode with links");
    if (!(sb = get_super(inode->i_dev)))
        panic("trying to free inode on nonexistent device");
    if (inode->i_num < 1 || inode->i_num > sb->s_ninodes)
        panic("trying to free inode 0 or nonexistant inode");
    if (!(bh = sb->s_imap[inode->i_num >> 13]))
        panic("nonexistent imap in superblock");
    if (clear_bit(inode->i_num & 8191, bh->b_data))
        printk("free_inode: bit already cleared.\n\r");
    bh->b_dirt = 1;
    memset(inode, 0, sizeof(*inode));
}

struct m_inode *new_inode(int dev)
{
    struct m_inode *inode;
    struct super_block *sb;
    struct buffer_head *bh;
    int i, j;

    if (!(inode = get_empty_inode()))
        return NULL;
    if (!(sb = get_super(dev)))
        panic("new_inode with unknown device");
    j = 8192;
    for (i = 0; i < 8; i++)
        if ((bh = sb->s_imap[i]))
            if ((j = find_first_zero(bh->b_data)) < 8192)
                break;
    if (!bh || j >= 8192 || j + i * 8192 > sb->s_ninodes)
    {
        iput(inode);
        return NULL;
    }
    if (set_bit(j, bh->b_data))
        panic("new_inode: bit already set");
    bh->b_dirt = 1;
    inode->i_count = 1;
    inode->i_nlinks = 1;
    inode->i_dev = dev;
    inode->i_uid = current->euid;
    inode->i_gid = current->egid;
    inode->i_dirt = 1;
    inode->i_num = j + i * 8192;
    inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
    return inode;
}

4.源码注释版本


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

/* bitmap.c 包含处理inode和块位图的代码 */
#include <string.h>

#include <linux/sched.h>
#include <linux/kernel.h>

/* 清除一个内存块(设置为0) */
#define clear_block(addr)                  \
    __asm__ __volatile__("cld\n\t"         \  // 清除方向标志(向前移动)
                         "rep\n\t"         \  // 重复执行
                         "stosl" ::"a"(0), \  // 将EAX(0)存储到EDI指向的位置
                         "c"(BLOCK_SIZE / 4), "D"((long)(addr))) // 计数=块大小/4,目标地址=addr

/* 设置指定位 */
#define set_bit(nr, addr) ({\
register int res ; \
__asm__ __volatile__("btsl %2,%3\n\tsetb %%al": \  // 位测试并设置,然后根据CF设置AL
"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \
res; })

/* 清除指定位 */
#define clear_bit(nr, addr) ({\
register int res ; \
__asm__ __volatile__("btrl %2,%3\n\tsetnb %%al": \  // 位测试并复位,然后根据CF设置AL
"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \
res; })

/* 查找第一个零位(空闲位) */
#define find_first_zero(addr) ({ \
int __res; \
__asm__ __volatile__ ("cld\n" \          // 清除方向标志
	"1:\tlodsl\n\t" \                   // 加载ESI到EAX,ESI增加4
	"notl %%eax\n\t" \                  // 按位取反(0变1,1变0)
	"bsfl %%eax,%%edx\n\t" \            // 从低位扫描,查找第一个设置位
	"je 2f\n\t" \                       // 如果没找到(全0),跳转到标签2
	"addl %%edx,%%ecx\n\t" \            // 将找到的位偏移加到ECX
	"jmp 3f\n" \                        // 跳转到标签3(结束)
	"2:\taddl $32,%%ecx\n\t" \          // 增加32位(检查下一个字)
	"cmpl $8192,%%ecx\n\t" \            // 比较是否达到8192(位图大小)
	"jl 1b\n" \                         // 如果小于,继续循环
	"3:" \                              // 结束标签
	:"=c" (__res):"c" (0),"S" (addr)); \  // 输出:ECX到__res,输入:ECX=0,ESI=addr
__res; })

/* 释放数据块 */
void free_block(int dev, int block)
{
    struct super_block *sb;
    struct buffer_head *bh;

    // 获取设备超级块
    if (!(sb = get_super(dev)))
        panic("trying to free block on nonexistent device");
    // 检查块号是否在数据区范围内
    if (block < sb->s_firstdatazone || block >= sb->s_nzones)
        panic("trying to free block not in datazone");
    // 获取块的缓冲头
    bh = get_hash_table(dev, block);
    if (bh)
    {
        // 检查引用计数
        if (bh->b_count != 1)
        {
            printk("trying to free block (%04x:%d), count=%d\n",
                   dev, block, bh->b_count);
            return;
        }
        // 清除脏位和更新标志
        bh->b_dirt = 0;
        bh->b_uptodate = 0;
        // 释放缓冲区
        brelse(bh);
    }
    // 计算位图中的位置(减去数据区起始偏移)
    block -= sb->s_firstdatazone - 1;
    // 清除位图中的位(标记为空闲)
    if (clear_bit(block & 8191, sb->s_zmap[block / 8192]->b_data))
    {
        // 如果位已经是清除状态,报错
        printk("block (%04x:%d) ", dev, block + sb->s_firstdatazone - 1);
        panic("free_block: bit already cleared");
    }
    // 标记位图缓冲区为脏(需要写回磁盘)
    sb->s_zmap[block / 8192]->b_dirt = 1;
}

/* 分配新数据块 */
int new_block(int dev)
{
    struct buffer_head *bh;
    struct super_block *sb;
    int i, j;

    // 获取设备超级块
    if (!(sb = get_super(dev)))
        panic("trying to get new block from nonexistant device");
    j = 8192; // 初始化为最大位图索引
    // 遍历所有块位图(共8个,每个管理8192个块)
    for (i = 0; i < 8; i++)
        if ((bh = sb->s_zmap[i]))
            // 查找第一个空闲位
            if ((j = find_first_zero(bh->b_data)) < 8192)
                break;
    // 检查是否找到空闲块
    if (i >= 8 || !bh || j >= 8192)
        return 0;
    // 设置位图中的位(标记为已使用)
    if (set_bit(j, bh->b_data))
        panic("new_block: bit already set");
    // 标记位图缓冲区为脏
    bh->b_dirt = 1;
    // 计算实际块号(加上数据区起始偏移)
    j += i * 8192 + sb->s_firstdatazone - 1;
    // 检查块号是否有效
    if (j >= sb->s_nzones)
        return 0;
    // 获取新块的缓冲区
    if (!(bh = getblk(dev, j)))
        panic("new_block: cannot get block");
    // 检查引用计数
    if (bh->b_count != 1)
        panic("new block: count is != 1");
    // 清空块数据(填充0)
    clear_block(bh->b_data);
    // 设置更新标志和脏标志
    bh->b_uptodate = 1;
    bh->b_dirt = 1;
    // 释放缓冲区(但块仍被分配)
    brelse(bh);
    return j; // 返回分配的块号
}

/* 释放inode */
void free_inode(struct m_inode *inode)
{
    struct super_block *sb;
    struct buffer_head *bh;

    // 检查inode是否有效
    if (!inode)
        return;
    // 检查设备号是否有效
    if (!inode->i_dev)
    {
        memset(inode, 0, sizeof(*inode));
        return;
    }
    // 检查引用计数
    if (inode->i_count > 1)
    {
        printk("trying to free inode with count=%d\n", inode->i_count);
        panic("free_inode");
    }
    // 检查链接数(应为0才能释放)
    if (inode->i_nlinks)
        panic("trying to free inode with links");
    // 获取设备超级块
    if (!(sb = get_super(inode->i_dev)))
        panic("trying to free inode on nonexistent device");
    // 检查inode号是否有效
    if (inode->i_num < 1 || inode->i_num > sb->s_ninodes)
        panic("trying to free inode 0 or nonexistant inode");
    // 获取inode位图(每个位图管理8192个inode)
    if (!(bh = sb->s_imap[inode->i_num >> 13]))
        panic("nonexistent imap in superblock");
    // 清除位图中的位(标记为空闲)
    if (clear_bit(inode->i_num & 8191, bh->b_data))
        printk("free_inode: bit already cleared.\n\r");
    // 标记位图缓冲区为脏
    bh->b_dirt = 1;
    // 清空inode结构
    memset(inode, 0, sizeof(*inode));
}

/* 分配新inode */
struct m_inode *new_inode(int dev)
{
    struct m_inode *inode;
    struct super_block *sb;
    struct buffer_head *bh;
    int i, j;

    // 获取空闲inode结构
    if (!(inode = get_empty_inode()))
        return NULL;
    // 获取设备超级块
    if (!(sb = get_super(dev)))
        panic("new_inode with unknown device");
    j = 8192; // 初始化为最大位图索引
    // 遍历所有inode位图(共8个,每个管理8192个inode)
    for (i = 0; i < 8; i++)
        if ((bh = sb->s_imap[i]))
            // 查找第一个空闲位
            if ((j = find_first_zero(bh->b_data)) < 8192)
                break;
    // 检查是否找到空闲inode
    if (!bh || j >= 8192 || j + i * 8192 > sb->s_ninodes)
    {
        iput(inode); // 释放inode结构
        return NULL;
    }
    // 设置位图中的位(标记为已使用)
    if (set_bit(j, bh->b_data))
        panic("new_inode: bit already set");
    // 标记位图缓冲区为脏
    bh->b_dirt = 1;
    // 设置inode属性
    inode->i_count = 1;           // 引用计数
    inode->i_nlinks = 1;          // 链接数
    inode->i_dev = dev;           // 设备号
    inode->i_uid = current->euid; // 用户ID
    inode->i_gid = current->egid; // 组ID
    inode->i_dirt = 1;            // 脏标志
    inode->i_num = j + i * 8192;  // 计算inode号
    inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; // 设置时间
    return inode; // 返回新inode
}

5.源码图像版

【Linux102】59-fs/bitmap.c


6.源码注释版图像

【Linux102】59-fs/bitmap.c



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,期待与您学习交流!

加微信请备注哦


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

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

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

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值