分享
 
 
 

大容量硬盘的读写操作

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

摘要:本文主要是讨论系统级C语言程序设计的又一话题:大容量硬盘的读写操作。文章首先介绍了硬盘的物理结构,然后简要地说明了存在容量限制的原因,最后给出了解决问题的方法,并用C语言实现对大容量硬盘的读写和测试操作。文章会涉及部分有关计算机数据存储和中断调用的内容,想要更深一步了解这些内容的读者可以参阅笔者所写的《系统级C语言程序设计(中断服务程序的编写)》或相关资料。

关键词:柱面、磁头、扇区、硬盘寻址模式、CHS、LBA、ATA界面、INT 13界面、扩展INT 13中断、硬盘容量、标准格式化

在对硬盘分区表进行跟踪时,可以发现一个问题:对于一个4.3GB的硬盘,可以正确地找到它的所有逻辑分区,而对于一个10.3GB的硬盘,我们只能够找到近8GB范围内的逻辑分区。同样,在采用传统方式编程来获取硬盘参数时,如果硬盘是4.3GB的,能够得到正确结果,但如果硬盘是10.3GB或更大的,程序就好象成了“近视眼”,只能“看到”8.4GB的容量。这是为什么呢?

为了解决这个问题,首先应该了解硬盘的物理结构。我们可以把硬盘看成一个圆柱体,然后将圆柱体沿垂直于中心轴的方向切成多块薄圆片,对于一个圆片,它有上下两面,每面上都有多个同心圆,每个同心圆又可以分成数量相等的扇形区域,数据就存放在这些扇形区域中。我们把一个圆片不同的上下两面叫做“磁头”(磁面),标号从0开始,把这些同心圆叫做“柱面”(磁道),标号从0开始,把扇形区域叫“扇区”,标号从1开始。对于标准格式化的磁盘,每个扇区一共是512个字节,所以硬盘的容量可以这样计算:

容量=柱面数 * 磁头数 *(扇区数/柱面)* 512(字节/扇区)

其实对于所有标准格式化的磁盘,上面的公式都成立。例如对于软盘,共有2个磁头(磁面),每磁头80条磁道,每磁道18个扇区,所以容量就是:

2 * 80 * 18 * 512 = 1.44MB

了解了硬盘的物理结构后,再来简要谈谈有关ATA界面和INT 13界面。ATA界面是寄存器驱动式并行总线,传输数据时,BIOS先向ATA中特定寄存器写入数据的开始地址和长度,再把相应的读写等命令写入特定寄存器,完成相应操作;INT 13界面其实也是靠寄存器来驱动的,它先将所有的参数包括数据地址等设置好(赋值给寄存器),再调用INT 13中断完成操作。

对于ATA界面,寄存器定义如下:

柱面低位寄存器 8bit

柱面高位寄存器 8bit

扇区寄存器 8bit

设备磁头寄存器 4bit

如果采用传统的CHS(Cylinder Head Sector)寻址,其最大寻址容量是:

2(8+8)*(28-1)* 24 * 512 = 65536*255*16*512=136.9GB

如果采用LBA(Logic Block Addressing)寻址,其最大寻址容量是:

2(8+8) * 28 * 24 * 512 = 65536*256*16*512=137.4GB

对于INT 13界面,寄存器定义如下:

柱面地位寄存器 8bit

柱面高位/扇区寄存器 2bit/6bit

磁头寄存器 8bit

如果采用传统的CHS(Cylinder Head Sector)寻址,最大寻址容量是:

210 * (26-1) * 28 * 512 = 8.456GB

如果采用LBA(Logic Block Addressing)寻址,最大寻址容量是:

210 * 26 * 28 * 512 = 8.590GB

注意:两种寻址方式的计算公式之所以有差别,是因为CHS寻址方式的扇区首地址是从1开始的,而LBA寻址方式的扇区首地址是从0开始的。

至此,为何存在8.4GB容量限制的原因已经清楚了。概括起来,原因有两个方面:一方面,主板的BIOS程序太旧,无法支持大容量的硬盘,报告给操作系统的参数也就不具备可靠性,解决这一问题的方法就是通过刷新BIOS的方法升级BIOS;另一方面是因为INT 13的局限。为了解决这个软问题,以超越容量限制,人们又定义了新的扩展INT 13,它不再使用操作系统的寄存器传递硬盘参数,而是使用存储在操作系统内存中保存着64位LBA地址的地址包。如果硬盘支持ATA,就把地址包低28位传给ATA界面,否则就先转换成CHS参数,再传给ATA界面。由此一来,采用扩展INT 13可以使最大寻址容量达到137GB左右。

现在开始研究如何在C语言中使用扩展INT 13来操作大容量硬盘。为了使说明更加简单,这里只例举读、写、获取硬盘参数三种操作的实现方法,其它操作如“测试硬盘扇区”等,请读者自己查阅有关资料。

对于扩展INT 13中断,参数如下:

中断号

功能

调用寄存器

返回寄存器

备注

INT 13

AH=41H

检测扩展中断功能是否安装

AH = 41h

BX = 55AAh

DL = 驱动器号

(80H到FFH)

失败:AH=1

CF置位

成功:AH=版本号

CF=0 BX=AA55H

INT 13

AH=42H

磁盘扩展读操作

AH = 42H

DL = 驱动器号

DS:SI=指向LBA地址包的指针

失败:AH=错误号

CF置位

成功:AH=0

CF=0

地址包定义:

偏移 大小 描述

00H 字节 地址包大小

01H 字节 保留(为0)

02H 字 传输包个数

04H 双字 指向数据指针

08H 4字 起始地址

其中LBA=((柱面*磁头/柱面+磁头)*扇区/柱面)+扇区-1

INT 13

AH=43H

磁盘扩展写操作

AH=43H

AL=写标志

DL = 驱动器号

DS:SI=指向LBA地址包的指针

失败:AH=错误号

CF置位

成功:AH=0

CF=0

同上

INT 13

AH=48H

获取磁盘参数

AH=48H

DL=驱动器号

DS:SI=指向保存参数缓冲区的指针

失败:AH=错误号

CF置位

成功:AH=0

CF=0

参数缓冲区定义:

偏移 大小 描述

00H 字 缓冲区大小

02H 字 信息标志位

04H 双字 物理柱面数

08H 双字 物理磁头数

0CH 双字 物理每柱扇区数

10H 4字 扇区总数

18H 字 每扇区字节数

有了上面的参数,我们就可以方便地编写程序,来操作大容量硬盘。这里举了几个例子来说明如何在C语言中编写程序实现操作。笔者用的C语言是Borland C++ 3.1的编译器。

[程序一] 检测扩展INT 13是否安装

#include <dos.h>

#include <stdio.h>

void main()

{

REGS regs;

SREGS sregs;

regs.h.ah=0x41;regs.x.bx=0x55aa; /*设置寄存器*/

regs.h.dl=0x80;

int86x(0x13,&regs,&regs,&sregs); /*调用中断*/

if (regs.x.bx= =0xaa55) /*对结果作判断*/

printf ("Int 13 Extensions Installed!\n");

else

printf ("Int 13 Extensions Not Installed!\n");

}

在使用扩展INT 13中断之前,最好用上面的程序判断一下,看BIOS是否支持扩展INT13,这样就可以保证你的程序正确无误。一般现在新的支持LBA模式的主板和Win98自带的DOS7操作系统是支持扩展INT 13的。

[程序二] 获得硬盘物理参数

#include <dos.h>

#include <math.h>

#include <stdio.h>

typedef unsigned char BYTE; /*定义字节数据类型名*/

typedef unsigned short WORD; /*定义字数据类型名*/

typedef unsigned long DWORD; /*定义双字数据类型名*/

void main ()

{

union REGS regs;

struct SREGS sregs;

struct {

WORD size; /*地址包大小*/

WORD inforflags; /*信息标志*/

DWORD cylns; /*物理柱面数*/

DWORD heads; /*物理磁头数*/

DWORD sects; /*物理每柱扇区数*/

DWORD tslow; /*扇区总数低八位*/

DWORD tshi; /*扇区总数高八位*/

WORD bps; /*每扇区字节数*/

} package; /*LBA地址包定义*/

regs.h.ah=0x48;

regs.h.dl=0x80;

sregs.ds=FP_SEG(&package);/*获得package的段地址*/

regs.x.si=FP_OFF(&package);/*获得package的偏移量*/

int86x(0x13,&regs,&regs,&sregs);/*调用中断*/

printf ("Total Cylinders in this disk is : %ld\n",package.cylns);/*输出信息*/

printf ("Total Heads in this disk is : %ld\n",package.heads);

printf ("Total Sect/Cyln in this disk is : %ld\n",package.sects);

printf ("Low bit of Total Sectors is : %ld\n",package.tslow);

printf ("High bit of Total Sectors is : %ld\n",package.tshi);

printf ("Bytes per Sector of the disk is : %d\n",package.bps);

printf ("Total Space amount by l-bit is : %.1f GB\n",(double)(package.tslow*512.0/pow(10,9)));

}

对于上面的程序,读者可以把它和上表的参数比较,这样可以更加深刻地了解表一的各个参数的含义,也可以更好地读懂这段程序。读者也可以自己把上面的程序转化成函数,在必要的时候直接调用就可以了。

[程序三 磁盘扩展读写操作]

#include <dos.h>

#include <stdio.h>

#define EXT_READ 0x42

#define EXT_WRITE 0x43

#define HDD 0x80

unsigned long CYLINDERS, HEADS, SECTORS;

typedef unsigned char BYTE;

typedef unsigned short WORD;

typedef unsigned long DWORD;

int get_parameter (int drive) /*读取硬盘参数以便于LBA数据的计算*/

{

union REGS regs;

struct SREGS sregs;

struct {

WORD size;

WORD inforflags;

DWORD cylns;

DWORD heads;

DWORD sects;

DWORD tslow;

DWORD tshi;

WORD bps;

} package;

regs.h.ah=0x48;

regs.h.dl=drive;

sregs.ds=FP_SEG(&package);

regs.x.si=FP_OFF(&package);

int86x(0x13,&regs,&regs,&sregs);

CYLINDERS = package.cylns;/*将得到的数据赋值给全局变量*/

HEADS = package.heads;

SECTORS = package.sects;

if (regs.h.ah) return (regs.h.ah);

else return 0;

}

int iodisk(unsigned char drv, unsigned char cmd, unsigned char *buffer,

unsigned long startlow, unsigned long starthi, unsigned short copyblk)

{

union REGS regs;

struct SREGS sregs;

struct {

unsigned char len; /*package的大小*/

unsigned char res; /*保留字节*/

unsigned short nob; /*package的个数*/

unsigned short bufoff; /*数据缓冲区偏移量*/

unsigned short bufseg;/*数据缓冲区段地址*/

unsigned long slow; /*扇区起始地址低位*/

unsigned long shi; /*扇区起始地址高位*/

} package;

package.len=sizeof(package);

package.res=0;

package.nob=copyblk;

package.bufoff=FP_OFF(buffer);

package.bufseg=FP_SEG(buffer);

package.slow=startlow;

package.shi=starthi;

regs.h.ah=cmd;

regs.h.dl=drv;

regs.h.al=0;

sregs.ds=FP_SEG(&package);

regs.x.si=FP_OFF(&package);

int86x(0x13,&regs,&regs,&sregs);

if (regs.h.ah) return (regs.h.ah);

else return (0);

}

int IoSectorEx (unsigned char drv, unsigned char cmd, unsigned long cylinder,unsigned long head, unsigned long sector, unsigned long copys, unsigned char *buffer)

{

int err;

unsigned long lba;

lba = ((cylinder * HEADS + head) * SECTORS ) + sector - 1; /*将CHS地址转化成LBA的扇区地址*/

err = iodisk (drv, cmd, buffer, lba, 0 , copys);/*调用函数读写扇区*/

return err;

}

void main ()

{

unsigned char buffer[512]; /*定义扇区数据区*/

get_parameter(HDD);/*调用函数获得硬盘参数*/

IoSectorEx (HDD, EXT_READ, 0, 0, 1, 1, buffer); /*调用函数读取0柱面0磁头1扇区的内容*/

printf ("%X\n",buffer[511]);/*输出所读扇区的最后一个字节*/

}

上面的[程序三]中定义了三个被调用函数,get_parameter()函数是通过扩展INT 13来读取硬盘物理参数的函数,设置这个函数主要是为了更加方便地计算LBA扇区地址的值;iodisk()是最通用的扇区读写操作函数,它可以利用LBA扇区的地址直接操作扇区;而IoSectorEx()函数则是利用提供CHS参数的方式来操作扇区的,中间是一个转化过程。

如果你需要的话,你完全可以在你的程序中使用上面的函数。对于扩展INT 13的其它磁盘操作功能,在此就不一一叙述了,其实你只要认真查阅有关资料, 你完全可以写一个功能更强大的函数来实现你所需要的磁盘操作。

参考文献

《细说IDE硬盘的容量限制》(http://www.pchome.net/),dwell

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有