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.源码图像版

6.源码注释版图像


本系列将精讲Linux0.11内核中的每一个文件,共计会发布100+文章。
😉【Linux102】11-kernel/vsprintf.c
😉【Linux102】12-include/stdarg.h
😉【Linux102】14-kernel/system_call.s
😉【Linux102】15-include/linux/sched.h
😉【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】26-include/sys/wait.h
😉【Linux102】27-include/inux/tty.h
😉【Linux102】28-include/termios.h
😉【Linux102】30-include/sys/times.h
😉【Linux102】31-include/sys/utsname.h
😉【Linux102】32-include/stddef.h
😉【Linux102】33-include/linux/sys.h
😉【Linux102】36-include/asm/system.h
😉【Linux102】38-include/linux/fdreg.h
😉【Linux102】39-include/asm/io.h
😉【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
😉【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】56-kernel/chr_drv/tty_ioctl.c
和Linux内核102系列不同,本系列将会从全局描绘Linux内核的各个模块,而非逐行源码分析,适合想对Linux系统有宏观了解的家人阅读。
😉【Linux】Linux概述1-linux对物理内存的使用
本系列将带领大家从0开始循序渐进学习汇编语言,直至完全掌握这门底层语言。同时给出学习平台DOSBox的使用教程。
本系列将直击C语言的本质基础,流利处理出各个易错、实用的实战点,并非从零开始学习C。
关于小希
😉嘿嘿嘿,我是小希,专注Linux内核领域,同时讲解C语言、汇编等知识。
我的微信:C_Linux_Cloud,期待与您学习交流!

加微信请备注哦
小希的座右铭:
别看简单,简单也是难。别看难,难也是简单。我的文章都是讲述简单的知识,如果你喜欢这种风格:
下一期想看什么?在评论区留言吧!我们下期见!


被折叠的 条评论
为什么被折叠?



