当前位置: 首页 > news >正文

linux0.11内核源码修仙传第十一章——硬盘初始化

🚀 前言

    本文是初始化最后一部分了,对硬盘的初始化,对应于书中的第20回。希望各位给个三连,拜托啦,这对我真的很重要!!!

目录

  • 🚀 前言
  • 🏆块设备管理
  • 🏆块设备初始化
  • 🏆块设备读取
  • 🎯总结
  • 📖参考资料

🏆块设备管理

    关于块设备,可以参考之前的博客:linux0.11内核源码修仙传第七章——块设备请求项初始化。在内核中管理块设备采用了一个结构体blk_dev进行管理,每一个索引表示一个块设备,每一个块设备的详细含义如下注释所示:

struct blk_dev_struct blk_dev[NR_BLK_DEV] = {
	{ NULL, NULL },		/* 表示没有块设备,访问会返回错误或者提示无有效设备 */
	{ NULL, NULL },		/* 内存设备 */
	{ NULL, NULL },		/* 软盘设备 */
	{ NULL, NULL },		/* 硬盘设备 */
	{ NULL, NULL },		/* 特定的终端设备 */
	{ NULL, NULL },		/* 终端设备 */
	{ NULL, NULL }		/* 打印机设备 */
};

    从上面的定义可以看到,索引为3的位置(第四个)表示硬盘。这个数组里面每个成员的定义如下:

struct request {
	int dev;		/* -1 if no request */
	int cmd;		/* READ or WRITE */
	int errors;
	unsigned long sector;
	unsigned long nr_sectors;
	char * buffer;
	struct task_struct * waiting;
	struct buffer_head * bh;
	struct request * next;
};

struct blk_dev_struct {
	void (*request_fn)(void);		// 处理函数
	struct request * current_request;	// 块设备的请求
};

    结合上面的blk_dev数组,可以发现一个有意思的事情:每个块设备执行读写请求都有自己的函数,在上层看来都是一个统一函数request_fn,具体实现各有不同,对于硬盘来说,这个实现就是do_hd_request函数。换句话说,只要初始化了所有块设备的request_fn函数指针是哪个处理函数,后面就可以直接调用对应块设备的request_fn,而无需关心具体我该调用哪个函数名字。这个就是多态思想在C语言的体现。也就是C++中的父类指针request_fn指向子类对象do_hd_request,或者Java中的父类引用指向子类对象。

🏆块设备初始化

    下面说完前置的一些信息,来看具体的初始化函数:

void hd_init(void)
{
	blk_dev[3].request_fn = do_hd_request;
	set_intr_gate(0x2E,&hd_interrupt);
	outb_p(inb_p(0x21)&0xfb,0x21);
	outb(inb_p(0xA1)&0xbf,0xA1);
}

    首先像上一节所讲的,设置了硬盘设备的request_fn 所对应的函数。之后设置了一个中断,中断号为0x2E,中断处理函数是hd_interrupt。也就是说,当硬盘发生读写时,硬盘会发出中断信号给CPU,之后CPU陷入中断处理程序。

    这里有个区分,上面设置了一个do_hd_request,下面中断设置了一个hd_interrupt。这两个函数是不一样的,上面是发送请求的函数,下面的函数是读取中断。打个比方:一个是请假的打报告阶段,一个是实际请假后休假的阶段。

    回到初始化函数里面,最后就是往IO端口上读写,作用是允许硬盘控制器发送中断请求信号

🏆块设备读取

    这里给个引子,如何读取硬盘上的数据。首先看硬盘的端口表:
在这里插入图片描述
    读硬盘就是,往除了第一个以外的后面几个端口写数据,告诉要读硬盘的哪个扇区,读多少。然后再从 0x1F0 端口一个字节一个字节的读数据。这就完成了一次硬盘读操作。更具体如下:

0x1F2 写入要读取的扇区数
0x1F3 ~ 0x1F6 这四个端口写入计算好的起始 LBA 地址
0x1F7 处写入读命令的指令号
不断检测 0x1F7 (此时已成为状态寄存器的含义)的忙位
如果第四步骤为不忙,则开始不断从 0x1F0 处读取数据到内存指定位置,直到读完

    代码的话如下所示:

static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
		unsigned int head,unsigned int cyl,unsigned int cmd,
		void (*intr_addr)(void))
{
	···
	port=0x1f0;
	outb_p(hd_info[drive].wpcom>>2,++port);	// 0x1f1,错误寄存器
	outb_p(nsect,++port);					// 0x1f2,扇区计数器
	outb_p(sect,++port);					// 0x1f3,扇区号寄存器
	outb_p(cyl,++port);						// 0x1f4,磁道数低8位
	outb_p(cyl>>8,++port);					// 0x1f5,磁道数高8位
	outb_p(0xA0|(drive<<4)|head,++port);	// 0x1f6,驱动器
	outb(cmd,++port);						// 0x1f7,命令
}

🎯总结

    到这里,初始化就都结束了,目前为止的中断如下所示:

中断号中断处理函数
0 ~ 0x10trap_init 里设置的一堆
0x20timer_interrupt
0x21keyboard_interrupt
0x80system_call
0x2Ehd_interrupt

    整个操作系统就是一个靠中断驱动的死循环而已,如果不发生任何中断,操作系统会一直在一个死循环里等待。换句话说,让操作系统工作的唯一方式,就是触发中断。


📖参考资料

[1] linux源码趣读
[2] 一个64位操作系统的设计与实现

相关文章:

  • trae初体验-java开发
  • 网络相关知识总结2
  • 基于神经网络的文本分类的设计与实现
  • P3379 【模板】最近公共祖先(LCA)【题解】(重链剖分法)
  • Python 装饰器(Decorators)
  • 华为虚墙配置实验
  • FLEXlm如何通过web调用
  • 银河麒麟高级服务器操作系统基础docker镜像封装http和docker端口映射操作
  • 大模型AI Agent:简介(第一部分)—— 特性、组件、运作及应用全景
  • Nature招牌1区Top认证!可解释多模态融合模型取得重大突破!
  • 座舱与智驾“双轮驱动”,芯擎科技打造智能汽车“芯”标杆
  • 香港电讯企业托管服务,助企业实现高效IT管理与运营
  • MySQL8.4 InnoDB Cluster高可用集群使用指南
  • 嵌入式c学习第十天
  • 留记录excel 模板导入
  • 深度学习处理时间序列(3)
  • AOA与TOA混合定位,MATLAB例程,三维空间下的运动轨迹,滤波使用EKF,附下载链接
  • Promise详解
  • 食品级低聚木糖市场报告​:2024年全球食品级低聚木糖市场销售额达到了0.35亿美元
  • Spring Cloud ReactorServiceInstanceLoadBalancer 自定义负载均衡
  • 浙江推动人工智能终端消费:家居机器人纳入以旧换新补贴范围
  • 纽约市长称墨西哥海军帆船撞桥事故已致2人死亡
  • 芬兰西南部两架直升机相撞坠毁,第一批救援队已抵达现场
  • 王东杰评《国家与学术》︱不“国”不“故”的“国学”
  • 通往国际舞台之路:清政府与万国公会的交往
  • 我国城市规划“全面体检”套餐出台,城市体检将逐步与供地计划等挂钩