學寫塊裝置驅動(三)----踢開IO排程器,自己處理bio(上)
前兩篇我們編寫了在記憶體中的最簡單的塊裝置驅動程式,併為其更換了我們心儀的’noop‘IO排程器。本篇我們試著搞清楚核心的塊裝置層在這裡為我們做的事情,以及我們如何做點自己想做的事情。
其實,我們前面兩篇都是圍繞著請求佇列(request_queue)這東西做事情。初始化請求佇列時我們註冊上驅動處理請求(request)的策略函式(simp_blkdev_do_request),然後在gendisk結構初始化時又填充上前面初始化好的queue。後面我們又用‘noop’IO排程器更換掉預設的'cfq'排程器。
下面試著搞清楚通用塊層在這裡的框架機制。先看一張圖:
當通用塊層以上的層要對塊裝置進行訪問時,通常是準備好一個bio,呼叫generic_make_request(struct bio *bio),OK。
但是我們是編寫底層驅動的可憐IT男,要是不知道generic_make_request是怎麼把我們前面實現的simp_blkdev_do_request和request_queue聯絡起來,那就相當沒有安全感。於是,我們開始RTFSC。
既然是圍著request_queue做文章,那麼我們先看下這個資料結構:
struct request_queue{
......
request_fn_proc *request_fn;// Method that implements the entry point of the strategy routine of the driver
make_request_fn *make_request_fn;//Method invoked when a new request has to be inserted in the queue
......
}
聰明的你肯定也看出來了,上面是兩個函式指標,它們的定義如下:
typedef void (request_fn_proc) (struct request_queue *q);
typedef int (make_request_fn) (struct request_queue *q, struct bio *bio);
對上面的資料結構留個印象,我們開始看 通用塊層的入口函式generic_make_request(struct bio *bio)。你可以發現下面的呼叫關係
generic_make_request(struct bio *bio) ------> __generic_make_request ------> q->make_request_fn(q, bio)
於是我們得出結論,generic_make_request()最終是通過呼叫request_queue.make_request_fn函式完成bio所描述的請求處理的。
那麼,make_request_fn具體又指向哪個函式呢?我們前面也沒有實現過make_request_fn這樣的函式啊?!
我們只記得初始化request_queue時呼叫了blk_init_queue(request_fn_proc *, spinlock_t *)這個函式,所以我們來看一下這個函式,
blk_init_queue(request_fn_proc *rfn, spinlock_t *lock) ------> blk_init_queue_node() ------>
q-> unplug_fn = generic_unplug_device;
q->request_fn = rfn;
blk_queue_make_request(q, __make_reqeust) ------> q->make_request_fn = mfn(即__make_request)
原來,我們request_queue的make_request_fn實際上指向了__make_request()函式。這樣,大名鼎鼎的__make_request()函式就可以叫來某個IO排程器幫忙,並對bio做些利於使用者的加工,比如將其對映到非線性對映區域。至此,我們知道了一個新的request是如何被提交給IO排程器的了(通過__make_request)。我們也知道了我們編寫的simp_blkdev_do_request就是在這裡被賦值給q->request_fn的了(通過blk_queue_make_request)。那麼,通用塊層的框架是什麼時候對我們的simp_blkdev_do_request函式進行呼叫,從而真正執行對資料的拷貝了呢?
這裡有點複雜,先補充一點理論知識,就一點,塊裝置有“阻塞”和“非阻塞”的狀態,從而通用塊層才能利用這樣的機制推遲對請求的處理,從而給了IO排程參與的機會;當“非阻塞”時(該函式為blk_remove_plug),才能夠處理請求。
有了這樣的知識,我們RTFSC時見到blk_remove_plug就不奇怪了。好了,我們下面給出q->request_fn指向的simp_blkdev_do_request在何時被呼叫。
這次從__make_request()開始,這函式果然強大。。
__make_request() ------> add_request() ------> __elv_add_request() ------> elv_insert() ------> blk_remove_plug ------> blk_unplug_timeout ------> kblocked_schedule_work ------> blk_unplug_work() ------> q->unplug_fn()
如果你循到了上面的呼叫關係,那麼恭喜你,你遇到了新問題,q->unplug_fn是哪隻鳥,它呼叫的誰??
如果你記性還不錯,那麼你會想起我們blk_init_queue呼叫關係裡面有一個“ q-> unplug_fn = generic_unplug_device; ”,很好,我們發現q->unplug_fn其實呼叫的是通用塊層另一個小有名氣的函式,generic_unplug_device()。我們繼續尋找呼叫關係。
generic_unplug_device() ------> __generic_unplug_device() ------> q->request_fn()
到這裡,長征結束,期間和IO排程器打個照面,見識了塊裝置解除阻塞狀態,還喚醒了核心的工作佇列,但最終我們可以大呼一口氣了。。。
綜上所述,當我們實現了塊裝置驅動程式的策略函式(例如前面實現的simp_blkdev_do_request)並用其作為引數初始化一個request_queue後,通用塊層的make_request_fn 函式指標幫我們指定了強力幫手 __make_request,該幫手又拉上了IO排程器,於是,一個bio經過通用塊層、IO排程層的處理,最後以request_queue中的request餵給我們實現的策略函式。
相關推薦
學寫塊裝置驅動(三)----踢開IO排程器,自己處理bio(下)
本篇的(上)基本搞清楚了我們已經實現的記憶體塊裝置驅動和通用塊層之間的絲絲聯絡。現在我們該做點自己想做的事情了: 踢開IO排程器,自己來處理bio。 踢開IO排程器很容易,即不使用__make_request 這個系統指定的強力函式,如何不使用?其實我們從(上)的blk_init_queue()函式中也能看
學寫塊裝置驅動(三)----踢開IO排程器,自己處理bio(上)
前兩篇我們編寫了在記憶體中的最簡單的塊裝置驅動程式,併為其更換了我們心儀的’noop‘IO排程器。本篇我們試著搞清楚核心的塊裝置層在這裡為我們做的事情,以及我們如何做點自己想做的事情。 其實,我們前面兩篇都是圍繞著請求佇列(request_queue)這東西做事情。初始化請求佇列時我們註冊上驅動處理請求(r
linux驅動由淺入深系列:塊裝置驅動之三(塊裝置驅動結構分析,以mmc為例)
linux驅動由淺入深系列:塊裝置驅動之一(高通eMMC分割槽例項)前一篇文章介紹了塊裝置驅動在linux框架張的位置關係,本文來分析一下驅動本身。塊裝置驅動的模型還是基本基於字元裝置驅動的,可以簡單理解為塊裝置僅僅增加了操作緩衝區,對使用者操作請求進行佇列重排。因此只在有了
Linux塊裝置驅動(一)————塊裝置的結構及磁碟的結構
塊裝置的結構及磁碟的結構 1、扇區 磁碟上的每個磁軌被等分成若干個弧段,這些弧段便是磁碟的扇區。磁碟驅動器在向磁碟讀寫資料時,都是以扇區為單位。一般為512個位元組,但也有1024或者
linux 塊裝置驅動(1)
功能:新增一個ramdisk這樣的裝置,並且進行格式化,掛在,讀寫,很簡單的一個塊裝置例子,剛剛學習裝置驅動 <font color="#ff0000" size="5">看完,並且執行完程式碼,希望大家和我有一樣的問題,就是新的裝置空間那裡來的?原來的硬碟?
Linux驅動開發----塊裝置驅動(記憶體模擬)Tiny6410
寫了好久的字元裝置驅動,是時候看下塊裝置驅動程式設計方法了,塊裝置驅動和字元裝置不同,字元裝置是直接和虛擬檔案系統進行互動,而塊裝置驅動則是通過塊緩衝/排程層間接和虛擬檔案系統互動;塊裝置驅動資料訪問都是以塊為單位;多個塊I/O需要組成一個請求佇列,這個功能是塊緩衝/排程層
linux下的塊裝置驅動(二)
上一章主要講了請求佇列的一系列問題。下面主要說一下請求函式。首先來說一下硬碟類塊裝置的請求函式。 請求函式可以在沒有完成請求佇列的中的所有請求的情況下就返回,也可以在一個請求都不完成的情況下就返回。 下面貼出請求函式的例程: static int simp_blkdev_m
Linux塊裝置驅動(五)————通用塊層
通用塊層是塊裝置驅動的核心部分,這部分主要包含塊裝置驅動程式的通過程式碼部分。 1、通用塊層 通用塊層是一個核心元件,他處理來自系統其他元件發出的塊裝置請求。換句話說,通用塊層包含
Linux驅動編寫(塊裝置驅動程式碼)
【 宣告:版權所有,歡迎轉載,請勿用於商業用途。 聯絡信箱:feixiaoxing @163.com】 按照ldd的說法,linux的裝置驅動包括了char,block,net三種裝置。char裝置是比較簡單的,只要分配了major、minor號,就可以進行讀寫處理了
S3C2440 塊裝置驅動程式的編寫驅動之用記憶體模擬硬碟(二十一)
通過上節的塊裝置驅動分析,本節便通過記憶體來模擬塊裝置驅動,方便我們更加熟悉塊裝置驅動框架 參考核心自帶的塊裝置驅動程式: drivers/block/xd.c drivers/block/z2ram.c 1、本節需要的結構體如下: 1.1 gendis
linux下的塊裝置驅動(一)
塊裝置的驅動比字符裝置的難,這是因為塊裝置的驅動和核心的聯絡進一步增大,但是同時塊裝置的訪問的幾個基本結構和字元還是有相似之處的。 有一句話必須記住:對於儲存裝置(硬碟~~帶有機械的操作)而言,調整讀寫的順序作用巨大,因為讀寫連續的扇區比分離的扇區快。 但是同時:SD卡和
linux驅動由淺入深系列:塊裝置驅動之一(高通eMMC分割槽例項)
塊裝置驅動的模型還是基本基於字元裝置驅動的,可以簡單理解為塊裝置僅僅增加了操作緩衝區,對使用者請求順序進行佇列重拍等等。字元裝置驅動的相關分析可以檢視本部落格相關的博文。 按照本部落格的行文習慣,在具體分析塊裝置驅動程式碼之前,我們會從整體上了解一下研究物件的特徵,以及使用者
塊裝置驅動實戰基礎篇一 (170行程式碼構建一個邏輯塊裝置驅動)
作業系統是如何將資料讀到緩衝區的,發生了什麼?我們帶著這樣的問題,粗略走一下read呼叫系統過程,希望這個初探,可以喚起大家研究作業系統核心的好奇心和興趣,並以此為例,讓我們先初步對請求在過濾塊裝置驅動中的處理過程有個大概印象和了解。 塊裝置在整個Linux中應用的
Linux裝置驅動第三天(字元裝置驅動、cdev)
裝置號的分配 靜態分配: 動態分配: /** * 功能:動態申請裝置號 * dev:存放返回的裝置號的結構體 * firstminor:指定次裝置號 * count:連續編號範圍 * name:編號相關聯的裝置名稱. (/proc/devices)
塊裝置驅動(3)——nand flash驅動
/* 參考 * drivers\mtd\nand\s3c2410.c * drivers\mtd\nand\at91_nand.c */ #include <linux/module.h> #include <linux/types.h> #
從零開始之驅動發開、linux驅動(三十二、簡單方式的lcd的背光碟機動)
前面lcd章節我們知道了LCD的背光可以由兩種方式決定調節: 1.一種是I/O口直接輸出高低電平來控制背光的量滅,這種方式簡單,但不能調背光亮度。 2.另一種是採用PWM調節脈衝寬度的方式來控制背光,這種方式需要採用PWM驅動來實現,優點是可以調節螢幕亮度,節省電量。
linux塊裝置驅動程式示例(適用於高版本核心3.16.0
1. 字元裝置與塊裝置的 I/O 操作主要有如下不同點: (1)塊裝置只能以塊為單位接受輸入和返回輸出,而字元裝置則以位元組為單位。大多數裝置是字元裝置,因為它們不需要緩衝而且不以固定塊大小進行操作。 (2)塊裝置對於 I/O 請求有對應的緩衝區,因此它們可以選擇
linux裝置驅動第三篇:寫一個簡單的字元裝置驅動
在linux裝置驅動第一篇:裝置驅動程式簡介中簡單介紹了字元驅動,本篇簡單介紹如何寫一個簡單的字元裝置驅動。本篇借鑑LDD中的原始碼,實現一個與硬體裝置無關的字元裝置驅動,僅僅操作從核心中分配的一些記憶體。 下面就開始學習如何寫一個簡單的字元裝置驅動。首先我們來分解一下字元
linux 驅動入門程式,寫一個塊裝置驅動 (1)
+---------------------------------------------------+ | 寫一個塊裝置驅動 | +---------------------------------------------------+ |
轉:寫一個塊裝置驅動
----------------------- Page 1----------------------- 第 1章 +---------------------------------------------------+ | 寫一