前言:
昨天我们已经闯过显示器的副本了,学习了和硬件沟通的技能,接下来我们来攻克计算机硬件中下一个重要的副本---硬盘,硬盘对于我们之后文件系统是必不可少的,话不多说我们开始进入副本
本日参考资料:
《操作系统真象还原》
我之前写的硬盘笔记
一,硬盘发展史
(小故事,不想看故事的可以跳过)
个人觉得,在计算机世界中,实现随机存取具有划时代的意义,程序中的算法不用再把存储时间考虑 进去,访问任意数据所用的时间几乎相等,这一改之前的存储设备其存取时间呈线性的历史。而改变这 历史的时间是 1956 月,世界上诞生了第 台磁盘存储设备 IBM 350 RAMAC (Random Access Method of Accounting and Control ),没错,又是 IBM ,这就是蓝色巨人的魅力,在几十年前己经为科技领航。此 设备用磁头来读写数据,用盘片来存储数据,以后的硬盘都是以这样的模式发展。这个磁头可以直接移动 到盘片上的任何 块存储单元,从而实现了随机存储。虽然它的总容量只有5MB,但是限于当时的制造 工艺水平,一共使用了 50 个直径为 24 英寸的盘片,摞起来的体积相当于冰箱那么大。在这些盘片表面都 涂有用于存储数据的磁性物质,它们摞起来被固定在一起,在电机的带动下,绕着同 个轴旋转。今天看 起来它显得过于笨重且容量小得以至于没什么用,但当时可是高大上的玩意儿,所以它在那时主要用于高 精尖领域昵,如航空业、银行业、医学领域及太空领域。 1968 年由 IBM 首次提出了“温彻斯特/Winchester的技术,这可是个了不起的技术。“温彻斯特”技 术的精髓是:“镀磁盘片在密封空间中高速自转,磁头悬浮在盘片上方,固定在磁头臂上沿盘片径向移动”。 磁头不与盘片接触也不应该接触,这是最容易想象的,如果磁头与盘片接触,在高速转动下摩擦,什么材料都会磨损,数据自然就丢啦。 另外,盘片自转速度是存取数据速度的关键,如果磁头与盘片接触, 受摩擦力的影响,想快也快不了。一个可行的方案是让磁头在盘片上方“悬浮”,与盘片保持非常近的距离, 类似咱们物理实验中的气垫导轨。盘片高速旋转会产生气流,磁头在这种气流下像飞碟一样悬浮,这样就保 证了不会与盘片有摩擦。磁头被固定在磁头臂上,它能沿盘片径向移动,由于磁头和盘片各自的运动,再加 上如此近的距离,所以哪怕一点灰尘都会造成磁盘的损伤。于是,磁头、盘片被密封在了一个盒子里。今天 的硬盘依然是这样的结构,如图 3-26 所示。 但这只是提出了这样的构想,此时还没有制造出这样的硬盘呢。思想是人家提出的,别人还真玩不转, 所以制造还得由人家 IBM 亲自来完成。世界上第一块基于“温彻斯特”技术的硬盘,在 1973 年诞生, 它就是所有硬盘的源头 IBM 3340 ,容量 60MB,由两个 30MB 的存储单元拼合而成。从此硬盘技术的发 展便有了成形的结构基础。所以,今天的硬盘也称为温盘,在一般的可编程中断控制器上连接硬盘的引脚 都标有温彻斯特硬盘呢 “温彻斯特”这个名字还有个典故, 些重大发明,其名字背后大多数都有个小故事。由于 IBM3340 拥有两个 。如囚的存储单元,而当时一种很有名的“温彻斯特来复枪”的口径和装药也恰好包含了两个数 字。于是这种硬盘的内部代号就被定为“温彻斯特”。据说电影《终结者》中施瓦辛格拿的枪便是“温 彻斯特来复枪”。 硬盘发展到今天,这几十年来都是靠容量不断提升才打败竞争对手存活下来的。可是速度一直是其最大的敌 人,家用电脑 10 年前硬盘主流转速是 7200 转/分钟,今天依然如此。硬盘的随机存取是靠磁头臂不断移动实现 的,磁头臂移动到目标位置的时间称为寻道时间,如果存储的数据不连续,这一块那一片的,磁头就得不断调整 位置,这是机械式硬盘不可避免的,这便是硬盘的瓶颈所在,所以 般的硬盘都将寻道时间作为重要参数。 在众多竞争对象当中,也有一款顽强地活了下来,它就是 SSD 固态硬盘,人家也有几十年的历史了。
二,工作原理
整个磁盘由以下核心部分组成:
磁头:磁头负责读取数据和写入数据,悬浮于盘片上盘片:盘片用于存储数据,且两面都有数据存在
扇区(512字节):一个盘片被等分的分成几个扇区(0~63大多数情况下是63个扇区)
柱面:一个扇区从里到外又被划分为几个柱面
磁道:由柱面形成的一个一个环
关于整个磁盘内部的运动我就不多阐述,我也层有幸拆开过一个机械硬盘,里面的盘片十分光滑且敲出来的声音很好听,有钱的可以拆开来把盘片当镜子使用
磁盘内部运动动画
三,硬盘控制器
CPU与硬盘的接口:硬盘控制器
有的人可能会问了,昨天说的显卡我经常见过,这个硬盘的接口硬盘控制器咋从来没见过呢,hhh,其实硬盘控制器已经和硬盘集成在一起,早期的硬盘和控制器其实也是分开的,不过之后合并了在了一起,这样的接口被称为集成设备电路IDE。
SATA:硬盘串行接口
PATA(ATA):并行接口
一个IDE线可以挂两块硬盘,一个主盘0,一个从盘1
端口
寄存器分为两个部分:command block reg 和 control block reg,我们重点来说command 里面的寄存器
端口是按通道分开的,所以一个通道的主从两块磁盘都用该通道上的端口号
Command block registers
- device(8位)
负责驱动设备设置
LBA即逻辑寻址模式,就是给定地址去寻址
CHS就是最传统的3D寻址模式,盘面,柱面,扇区这样子去寻道
-
error
读硬盘操作时,是error寄存器,会记录错误信息,未读扇区保存在sector count寄存器中
写磁盘操作时,是feature寄存器,写命令参数
- data(16位)
管理数据,作为磁盘的缓冲区,负责读取和写入数据
- sector count(8位)
待读取和待写入的扇区,每读取一个扇区,该寄存器的值便会-1
- LBA
用于描述一个扇区的地址,LBA分为两类
LBA28:28比特描述一个扇区,2^28*512字节 = 128G
LBA48:128PB = 131072TB
low,mid,high寄存器:一共24位,剩下四位在device寄存器中
-
command
存储让硬盘执行的命令
identify:0xEC 硬盘识别
read sector:0x20,读硬盘
write secotr:0x30,写扇区
等等
- status
存储磁盘状态
操作扇区
1,选择通道,往通道的sector count中写入待操作的扇区数
2,往该通道上的三个 LBA 寄存器写入扇区起始地址的低 24 位。
3,往 device 寄存器中写入 LBA 地址的 24 27 位,并置第 位为 ,使其为 LBA 模式,设置第 4位,选择操作的硬盘( master 硬盘或 slave 硬盘)
4,往该通道上的 command 寄存器写入操作命令。
5,读取该通道上的 status 寄存器,判断硬盘工作是否完成。
6,如果以上步骤是读硬盘,进入下 个步骤。否则,完工。
7,将硬盘数据读出。
磁盘数据读出,emmm该怎么读出呢,别怕这里有几种方式:
1,无条件传送方式
数据源提前准备好数据,CPU不打招呼直接取数据
2,查询传送方式
类似于非阻塞IO,CPU不断去查看磁盘状态(status寄存器)如果好了就读出数据
3,中断传输方式
这个就类似于IO多路复用了,我CPU不去看你,磁盘数据准备好后和我CPU说一声(发个中断),我收到中断后来读取数据
4,直接存储传输DMA
类似于异步IO,上面中断的方式是要调用CPU资源的,因为需要保存上下文状态,而这次我CPU都无需亲自出马,你数据源直接和内存通信就好了,我到时候直接去内存拿数据即可,但是DMA选用有DMA控制器才行
5,IO处理机
CPU就像人一样,不断往最懒的方向发展。DMA方法实际上还不够简便,需要数据交换,组合,校验。IO处理机专门负责这件事情,
CPU真实幸福呢
四,开始写代码
沉舟侧畔千帆过,病树前头万木春。当年BIOS将交接棒交给了MBR,而此时的MBR也已是风前残烛 ,他也要将自己的交接棒交给下一位了(512字节追究还是无法写完一个内核的)。而这个人就是loader,接下来我们要读取磁盘把loader加载到内存中,并执行其中的代码!
流程:
1,Loader放入磁盘的2号扇区(LBA是从0开始的,0号是MBR,你也可以放1号)
2,MBR编写磁盘读取操作,将Loader读出
3,将loader读出放入可用的内存区域(自己看内存分布挑一个风水宝地,但尽量离其他区域远一点)
流程知道了,那就让我们开始做吧,着手の準備をする!!
先编写一个boot.inc吧
LOADER_BASE_ADDR equ 0x900 ;loader的内存地址 0x900
LOADER_START_SECTOR equ 0x2 ;lader的磁盘扇区 2
mbr.S(已修改)
%include "boot.inc"
SECTION MBR vstart=0x7c00
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00
mov ax,0xb800
mov gs,ax
mov ax,0x600
mov bx,0x700
mov cx,0
mov dx,0x184f
int 0x10
mov eax,0x2
mov bx, 0x900
mov cx,4
call rd_disk_m_16
jmp LOADER_BASE_ADDR ;跨过数据段
rd_disk_m_16:
mov esi,eax
mov di,cx
mov dx,0x1f2
mov al,cl
out dx,al
in al,dx
mov eax,esi
mov dx,0x1f3
out dx,al
mov cl,8
shr eax,cl
mov dx,0x1f4
out dx,al
shr eax,cl
mov dx,0x1f5
out dx,al
shr eax,cl
and al,0x0f
or al,0xe0
mov dx,0x1f6
out dx,al
mov dx,0x1f7
mov al,0x20
out dx,al
not_ready:
nop
in al,dx
and al,0x88
cmp al,0x08
jnz not_ready
mov ax,di
mov dx,256
mul dx
mov cx,ax
mov dx,0x1f0
go_read:
in ax,dx
mov [bx],ax
add bx,2
loop go_read
ret
times 510-($-$$) db 0
db 0x55,0xaa
loader.S暂且写简单点。就用我们之前的屏幕输出吧
%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
mov byte [gs:0x00],'G'
mov byte [gs:0x01],0xA4
mov byte [gs:0x02],'e'
mov byte [gs:0x03],0xA4
mov byte [gs:0x04],'n'
mov byte [gs:0x05],0xA4
mov byte [gs:0x06],'i'
mov byte [gs:0x07],0xA4
mov byte [gs:0x08],'u'
mov byte [gs:0x09],0xA4
mov byte [gs:0x0a],'s'
mov byte [gs:0x0b],0xA4
jmp $
以上代码完成后呢,我们需要建立一个include文件夹,因为我们包含了一个boot.inc文件
sudo nasm -I include/ -o geniusos.bin mbr.S ;要包含include文件
sudo dd if=./geniusos.bin of=/usr/geniux/img/geniusos.img bs=512 count=1 conv=notrunc
接下来编译 loader
sudo nasm -I include/ -o loader.bin loader.S
sudo dd if=./loader.bin of=/usr/geniux/img/geniusos.img bs=512 count=1 seek=2 conv=notrunc
;这里要放在第二个扇区,所以不要搞错了
由于操作太多我写了个bash
#!/bin/bash
rm -rf ./img/geniusos.img
nasm -I include/ -o mbr.bin mbr.s
dd if=mbr.bin of=geniusos.img bs=512 count=1 conv=notrunc
echo "disk write success!!"
nasm -I include/ -o loader.bin loader.s
dd if=loader.bin of=geniusos.img bs=512 count=1 seek=2 conv=notrunc
11.30补档说明
此处可能存在loader无法加载的情况,请大家跳转到5 Day,里面有该bug的相关解决方法,翻到最下面bug解决就是的了