一、概述
首先要说明的是内核每次读写的数据量以一个逻辑块为单位,即1024B,而块设备控制器则是以扇区(512B)为单位。
对硬盘的读写操作是通过中断处理程序进行的,使用读写请求项等待队列来顺序缓冲一次读写多个逻辑块的操作。进程读
硬盘上的一个逻辑块时向缓冲区管理程序提出申请,然后进入睡眠状态。缓冲区管理程序检查缓冲区以前是否已经读取过
这块数据,如有直接将对应的缓冲区块头指针返回给进程并唤醒它,否则,调用ll_rw_block()向相应的设备驱动程序发出
一个读数据块的操作请求。该函数创建一个请求结构项,插入队列,插入时采用电梯算法(至今还没读懂电梯算法)。如对应
设备的请求项队列空,则设备不忙,立即向该设备控制器发出读数据命令。当设备控制器将数据读入到指定的缓冲块中后,就
会发出中断请求信号,并调用相应的读命令后处理函数,处理继续读扇区操作或者结束本次请求项的过程。(引自赵炯老师)
二、数据结构
块设备项
struct blk_dev_struct {
void (*request_fn)(void);//请求项操作的函数指针
struct request * current_request;//当前请求项指针
};
块设备表
extern struct blk_dev_struct blk_dev[NR_BLK_DEV];//每种块设备都在表中占有一项,共7项
请求项
struct request {
int dev; /* -1 if no request */
int cmd; /* READ or WRITE */
int errors;//错误次数
unsigned long sector;//起始扇区(********?*******)
unsigned long nr_sectors;//读写扇区数
char * buffer;//数据缓冲区
struct task_struct * waiting;//发请求的进程
struct buffer_head * bh;//缓冲区头
struct request * next;
};
请求项数组
extern struct request request[NR_REQUEST];//共32项
请求项队列由请求项数组中的项构成,如图
三、硬盘驱动程序
涉及的文件:blk.h,hd.c,ll_rw_block.c
blk.h定义如上列出的数据结构,电梯算法(竟然是用一宏定义实现的),还有解锁指定的缓冲块,结束请求处理等extern inline类型
的函数(仅作为嵌入使用)。
hd.c是硬盘控制器驱动程序。在对硬盘控制器进行控制时,需要同时发送参数(6B)和命令(1B)。按照这个步骤,将hd.c文件中的几乎所有函数串起来。
1.检测控制器空闲状态:读主状态寄存器,若BUSY_STAT(位7)为0,表示空闲,若在规定时间内控制器一直忙,则超时出错。该部分由
controller_ready函数实现:
static int controller_ready(void)
{
int retries=10000;
while (--retries && (inb_p(HD_STATUS)&0xc0)!=0x40);
return (retries);
}
2.检测驱动器就绪:判断主状态寄存器的READY_STAT(位6)是否为1;
static int drive_busy(void)
{
unsigned int i;
for (i = 0; i < 10000; i++)
if (READY_STAT == (inb_p(HD_STATUS) & (BUSY_STAT|READY_STAT)))
break;
i = inb(HD_STATUS);
i &= BUSY_STAT | READY_STAT | SEEK_STAT;
if (i == (READY_STAT | SEEK_STAT))
return(0);
printk("HD controller times out/n/r");
return(1);
}
3.输出命令块,向对应端口输出参数和命令,hd_out函数实现。
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");
if (drive>1 || head>15)
panic("Trying to write bad sector");
if (!controller_ready())
panic("HD controller not ready");
do_hd = intr_addr;
outb_p(hd_info[drive].ctl,HD_CMD);
port=HD_DATA;
outb_p(hd_info[drive].wpcom>>2,++port);
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);
}
4.等待中断产生。命令执行后,由硬盘控制器产生中断请求信号或置控制器状态为空闲,表明操作结束或请求扇区传输。hd.c中在中断
处理过程中调用的函数有5个,分别是,void unexpected_hd_interrupt(void),static void bad_rw_intr(void),static void read_intr(void),static void write_intr(void),static void recal_intr(void)。
5.操作检测结果:cpu再次读主状态寄存器,若位0为0,则表示命令执行成功,否则失败,若失败,还可以进一步查询错误寄存器(HD_ERROR)取错误码。相关的函数是static int win_result(void)。
static int win_result(void)
{
int i=inb_p(HD_STATUS);
if ((i & (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT))
== (READY_STAT | SEEK_STAT))
return(0); /* ok */
if (i&1) i=inb(HD_ERROR);
return (1);
}
整个流程由void do_hd_request(void)控制执行。
ll_rw_block.c程序主要用于执行底层块设备的读写操作,为快设备创建设备读写项,并插入到指定块设备请求队列中。其调用关系如图,
到文件系统时再讲。