对于硬盘驱动的理解

目录

如何读写硬盘

  读写操作

  硬盘控制器端口及作用

  硬盘中断

  硬盘分区信息的获取

  如何读写文件

  TASK_HD

如何读写硬盘

读写操作

  第一次看到linux 0.12关于读写硬盘几行代码时候,感觉很费解。

do_hd = intr_addr;
outb_p(hd_info[drive].ctl,HD_CMD);
port=HD_DATA;
outb_p(nsect,++port);
outb_p(sect,++port);
outb_p(cyl,++port);
outb_p(cyl>>8,++port);
outb_p(0xA0|(drive<<4)|head,++port);
outb(cmd,++port)

  我还是不明白怎么这样就可以读写硬盘了。但是代码到此就结束了。

  一直好奇程序是如何控制硬件的,这些指令就是一个个电信号在cpu中流动,怎么就能把硬盘中的数据拿到内存中呢?

  正好在同一个学期开设了《计算机组成原理》和《微机原理与接口技术》这两门课程,那个时候才了解到端口的意思,了解到cpu寻址、数据传输的流程。

  往端口写了数据和指令,剩下的我们只能相信硬件制造商的设计和生产能力了,然后默默等待硬件的回应。我记得当时自己疑惑了一段时间,苦于没有人来提醒这一点,可能会的人感觉这根本不是问题吧。

硬盘控制器端口及作用

  Linux 0.12当时操作的硬盘是CHS寻址模式,起始扇区编号是1。对于《实现》来说,用bochs自带的工具bximage命令生成的虚拟硬盘是LBA寻址模式的,起始扇区编号是0。CHS模式和LBA模式的端口号和操作方式都一样,只是有一些端口代表的意义不一样了,来看一下LBA寻址模式的端口作用,借用书中的表9.1。

表1           LBA寻址模式的硬盘端口及其作用

I/O端口 读时 写时  
primary secondary
1F0H 170H  Data 
1F1H  171H   Error  Features
1F2H  172H   Sector count 
1F3H 173H   LBA low 
1F4H  174H   LBA mid 
1F5H  175H   LBA high 
1F6H  176H   Device 
1F7H  177H   Status  Command
3F6H 376H   Alternate status  Device control

  其中Device寄存器比较特殊,它用来指明寻址模式。来看一下格式。

表2           Device寄存器各个bit为的意义

Bit位 意义
7 1  
6 L 0表示CHS模式,1表示LBA模式
5 1  
4 DRV 0表示主盘,1表示从盘
3 HS3
如果是L=0,CHS模式,那么这四位的值表示磁头号

如果L=1,LBA模式,那么这四位的值表示LBA的24到27位

2 HS2
1 HS1
0 HS0

  从上面的代码可以很清楚的看到如何读写硬盘,往相应的端口写上我们要读多少个扇区,读哪个扇区,哪个柱面,哪个磁头,哪个硬盘,然后告诉硬盘我们的需求cmd,读或者写。

  另外,CHS模式下,硬盘扇区编号从1开始编号。LBA模式下,从0开始编号。

硬盘中断

  我们怎么知道硬盘的工作做完了没有呢?只能等待硬盘产生中断信号,通过8259A告诉cpu,这个中断信号是哪个硬件产生的。

  在书中,用的是微内核,所有的进程都给TASK_HD(硬盘驱动)发送读写硬盘的命令,而不是自己调用硬盘驱动中的读写函数。所以中断产生后,仅仅需要通知TASK_HD这个进程,TASK_HD会把硬盘准备好的数据读到发出读请求进程指定的内存位置。

硬盘分区信息的获取

  前面说了如何向硬盘发送命令,让它读写哪些扇区,但是这些参数都是我们提前计算好的。如何计算这些参数?我们又是如何知道该读写那个扇区呢?

  之所以把分区信息的介绍放到读写文件这一小节中,是因为我觉得分区信息和文件关联很大。我们要读写文件,才需要知道分区信息,如果我们不需要按照文件形式来读写硬盘,那么知不知道分区信息就无所谓啦,凭我们的大脑记住要读取的数据在第几个分区,到时候直接汇编操作寄存器就好啦。

  那为什么要分区呢?似乎不分区把所有的数据都杂糅在一起,电脑也可以正常运行啊。我百度了一下,大概是由于为了把操作系统和数据分开吧。试想,如果所有的东西和操作系统共处一个空间,那么操作系统崩溃了,这个空间的所有数据的记录索引在重新安装操作系统后都会失效,尽管数据本身依然很正常,但是由于记录索引丢失,我们却没法找到他们。如果分区了,那么最多操作系统的所在分区的数据拿不到了,其他分区数据的记录索引还在。

  如何获取分区信息?

  在硬盘的0号扇区(MBR扇区)偏移0x1BE处保存的有一张硬盘主分区表。只有四个表项,也就是说一个硬盘只能记录四个主分区,据说是因为当初IBM认为一个PC上装4个操作系统(只有主分区上能安装操作系统)就够用了。如果想要更多的分区,那么需要在格式化的时候指明一个(只能有一个主分区记录能用于扩展分区)表项用作扩展分区,扩展分区并不能直接使用,在这个扩展分区里面我们还要划分出逻辑分区,每一个逻辑分区的起始扇区记录的分区表只能使用两个表项。

  对于操作系统而言,每个分区都被当做一个独立的设备对待。

  那么书中如何记录分区信息呢?看一下保存数据的结构体:

struct part_info {
    u32    base;    /* # of start sector (NOT byte offset, but SECTOR) */
    u32    size;    /* how many sectors in this partition */
};

/* main drive struct, one entry per drive */
struct hd_info
{
    int            open_cnt;
    struct part_info    primary[NR_PRIM_PER_DRIVE];//计算后NR_PRIM_PER_DRIVE = 5
    struct part_info    logical[NR_SUB_PER_DRIVE];// 计算后NR_SUB_PER_DRIVE = 64
};

  书中根设备编号是0x322,可以知道子设备号是0x22,一开始很困惑,这么大的子设备号,难道要分0x22个分区?或者说系统怎么就知道0x22表示的是根分区呢?

  还得再看一段代码

logidx = (p->DEVICE - MINOR_hd1a) % NR_SUB_PER_DRIVE;
sect_nr += p->DEVICE < MAX_PRIM ?
        hd_info[drive].primary[p->DEVICE].base :
        hd_info[drive].logical[logidx].base; 

  先将设备号减去第一个逻辑设备的编号得到设备号在logical数组的下标。当然,可能这个设备号不是逻辑设备,而是主分区。没关系,下一步判断p->DEVICE 是不是小于MAX_PRIM,如果小于,说明是主分区,直接用p->DEVICE在primary数组中取值就可以了。

  原来是这样,你想怎么样编号就怎么样编号,只要你自己能找到映射关系就可以了。

  获取信息的步骤:

  1. device = 0,style = P_PRIMARY
  2. 调用获取分区信息函数
  3. 如果style == P_ EXTENDED执行第10步
  4. 读取设备device的起始扇区,提取0x1BE处的4个表项到part_tbl
  5. 令i=0
  6. 判断第i个分区表项part_tbl[i]
  7. 如果是主分区,记录起始扇区sect_start和扇区数目setcs到相应的primary[i+1]。
  8. 如果是扩展分区,记录起始扇区sect_start和扇区数目setcs到相应的primary[i+1],令device += i+1,style = P_ EXTENDED跳到第2步
  9. 如果i>=4,结束;否则i++,执行第6步
  10. 扩展分区的起始扇区ext_start_sect = primary[device].base(这个值在第8步中已经计算出来了),求出该扩展分区的第一个逻辑分区的编号,nr_1st_sub = (device-1) * NR_SUB_PER_PART,计算该扩展分区第0个逻辑分区的起始地址s= ext_start_sect
  11. 令i=0(由于是递归调用,此处i的值并不影响第5步的i)
  12. 读取第nr_1st_sub+i个逻辑分区的起始扇区s,提取0x1BE处的2个表项(逻辑分区只使用分区表的两个表项)到part_tbl
  13. 记录逻辑分区的信息到logical[nr_1st_sub+i]
  14. s = ext_start_sect + part_tbl[1].start_sect
  15. 如果i>=16,本次递归结束,返回到第8步;否则i++,执行第12步

  感觉文字叙述理解起来可能比较模糊,但是比代码实现起来还是省事一些,像读分区起始扇区,一句话带过,知道怎么做就可以了,如果用代码描述,可能还要牵扯到其他知识点。

如何读写文件

  其实对于硬盘驱动而言,没有文件这个概念,只有扇区。硬盘驱动能接受的参数就是要读写的起始扇区,读写扇区个数。文件这个概念由上层的文件系统来处理。

  这个时候,我们会想起来inode结构体中有两个记录是i_dev和i_start_sect,这两个元素把上层文件系统和硬盘关联起来了。当我们要读某某个文件的时候,文件系统告诉硬盘驱动读目录区,把文件的inode号找到,再读indoe到内存中,这个时候就有了文件在哪个分区i_dev,数据存放在第i_start_sect号扇区,及之后一共的x800个扇区中,这个i_start_sect的值是相对于分区i_dev为起始偏移的。

  知道i_dev和i_start_sect之后,硬盘驱动可以做什么呢?首先将以i_dev分区为起始偏移的i_start_sect转化为相对于整个硬盘。怎么转化呢?上面获取分区信息的时候,每个分区的起始扇区都被记录,我们找到i_dev的起始扇区,加上i_start_sect就是相对于整个硬盘的了。

  这样就把文件读进来了。至于读文件哪一段的内容,其实还是上层的文件系统来记录处理的,还记得file结构体中有一个元素是pos,这个值就是用来标明要读写的内容在文件中的偏移。将pos/SECT_SIZE再加上上面计算的文件相对于整个硬盘的偏移,就是要读写的某一段数据了。

   所谓块设备的名称也许就是这样由来的吧,一次最少处理的数据是一个扇区(当然,不同的硬盘给出的接口肯定不一样)。

TASK_HD

  这样一来,TASK_HD的任务就是很简单了啊,接收TASK_FS发送的读写请求,将针对于i_dev设备的i_start_sect转化为相对于整个硬盘的扇区号,再加上pos/SECT_SIZE,然后读写这个扇区交给TASK_FS就什么都不管了。进入下一个循环。

时间: 2024-12-09 01:25:50

对于硬盘驱动的理解的相关文章

linux硬盘驱动更新

作者:邹祁峰 邮箱:[email protected] 博客:http://blog.csdn.net/qifengzou 日期:2015.01.16 00:33 转载请注明来自"祁峰"的CSDN博客 在内核未升级之前,系统能够正常启动和使用,而当升级系统内核后,系统却无法正常启动时,这可能是因为硬盘驱动不兼容造成的.现可通过进入旧系统按照以下步骤逐步排查相关原因: ①.查询硬盘控制芯片厂商和型号 #lspci -nn | grep "SAS" #lspci -nn

在vmware esxi6.5中将硬盘驱动类型由HDD变为SSD类型

环境:在vmware web client6.5中的主机,主机IP为10.1.1.47 目的:为配置VSAN,需将所创建的三块硬盘类型的一块变成SSD类型,作为缓冲. 步骤1:将主机的SSH打开,以使xshell进行连接,配置->安全配置文件->编辑 将ESXI Shell 和SSH手动打开 将锁定模式禁用,配置->安全配置文件->锁定模式 使用xshell进行连接 输入密码即可登录 步骤2:更改硬盘驱动类型 esxcli storage core device list 找到目标

Dingus 硬盘驱动(简)

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

从Linux内核LED驱动来理解字符设备驱动开发流程

目录 博客说明 开发环境 1. Linux字符设备驱动的组成 1.1 字符设备驱动模块加载与卸载函数 1.2 字符设备驱动的file_operations 结构体中的成员函数 2. 字符设备驱动--设备号注册卸载 2.1 设备号注册 2.2 设备号注销 3. 字符设备驱动--文件操作 参考资料 示例代码 @(从Linux内核LED驱动来理解字符设备驱动开发流程) 博客说明 撰写日期 2018.12.08 完稿日期 2019.10.06 最近维护 暂无 本文作者 multimicro 联系方式 [

【智能家居篇】wifi驱动的理解(1)——驱动架构

转载请注明出处:http://blog.csdn.net/Righthek 谢谢! 在分析WIFI驱动前,分享一下个人对Linux驱动的一些了解,其实纵观Linux众多的设备驱动,几乎都是以总线为载体,所有的数据传输都是基于总线形式的,即使设备没有所谓的总线接口,但是Linux还是会给它添加一条虚拟总线,如platform总线等:介于WIFI的驱动实在是太庞大了,同时又是基于比较复杂的USB总线,所以建议读者在看此文章之前,先了解一下USB设备驱动和网络设备驱动. 我们要看懂WIFI驱动,首先要

【智能家居篇】wifi驱动的理解(2)

转载请注明出处:http://blog.csdn.net/Righthek 谢谢! 上一篇文章我们已经通过三条线索简单地描述了wifi驱动的框架,现在我们开始深入到每条线索中.首先我们从USB设备这条线索开始.在分析之前,我们需要理解在整个wifi模块中,USB充当什么角色?它的作用是什么?实质上wifi模块上的数据传输有两端,一端是wifi芯片与wifi芯片之间,通过无线射频(RF)进行数据传输:另一端则是wifi芯片与CPU之间,通过USB进行数据传输. 了解Linux的USB驱动的读者都知

SylixOS nand驱动框架理解

1.适用范围 该应用笔记适用于帮助用户在SylixOS下使用nandflash器件. 2.原理概述 2.1nandflash存储结构 nandflash存储结构如图2-1所示,下面以mini2440开发板上的k9f1g08系列nandflash为例,简单介绍nandflash的存储结构. 图2-1 nandflash存储结构 nandflash的存储结构主要有两个单位:页(page),块(block).每页大小为2K+64字节,2K用来存储数据,64字节用来存储控制信息(OOB区,例如坏块信息.

【智能家居篇】wifi驱动的理解(3)——usb接口在wifi模块中的角色

转载请注明出处:http://blog.csdn.net/Righthek 谢谢! 上一篇文章已经提到USB接口在wifi模块中的最重要两个函数是usb_read_port()和usb_write_port().那它们是怎么和wifi扯上关系的呢?我们可以从以下三个方面去分析: 1.首先需要明确wifi模块是USB设备,主控(CPU)端是USB主机: 2.USB主机若需要对wifi模块进行数据的读写时,就必须经过USB接口: 3.既然涉及到数据的读写操作,必然要用相应的读写函数,那么usb_re

CCD 驱动 时序理解

由于项目的需要,接触到perkinelmer 公司生产的CCD sensor:年末了,好好回顾下:目前生产CCD传感器的公司有dalsa,e2v,toshiba,sony等,perkinelmer公司目前主要从事医疗设备以及生物工程等,其传感器生产与销售已经分离出去,由埃赛力经营,好了,废话不多说,我们来探讨下该CCD驱动时序吧: CCD驱动信号不多包含以下: ØPG:photo gate; ØTG: transfer gate; ØAB: antibooming gate; VOG: outp