?? ll_rw_blk.c
字號:
/* passed* linux/kernel/blk_dev/ll_rw.c** (C) 1991 Linus Torvalds*/#include <set_seg.h>
/** This handles all read/write requests to block devices*//** 該程序處理塊設備的所有讀/寫操作。*/#include <errno.h> // 錯誤號頭文件。包含系統中各種出錯號。(Linus 從minix 中引進的)#include <linux/sched.h> // 調度程序頭文件,定義了任務結構task_struct、初始任務0 的數據,// 還有一些有關描述符參數設置和獲取的嵌入式匯編函數宏語句。#include <linux/kernel.h> // 內核頭文件。含有一些內核常用函數的原形定義。#include <asm/system.h> // 系統頭文件。定義了設置或修改描述符/中斷門等的嵌入式匯編宏。#include "blk.h" // 塊設備頭文件。定義請求數據結構、塊設備數據結構和宏函數等信息。/** The request-struct contains all necessary data* to load a nr of sectors into memory*//** 請求結構中含有加載nr 扇區數據到內存的所有必須的信息。*/struct request request[NR_REQUEST] = {0};/** used to wait on when there are no free requests*//* 是用于請求數組沒有空閑項時的臨時等待處 */struct task_struct *wait_for_request = NULL;/* blk_dev_struct is:* do_request-address* next-request*//* blk_dev_struct 塊設備結構是:(kernel/blk_drv/blk.h,23)* do_request-address //對應主設備號的請求處理程序指針。* current-request // 該設備的下一個請求。*/// 該數組使用主設備號作為索引(下標)。struct blk_dev_struct blk_dev[NR_BLK_DEV] = { {NULL, NULL}, /* no_dev */// 0 - 無設備。 {NULL, NULL}, /* dev mem */// 1 - 內存。 {NULL, NULL}, /* dev fd */// 2 - 軟驅設備。 {NULL, NULL}, /* dev hd */// 3 - 硬盤設備。 {NULL, NULL}, /* dev ttyx */// 4 - ttyx 設備。 {NULL, NULL}, /* dev tty */// 5 - tty 設備。 {NULL, NULL} /* dev lp */// 6 - lp 打印機設備。};// 鎖定指定的緩沖區bh。如果指定的緩沖區已經被其它任務鎖定,則使自己睡眠(不可中斷地等待),// 直到被執行解鎖緩沖區的任務明確地喚醒。static _inline voidlock_buffer (struct buffer_head *bh){ cli (); // 清中斷許可。 while (bh->b_lock) // 如果緩沖區已被鎖定,則睡眠,直到緩沖區解鎖。 sleep_on (&bh->b_wait); bh->b_lock = 1; // 立刻鎖定該緩沖區。 sti (); // 開中斷。}// 釋放(解鎖)鎖定的緩沖區。static _inline voidunlock_buffer (struct buffer_head *bh){ if (!bh->b_lock) // 如果該緩沖區并沒有被鎖定,則打印出錯信息。 printk ("ll_rw_block.c: buffer not locked\n\r"); bh->b_lock = 0; // 清鎖定標志。 wake_up (&bh->b_wait); // 喚醒等待該緩沖區的任務。}/** add-request adds a request to the linked list.* It disables interrupts so that it can muck with the* request-lists in peace.*//** add-request()向連表中加入一項請求。它關閉中斷,* 這樣就能安全地處理請求連表了 *///// 向鏈表中加入請求項。參數dev 指定塊設備,req 是請求的結構信息。 static voidadd_request (struct blk_dev_struct *dev, struct request *req){ struct request *tmp; req->next = NULL; cli (); // 關中斷。 if (req->bh) req->bh->b_dirt = 0; // 清緩沖區“臟”標志。// 如果dev 的當前請求(current_request)子段為空,則表示目前該設備沒有請求項,本次是第1 個// 請求項,因此可將塊設備當前請求指針直接指向請求項,并立刻執行相應設備的請求函數。 if (!(tmp = dev->current_request)) { dev->current_request = req; sti (); // 開中斷。 (dev->request_fn) (); // 執行設備請求函數,對于硬盤(3)是do_hd_request()。 return; }// 如果目前該設備已經有請求項在等待,則首先利用電梯算法搜索最佳位置,然后將當前請求插入// 請求鏈表中。 for (; tmp->next; tmp = tmp->next) if ((IN_ORDER (tmp, req) || !IN_ORDER (tmp, tmp->next)) && IN_ORDER (req, tmp->next)) break; req->next = tmp->next; tmp->next = req; sti ();}//// 創建請求項并插入請求隊列。參數是:主設備號major,命令rw,存放數據的緩沖區頭指針bh。static voidmake_request (int major, int rw, struct buffer_head *bh){ struct request *req; int rw_ahead;/* WRITEA/READA 是特殊的情況 - 它們并不是必要的,所以如果緩沖區已經上鎖,*//* 我們就不管它而退出,否則的話就執行一般的讀/寫操作。 */// 這里'READ'和'WRITE'后面的'A'字符代表英文單詞Ahead,表示提前預讀/寫數據塊的意思。// 當指定的緩沖區正在使用,已被上鎖時,就放棄預讀/寫請求。 if (rw_ahead = (rw == READA || rw == WRITEA)) { if (bh->b_lock) return; if (rw == READA) rw = READ; else rw = WRITE; }// 如果命令不是READ 或WRITE 則表示內核程序有錯,顯示出錯信息并死機。 if (rw != READ && rw != WRITE) panic ("Bad block dev command, must be R/W/RA/WA");// 鎖定緩沖區,如果緩沖區已經上鎖,則當前任務(進程)就會睡眠,直到被明確地喚醒。 lock_buffer (bh);// 如果命令是寫并且緩沖區數據不臟,或者命令是讀并且緩沖區數據是更新過的,則不用添加// 這個請求。將緩沖區解鎖并退出。 if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) { unlock_buffer (bh); return; }repeat:/* 我們不能讓隊列中全都是寫請求項:我們需要為讀請求保留一些空間:讀操作* 是優先的。請求隊列的后三分之一空間是為讀準備的。*/// 請求項是從請求數組末尾開始搜索空項填入的。根據上述要求,對于讀命令請求,可以直接// 從隊列末尾開始操作,而寫請求則只能從隊列的2/3 處向頭上搜索空項填入。 if (rw == READ) req = request + NR_REQUEST; // 對于讀請求,將隊列指針指向隊列尾部。 else req = request + ((NR_REQUEST * 2) / 3); // 對于寫請求,隊列指針指向隊列2/3 處。/* 搜索一個空請求項 */// 從后向前搜索,當請求結構request 的dev 字段值=-1 時,表示該項未被占用。 while (--req >= request) if (req->dev < 0) break;/* 如果沒有找到空閑項,則讓該次新請求睡眠:需檢查是否提前讀/寫 */// 如果沒有一項是空閑的(此時request 數組指針已經搜索越過頭部),則查看此次請求是否是// 提前讀/寫(READA 或WRITEA),如果是則放棄此次請求。否則讓本次請求睡眠(等待請求隊列// 騰出空項),過一會再來搜索請求隊列。 if (req < request) { // 如果請求隊列中沒有空項,則 if (rw_ahead) { // 如果是提前讀/寫請求,則解鎖緩沖區,退出。 unlock_buffer (bh); return; } sleep_on (&wait_for_request); // 否則讓本次請求睡眠,過會再查看請求隊列。 goto repeat; }/* 向空閑請求項中填寫請求信息,并將其加入隊列中 */// 請求結構參見(kernel/blk_drv/blk.h,23)。 req->dev = bh->b_dev; // 設備號。 req->cmd = rw; // 命令(READ/WRITE)。 req->errors = 0; // 操作時產生的錯誤次數。 req->sector = bh->b_blocknr << 1; // 起始扇區。(1 塊=2 扇區) req->nr_sectors = 2; // 讀寫扇區數。 req->buffer = bh->b_data; // 數據緩沖區。 req->waiting = NULL; // 任務等待操作執行完成的地方。 req->bh = bh; // 緩沖區頭指針。 req->next = NULL; // 指向下一請求項。 add_request (major + blk_dev, req); // 將請求項加入隊列中(blk_dev[major],req)。}//// 低層讀寫數據塊函數。// 該函數主要是在fs/buffer.c 中被調用。實際的讀寫操作是由設備的request_fn()函數完成。// 對于硬盤操作,該函數是do_hd_request()。(kernel/blk_drv/hd.c,294)void ll_rw_block (int rw, struct buffer_head *bh){ unsigned int major; // 主設備號(對于硬盤是3)。// 如果設備的主設備號不存在或者該設備的讀寫操作函數不存在,則顯示出錯信息,并返回。 if ((major = MAJOR (bh->b_dev)) >= NR_BLK_DEV || !(blk_dev[major].request_fn)) { printk ("Trying to read nonexistent block-device\n\r"); return; } make_request (major, rw, bh); // 創建請求項并插入請求隊列。}//// 塊設備初始化函數,由初始化程序main.c 調用(init/main.c,128)。// 初始化請求數組,將所有請求項置為空閑項(dev = -1)。有32 項(NR_REQUEST = 32)。void blk_dev_init (void){ int i; for (i = 0; i < NR_REQUEST; i++) { request[i].dev = -1; request[i].next = NULL; }}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -