读硬盘

  从零学编程

Help > 第二章:汇编语言 >

029.读写硬盘

如果把硬盘也抽象成类似内存一样的高楼,撇开扇区的“概念”。
 

内存里面的一层楼住着:8个bit位=1个字节。
硬盘里面的一层楼住着:4096bit位=512个字节。

之前因为我们是没有操作系统的虚拟计算机,为了让CPU执行我们的代码。
迫不得已把我们的代码放到了硬盘的引导区,让BIOS自检完成之后。
自动把引导区的代码加载到0x07c00的位置运行。

但如果我们写的程序,万一大于512个字节怎么办?比如3000个字节?甚至很多。
当然前提是内存里面也得能放下。

这个时候,引导区肯定就放不下了,如果用之前的方法,我们的程序就没办法运行了。

为了解决这个问题。
我们需要思考一个问题:
大家想象:BIOS程序是怎么样把我们512个字节的引导区代码加载到内存0x07c00处,开始执行的?虽然不知道原理。
但根据这个原理,我们可以想象一副这样的图。




当BIOS,把硬盘的第一扇区,也就是最开始512个字节加载到0x07C00的位置。
引导区的程序指令就被运行起来了,这个过程跟奥运火炬传递一样。

而加载器要把“真正的程序”加载到内存得肯定得知道几件事情。

1.首先得知道“真正的程序”在硬盘上的起始扇区?
2.这个程序占用了硬盘多少个扇区。
3.内存中的什么地方是空闲的?
4.怎么样把它们从硬盘上搬到内存里面。
5.搬进来之后,让CPU如何跳到那块程序代码区执行指令。

现在我们讲的内容对应着《x86》这本书的第八章,大家可以私下来结合着书,网上可以下载到电子书看。

这节课我们需要进一步抽象了。

至于为什么这么设计,或者为何如此定义理解,这是我们的计算机先驱者抽象的概念。
当然这并不是最终心态,Windows上操作系统的可执行程序(exe)文件格式。
里面的抽象概念和细节要比这个复杂的多,也是我们未来要研究的课程目标之一。

站到CPU,内存,硬盘的角度去看待什么标号,变量,函数,指针,屏幕显示字符,段寄存器,通用寄存器,它完全不理解。
它只是傻乎乎的一堆开关不停的变化切换电流运行的线路,听你的指挥,通过什么寄存器组合定位到哪个内存地址,把什么数据从硬盘,内存,寄存器,运算器里面搬来搬去的。

操作系统本身就是一个很大的程序,而且这个程序抽象出来很多新的概念。
什么文件夹,文件目录,进程,线程,内核,注册表,启动项,时钟,窗体,对话框,定时器,多任务多线程……



之所以这些课程有人认为比较难以理解,就是抽象概念太多了。本质就是一堆1和0.
我们以前在《穿越计算机的迷雾》里面学习的非门,与门,或门,最后抽象了很多概念寄存器,计数器,控制器,运算器,存储器等。

又利用抽象出来的概念再次组合不断的发明新的名词,新的知识点。
其实任何行业,任何知识都是如此。

按照现代的科学技术,无论是计算机还是人类,所有的活动,生命,死亡,诞生,疾病,一切的一切,都是电子,质子,中子的变化。

那么人的意识,思维,想法,又是什么呢?

回到主题,所以说,学习计算机,跟学习哲学很像!!

聪明的朋友可能会想到了,以后我们抽象的东西会越来越多。
越来越接近人脑中现实世界已经抽象好的概念,即使一些完全不懂计算机的爷爷奶奶也会把“电子计算机”理解成
“电脑”,这何尝不是一种抽象。

操作系统把一个硬盘分成几大区,跟家里面不同的柜子一样。
C盘安装系统。 D盘安装常用软件 
E盘存储一些照片,视频,音乐等。

每个盘就像一个独立的王国,每个王国下面又可以新建文件夹,好像不同的城市。
每个城市还有村落。

当有不同的程序同时运行的时候,抽象出进程的概念。

可当我们最初使用电脑的时候,如果没有这些知识。
是无法感受到这个过程其实就是不断的对硬盘和内存运算器寄存器不停的写数据,读数据继而演化出如此众多的概念名词。

这对于我个人也是一个非常大的挑战。
坦白来说,这比讲其他编程语言程序设计要难的多。

因为有很多硬件层面的知识点,我们只能靠自己的想象力。



先来看一张图

在这里,我们就得再引出一个概念了:端口。(port)
你可以简单把它理解成一个“住址”。

为了后面的解释更加简单。
我们把处理器比作“皇帝”。
输入输出控制设备集中器芯片比作“服侍皇帝的身边太监”

“住址”(端口)都是统一标号的:
0x01. 
代表太后寝宫
0x0002.  代表王爷府
0x0003. 
代表御膳房
…………………………………………
……

0x01 "向太后问安"
0x0002 "召见"
0x0003
"用膳"

………………………………………………

"皇帝"先告诉"太监"一个"住址",以及要办的事情,比如召见某位“辅佐大臣“
剩下的事情就是“太监”去处理了。

当然上述比喻逻辑上并没有那么完美,也不一定完全适用,参考理解而已。



内存的读写单位是字节。
硬盘的读写单位是扇区。

要读就至少得读一个扇区,要写也至少得写一个扇区。
不能说,我只读一个扇区的几个字节。

所以,内存和硬盘之间的数据交互是以扇区单位交互的。

我在备课到这个地方的时候,心里嘀咕一个问题,固态硬盘是没有“扇区”的概念。
很多历史性技术问题,可能已经处于淘汰甚至早就应该被淘汰了。
但迫不得已为了兼容历史的包袱,依然在沿用,归根到底还是“成本”。

比如现在发明了一种新的燃油,“氦油”,可以取代汽油,同样的做功,成本只需要汽油的70%
但由于目前所有已生产的汽车发动机技术,都不能使用这种“氦油”。
厂家也不会为了这个问题,单独重新设计所有型号的汽车发动机,再更换。
毕竟有些型号早就停售了。

所以想使用新的“氦油”,就得用支持“氦油”的发动机技术,假如现在全球有5亿辆汽车,全部都得更换的话。
从汽车流水线,到工人培训,以及慢慢淘汰回收市场的旧汽车到真正彻底停止销售汽油,是需要很长时间的,很大的成本。

计算机硬件技术发展太快。
在历史上,往往一个优秀的产品设计出来没过多久,结果技术进步了,有了更好的设计……
所以导致了很多历史的“包袱”,其实很多行业都存在这种情况。

不光是硬件,包括一些软件技术也是,已经被淘汰了,出来更好的设计。
比如微软关于VC++里面的MFC框架,微软自己也放弃了。
但有很多以前的软件技术使用的MFC框架开发的,直到今天还在维护升级。

再比如有的一些软件,只能在Windows
xp上运行。 
重新开发的话成本太高,所以依然有的地方迫不得已使用Windows xp系统,甚至更早的Windows 98.



给大家说这些事情,就是想让大家不用太过于纠结,较真。
世界上唯一不变的就是一直在改变。

有个概念的笼统认识,最后我们的汇编代码能正常运行就OK。

学习计算机体系结构的目的,不是让你去设计自己的
CPU(新的 ISA 或微架构),打败 Intel 和 ARM;也不是参与到 CPU
设计团队,改进现有的微架构;而是明白现代的处理器的能力与特性(例如流水线、多发射、分支预测、乱序执行等等指令级并行手段,内存局部性与
cache,多处理器的内存模型、能见度、重排序等等),在编程的时候通过适当组织代码和数据来发挥 CPU 的效能。

学习操作系统的目的,不是让你去发明自己操作系统内核,打败
Linux;也不是成为内核开发人员;而是理解操作系统为用户态进程提供了怎样的运行环境,作为程序员应该如何才能充分利用好这个环境,哪些做法是有益的,哪些是做无用功,哪些则是帮倒忙。


学习X86这门课程,也不是让大家以后真的用汇编语言,在没有操作系统的电脑上,去写代码,为了打印一行文字,都费老大的劲。


没有大家没有一个正确的学习心态和把握,很容易陷入到无穷无尽没意义的研究中,继而浪费掉大量的时间,做了太多的无用功。
大家一定要明白一件事情,我们最终的目的是:为了能够做网站,做电脑软件,做手机软件这叫做计算机应用,解决自己遇到的技术问题,这是可以兑现成金钱,价值,被人尊重的东西。

什么知识点,应该浅尝而止。
什么知识点,应该重点了解。

把时间和精力放到有意义的地方。



要从硬盘上读写数据。
需要向硬盘控制器发送磁头号,柱面号和扇区号,用这样的方式跟硬盘沟通太费劲了,这种最原始,最费劲的方法叫做CHS模式。
正如最早的时候用机器语言跟计算机说话,用像素点的方式在屏幕上拼凑字符。

后来有了汇编语言和汇编器。
有了Ascll码,直接写到显卡的某个指定位置,如何变成像素点显示到屏幕上,那是显卡的工作,与我们没关系。



CHS模式太麻烦。
又发明了一种模式,跟读写内存比较像。
这种方法叫做LBA28。
使用28个比特位来表示逻辑区号。
也就是0x0000000到0xFFFFFFF.
最多可以表示:268435456个扇区,每个扇区512字节。
所以LBA28可以管理128GB的硬盘。

后来硬盘越来越大,28个比特位定位扇区显然不能满足了。
一堆指定规则的人,又商量,干脆用48个比特位来定位扇区吧。
所以,又有了LBA48,采用48个比特位定位扇区,可以管理131072TB的硬盘容量了。


我们今天的电脑,硬盘控制器和“太监”之间一共有8个端口。
这8个门端口的名字叫做,这都是人定义的,没有为什么!
0x1F0
0x1F1
0x1F2
0x1F3
0x1F4
0x1F5
0x1F6
0x1F7

假设我们要从硬盘的扇区1开始读,并且读1个扇区长度。
要读的位置用LBA28的方式来表示。
0000 0000 0000 0000 0000
0000 0001
0x1f3端口存放的是0~7位.
0x1f4端口存放的是8~15位
0x1f5端口存放的是16~23位
最后4位在0x16f端口里面

要读的长度放到1F2里面。



mov dx,0x1F2
mov al,0x01
out dx,al

mov dx,0x1F3
mov
al,0x01
out dx,al

mov dx,0x1F4
mov al,0x00
out
dx,al

mov dx,0x1F5
mov al,0x00
out dx,al

mov dx,0x1F6
mov al,0xE0
out dx,al

mov
dx,0x1F7
mov al,0x20          ;读命令
out dx,al

0x1F7是命令端口,又是状态端口。
发送0x20,硬盘里面的“管理员”就开始做一些准备工作了,并且把会0x1F7里面的第7个位置变成1代表自己很忙。
0010
0000  =我要拿东西
1010 0000  =我开始准备了
0010 1000 
=我准备好了

等“硬盘管理员”把一些准备工作做好之后,再把0x1F7里面的第7个位置变成0,代表忙完了。
并且会把0x1F7端口的第3个位置变成“1”,代表,我已经把准备工作做好了。

wait:
in al,dx   ;把1F7端口里面的数据读入到al里面
and
al,0x88  ;   请求读 al=0010 0000   准备中 al=1010 0000  准备完毕 al=0010 1000
cmp
al,0x08
jnz wait   不为0的时候跳转。

and
al,0x88
0x88的二进制是1000
1000。
用这条指令保留住寄存器AL中的第7位和第3位,其他无关的位都清零
如果寄存器AL中的二进制数是00001000(0x08),那就说明可以退出等待状态,继续往下操作,否则继续等待。
 


写入第1扇区
db ‘I‘,0x7,‘
‘,0x7,‘M‘,0x7,‘i‘,0x7,‘s‘,0x7,‘s‘,0x7,‘ ‘,0x7,‘Y‘,0x7,‘o‘,0x7,‘u‘,0x7
times
512-($-$$) db
0

写入第0扇区(引导区)

一切都准备工作都就绪了。
0x1f0是硬盘接口的数据端口,并且是16位的。
一旦硬盘控制器比较闲,准备工作也做好了。

就可以连续从这个端口写入或者读取数据。
假如现在从硬盘第一个扇区,读取256个字节。
读取的数据存放到段寄存器DS:BX指定的位置。

mov
cx,256
mov dx,0x1f0

readw:
in ax,bx
mov [bx],ax
add
bx,2
loop readw

0x1F1是端口的错误寄存器,里面保留着“硬盘驱动器”最后一次执行工作的时候状态。(错误原因。)

极客编程  http://www.jikebiancheng.com/     
QQ群:77517491

时间: 2024-10-27 18:43:32

读硬盘的相关文章

物理读,逻辑读,预读

在使用SET STATISTICS IO ON语句统计I/O时候,我们会看到类似下面的结果: 扫描计数 1,逻辑读取 2 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次. 那么它们代表什么呢? 预读:用于估计信息,去硬盘读取数据到缓存. 物理读:查询计划生成好以后,如果缓存缺少所需要的数据,让缓存再次去读硬盘.如果内存里没有缓存数据或执行计划(sql语句改变执行计划不能重用,需要重新计算执行计划),那么SQLSERVER就要去硬盘读取

node.js学习笔记之读文件

直接读硬盘上aa.txt内容var  http  =  require('http');var  optfile  =  require('./models/optfile');http.createServer(function  (request,  response)  {    response.writeHead(200,  {'Content-Type':  'textml;  charset=utf-8'});    if(request.url!=="/favicon.ico&q

node.js学习笔记之_读文件

直接读硬盘上aa.txt内容var  http  =  require('http');var  optfile  =  require('./models/optfile');http.createServer(function  (request,  response)  {    response.writeHead(200,  {'Content-Type':  'textml;  charset=utf-8'});    if(request.url!=="/favicon.ico&q

初谈SQL Server逻辑读、物理读、预读

前言 本文涉及的内容均不是原创,是记录自己在学习IO.执行计划的过程中学习其他大牛的博客和心得并记录下来,之所以想写下来是为了记录自己在追溯的过程遇到的几个问题,并把这些问题弄清楚. 本章最后已贴出原文地址. 1.SQL Server的数据存储方式 要理解逻辑读.物理读.预读这三个概念,先要搞懂SQL Server的数据存储方式. SQL Server数据库包括数据文件和日志文件,一个数据库可以有一个或多少数据文件.日志文件.所有的数据存储在数据文件中,数据文件可以划分为再小的单元,我们称为“页

SQL Server逻辑读、预读和物理读

SQL Server数据存储的形式 预读:用估计信息,去硬盘读取数据到缓存.预读100次,也就是估计将要从硬盘中读取了100页数据到缓存. 物理读:查询计划生成好以后,如果缓存缺少所需要的数据,让缓存再次去读硬盘.物理读10页,从硬盘中读取10页数据到缓存. 逻辑读:从缓存中取出所有数据.逻辑读100次,也就是从缓存里取到100页数据. SQL Server存储的最小单位是页,每一页大小为8K,SQL Server对于页的读取是原子性的,要么读完一页,要么完全不读.即使是仅仅要获得一条数据,也要

硬盘惊魂记

不一小心把笔记本摔地上了,因为是地毯,一开始没在意.后来开机发现反应很慢,强制重启发现硬盘已经不能启动了! 第一反应就是硬盘坏掉了,但不知道坏的程度是多少.于是接到移动硬盘盒上,居然还能读得出来!我略一想就开始拷贝照片.也不知道过了多久我回来看的时候拷贝进度停在79%不动了,我的心又一登,估计是碰到坏扇区了.停止不了移动硬盘,只能强行拨出来.结果再接上移动硬盘时已经读不出来分区上面的数据了.读硬盘时变成有规律的“咯咯咯,咯”声音,好像是在不停地重试坏扇区. 我开始后悔和紧张了,因为还有别的更重要

SQL Server逻辑读-预读-物理读

SQL Server逻辑读-预读-物理读    SQL Server 存储数据的方式        1.页是最小的操作单元,也就是说从磁盘读取数据库的时候最少读取一页,每一页的大小是8KB,SQL SERVER对于页的读取是原子性,要么读完一页,要么完全不读,不会有中间状态 2.区是8个连续的页组成的,区是最小的分配单元,当需要空间时最少分配一个区的空间. 看图说话,两个表的结构完全一样,一个插入四条数据,另一个插入100条数据,结果大小都为0.008: SQL SERVER一页的总大小为:8K

Dingus 硬盘驱动(简)

网上查找Linux硬盘驱动的话能找到好多相关信息,但是具体代码都没有.经过一番努力,实现了硬盘读写的代码,特来分享一下.可能因为硬盘控制器更新的原因,一些新式的笔记本上使用这些代码会失败.不过在VmWare上是可以用的.这个项目的git地址:git://code.csdn.net/hanjianqiao/dingus.git.在commit为"Add harddisk"的版本直接编译出的镜像便是下面这个. 上图中密密麻麻的16进制数据是从硬盘第一个扇区取出来的,这些数据是在之前就已经写

cpu,内存,虚拟内存,硬盘,缓存之间是什么关系??

1.CPU即中央处理器,是英语“Central Processing Unit”的缩写.CPU从内存或缓存中取出指令,放入指令寄存器,并对指令译码分解成一系列的微操作,然后发出各种控制命令,执行微操作系列,从而完成一条指令的执行. 2.但是,CPU并不能直接调用存储在硬盘上的系统.程序和数据,必须首先将硬盘的有关内容存储在内存中,这样才能被CPU读取运行.因而,内存(即物理内存,是相对于硬盘这个“外存”而言)作为硬盘和CPU的“中转站”,对电脑运行速度有较大影响. 3.当运行数据超出物理内存容纳