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.h | config.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.h | include/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.源码图像版


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

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


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



