分类: LINUX
2009-09-29 17:13:24
读/写硬盘数据的过程
读数据
1.系统调用hd_out函数向硬盘控制器发送写命令
//// 向硬盘控制器发送命令块。
// 调用参数:drive - 硬盘号(0-1); nsect - 读写扇区数;
// sect - 起始扇区; head - 磁头号;
// cyl - 柱面号; cmd - 命令码(读/写);
// *intr_addr() - 硬盘中断处理程序中将调用的C 处理函数。
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))
{
register int port asm ("dx"); // port 变量对应寄存器dx。
if (drive > 1 || head > 15) // 如果驱动器号(0,1)>1 或磁头号>15,则程序不支持。
panic ("Trying to write bad sector");
if (!controller_ready ()) // 如果等待一段时间后仍未就绪则出错,死机。
panic ("HD controller not ready");
do_hd = intr_addr; // do_hd 函数指针将在硬盘中断程序中被调用。
outb_p (hd_info[drive].ctl, HD_CMD); // 向控制寄存器(0x
port = HD_DATA; // 置dx 为数据寄存器端口(0x
outb_p (hd_info[drive].wpcom >> 2, ++port); // 参数:写预补偿柱面号(需除4)。
outb_p (nsect, ++port); // 参数:读/写扇区总数。
outb_p (sect, ++port); // 参数:起始扇区。
outb_p (cyl, ++port); // 参数:柱面号低8 位。
outb_p (cyl >> 8, ++port); // 参数:柱面号高8 位。
outb_p (0xA0 | (drive << 4) | head, ++port); // 参数:驱动器号+磁头号。
outb (cmd, ++port); // 命令:硬盘控制命令。
}
这个函数的作用就是把要读取的drive - 硬盘号(0-1)、 nsect - 读写扇区数、sect - 起始扇区、 head - 磁头号、cyl - 柱面号、cmd - 命令码(读/写)都通过端口发送到硬盘控制器去。就是相当于系统向硬盘发出了读取数据的通知。当这个函数运行结束后就返回了,接下下的读数据的工作交给硬盘控制器。如果读取数据成功,那么数据会被放到硬盘控制器的一个缓冲区中。当然由于一些原因,硬盘控制器和硬盘也有可能读取数据失败。无论是以失败还是成功结束读取操作,硬盘控制器都会通知操作系统。通知操作系统的方式是通过中断(这个中断是外部中断由硬盘控制器触发中断控制器8259产生的)。中断的处理函数就是intr_addr参数 这是一个函数指针,如果中读操作intr_addr参数就会被值为read_intr()如果是写操作intr_addr参数就会被值为write_intr()。硬盘中断处理程序中将调用的C 处理函数。这里说的是读,那么intr_addr参数就被值为read_intr()。
下面就简单的来说一下static void (void)在这个函数中必须判断读硬盘操作是成功了,失败了。static void (void)会去调用
static int (void)函数的检测读操作是否成功。这里也插入讲一下static int (void)函数是如何来检测的。
static int (void)
{
int i=();
if ((i & ( | | | | ))
== ( | ))
return(0); /* ok */
if (i&1) i=();
return (1);
}
从代码来看,这个函数就是读硬盘控制和CPU引脚相连接的端口。硬盘控制器在读写结束后就会设置这个读写状态端口的值。这个端口的值描述了读写操作是以成功还是失败结束的。当然通过判断这个端口的值就可以知道读写操作是成功了还是失败了。从static int (void)返回0是成功的,1是失败的。
static void (void)调用static int (void)函数调用这个函数检测到读硬盘失败后会尝试几次再读硬盘,几次都失败后才“判刑”是失败。如果检测到读操作成功,说明硬盘控制器已经把数据从硬盘里读出来了,并且数据已放到了硬盘控制器的缓冲区中去,这时static void (void)调用就会调用这个宏(port,,nr),把数据从硬盘控制器的缓冲区中读到系统的缓冲区中去。
#define (port,,nr) \
__asm__("cld;rep;insw"::"d" (port),"D" (),"c" (nr):"cx","di")
这个宏就很简单了,就是把port端口的nr个字(2 bytes)读到buf缓冲区中。这个port就是硬盘控制器的缓冲区,而buf则是系统的缓冲区。最后读硬盘操作结束后
static void (void)调用static void (int uptodate)函数的做一些后期处理的工作。static void (int uptodate)函数的作用是检测uptodate参数,uptodate = 1说明读写硬盘的操作成功 = 0为读写失败。如果读写失败了就会打印出错的信息。然后把当前的硬盘读写请求项指向下一下请求项。如果读写成功就不打印任何信息,直接把当前的硬盘读写请求项指向下一下请求项。