一、概述
首先要說明的是核心每次讀寫的資料量以一個邏輯塊為單位,即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程式主要用於執行底層塊裝置的讀寫操作,為快裝置建立裝置讀寫項,並插入到指定塊裝置請求隊列中。其調用關係,
到檔案系統時再講。