本文結(jié)合具體代碼對 Linux 內(nèi)核中的 device mapper 映射機(jī)制進(jìn)行了介紹。Device mapper 是 Linux 2.6 內(nèi)核中提供的一種從邏輯設(shè)備到物理設(shè)備的映射框架機(jī)制,在該機(jī)制下,用戶可以很方便的根據(jù)自己的需要制定實(shí)現(xiàn)存儲資源的管理策略,當(dāng)前比較流行的 Linux 下的邏輯卷管理器如 LVM2(Linux Volume Manager 2 version)、EVMS(Enterprise Volume Management System)、dmraid(Device Mapper Raid Tool)等都是基于該機(jī)制實(shí)現(xiàn)的。理解該機(jī)制是進(jìn)一步分析、理解這些卷管理器的實(shí)現(xiàn)及設(shè)計的基礎(chǔ)。通過本文也可以進(jìn)一步理解 Linux 系統(tǒng)塊一級 IO的設(shè)計和實(shí)現(xiàn)。
Device Mapper 是 Linux2.6 內(nèi)核中支持邏輯卷管理的通用設(shè)備映射機(jī)制,它為實(shí)現(xiàn)用于存儲資源管理的塊設(shè)備驅(qū)動提供了一個高度模塊化的內(nèi)核架構(gòu),如圖 1。
圖1 Device Mapper的內(nèi)核體系架構(gòu)
在內(nèi)核中它通過一個一個模塊化的 target driver 插件實(shí)現(xiàn)對 IO 請求的過濾或者重新定向等工作,當(dāng)前已經(jīng)實(shí)現(xiàn)的 target driver 插件包括軟 raid、軟加密、邏輯卷?xiàng)l帶、多路徑、鏡像、快照等,圖中 linear、mirror、snapshot、multipath 表示的就是這些 target driver。Device mapper 進(jìn)一步體現(xiàn)了在 Linux 內(nèi)核設(shè)計中策略和機(jī)制分離的原則,將所有與策略相關(guān)的工作放到用戶空間完成,內(nèi)核中主要提供完成這些策略所需要的機(jī)制。Device mapper 用戶空間相關(guān)部分主要負(fù)責(zé)配置具體的策略和控制邏輯,比如邏輯設(shè)備和哪些物理設(shè)備建立映射,怎么建立這些映射關(guān)系等等,而具體過濾和重定向 IO 請求的工作由內(nèi)核中相關(guān)代碼完成。因此整個 device mapper 機(jī)制由兩部分組成--內(nèi)核空間的 device mapper 驅(qū)動、用戶空間的device mapper 庫以及它提供的 dmsetup 工具。在下文中,我們分內(nèi)核和用戶空間兩部分進(jìn)行介紹。
內(nèi)核部分
Device mapper 的內(nèi)核相關(guān)代碼已經(jīng)作為 Linux 2.6 內(nèi)核發(fā)布版的一部分集成到內(nèi)核源碼中了,相關(guān)代碼在內(nèi)核源碼的 driver/md/ 目錄中,其代碼文件可以劃分為實(shí)現(xiàn) device mapper 內(nèi)核中基本架構(gòu)的文件和實(shí)現(xiàn)具體映射工作的 target driver 插件文件兩部分。文章下面的分析結(jié)果主要是基于上述源碼文件得到的。
重要概念
Device mapper 在內(nèi)核中作為一個塊設(shè)備驅(qū)動被注冊的,它包含三個重要的對象概念,mapped device、映射表、target device。Mapped device 是一個邏輯抽象,可以理解成為內(nèi)核向外提供的邏輯設(shè)備,它通過映射表描述的映射關(guān)系和 target device 建立映射。從 Mapped device 到一個 target device 的映射表由一個多元組表示,該多元組由表示 mapped device 邏輯的起始地址、范圍、和表示在 target device 所在物理設(shè)備的地址偏移量以及target 類型等變量組成(這些地址和偏移量都是以磁盤的扇區(qū)為單位的,即 512 個字節(jié)大?。?。Target device 表示的是 mapped device 所映射的物理空間段,對 mapped device 所表示的邏輯設(shè)備來說,就是該邏輯設(shè)備映射到的一個物理設(shè)備。Device mapper 中這三個對象和 target driver 插件一起構(gòu)成了一個可迭代的設(shè)備樹。在該樹型結(jié)構(gòu)中的頂層根節(jié)點(diǎn)是最終作為邏輯設(shè)備向外提供的 mapped device,葉子節(jié)點(diǎn)是 target device 所表示的底層物理設(shè)備。最小的設(shè)備樹由單個 mapped device 和 target device 組成。每個 target device 都是被mapped device 獨(dú)占的,只能被一個 mapped device 使用。一個 mapped device 可以映射到一個或者多個 target device 上,而一個 mapped device 又可以作為它上層 mapped device的 target device 被使用,該層次在理論上可以在 device mapper 架構(gòu)下無限迭代下去。
圖2 Device mapper 內(nèi)核中各對象的層次關(guān)系
在圖2 中我們可以看到 mapped device1 通過映射表和 a、b、c 三個 target device 建立了映射關(guān)系,而 target device a 又是通過 mapped device 2 演化過來,mapped device 2 通過映射表和 target device d 建立映射關(guān)系。
我們進(jìn)一步看一下上述三個對象在代碼中的具體實(shí)現(xiàn),dm.c 文件定義的 mapped_device 結(jié)構(gòu)用于表示 mapped device,它主要包括該 mapped device 相關(guān)的鎖,注冊的請求隊列和一些內(nèi)存池以及指向它所對應(yīng)映射表的指針等域。Mapped device 對應(yīng)的映射表是由 dm_table.c 文件中定義的 dm_table 結(jié)構(gòu)表示的,該結(jié)構(gòu)中包含一個 dm_target結(jié)構(gòu)數(shù)組,dm_target 結(jié)構(gòu)具體描述了 mapped_device 到它某個 target device 的映射關(guān)系。而在 dm_table 結(jié)構(gòu)中將這些 dm_target 按照 B 樹的方式組織起來方便 IO 請求映射時的查找操作。Dm_target 結(jié)構(gòu)具體記錄該結(jié)構(gòu)對應(yīng) target device 所映射的 mapped device 邏輯區(qū)域的開始地址和范圍,同時還包含指向具體 target device 相關(guān)操作的 target_type 結(jié)構(gòu)的指針。Target_type 結(jié)構(gòu)主要包含了 target device 對應(yīng)的 target driver 插件的名字、定義的構(gòu)建和刪除該類型target device的方法、該類target device對應(yīng)的IO請求重映射和結(jié)束IO的方法等。而表示具體的target device的域是dm_target中的private域,該指針指向mapped device所映射的具體target device對應(yīng)的結(jié)構(gòu)。表示target device的具體結(jié)構(gòu)由于不同的target 類型而不同,比如最簡單的線性映射target類型對應(yīng)target device的結(jié)構(gòu)是dm-linear.c文件中定義的linear_c結(jié)構(gòu)。其定義如下:
struct linear_c { struct dm_dev *dev; sector_t start; };
該target device的定義相當(dāng)簡單,就只包括了表示對應(yīng)物理設(shè)備的dm_dev結(jié)構(gòu)指針和在該物理設(shè)備中以扇區(qū)為單位的偏移地址start。上述幾個數(shù)據(jù)結(jié)構(gòu)關(guān)系如圖3所示:
圖3 device mapper中幾個重要數(shù)據(jù)結(jié)構(gòu)的關(guān)系
內(nèi)核中建立過程
在下面我們結(jié)合具體的代碼簡要介紹下在內(nèi)核中創(chuàng)建一個mapped device的過程:
1、 根據(jù)內(nèi)核向用戶空間提供的ioctl 接口傳來的參數(shù),用dm-ioctl.c文件中的dev_create函數(shù)創(chuàng)建相應(yīng)的mapped device結(jié)構(gòu)。這個過程很簡單,主要是向內(nèi)核申請必要的內(nèi)存資源,包括mapped device和為進(jìn)行IO操作預(yù)申請的內(nèi)存池,通過內(nèi)核提供的blk_queue_make_request函數(shù)注冊該mapped device對應(yīng)的請求隊列dm_request。并將該mapped device作為磁盤塊設(shè)備注冊到內(nèi)核中。
2、 調(diào)用dm_hash_insert將創(chuàng)建好的mapped device插入到device mapper中的一個全局hash表中,該表中保存了內(nèi)核中當(dāng)前創(chuàng)建的所有mapped device。
3、 用戶空間命令通過ioctl調(diào)用table_load函數(shù),該函數(shù)根據(jù)用戶空間傳來的參數(shù)構(gòu)建指定mapped device的映射表和所映射的target device。該函數(shù)先構(gòu)建相應(yīng)的dm_table、dm_target結(jié)構(gòu),再調(diào)用dm-table.c中的dm_table_add_target函數(shù)根據(jù)用戶傳入的參數(shù)初始化這些結(jié)構(gòu),并且根據(jù)參數(shù)所指定的target類型,調(diào)用相應(yīng)的target類型的構(gòu)建函數(shù)ctr在內(nèi)存中構(gòu)建target device對應(yīng)的結(jié)構(gòu),然后再根據(jù)所建立的dm_target結(jié)構(gòu)更新dm_table中維護(hù)的B樹。上述過程完畢后,再將建立好的dm_table添加到mapped device的全局hash表對應(yīng)的hash_cell結(jié)構(gòu)中。
4、 最后通過ioctl調(diào)用do_resume函數(shù)建立mapped device和映射表之間的綁定關(guān)系,事實(shí)上該過程就是通過dm_swap_table函數(shù)將當(dāng)前dm_table結(jié)構(gòu)指針值賦予 mapped_device相應(yīng)的map域中,然后再修改mapped_device表示當(dāng)前狀態(tài)的域。
通過上述的4個主要步驟,device mapper在內(nèi)核中就建立一個可以提供給用戶使用的mapped device邏輯塊設(shè)備。
IO流
Device mapper本質(zhì)功能就是根據(jù)映射關(guān)系和target driver描述的IO處理規(guī)則,將IO請求從邏輯設(shè)備mapped device轉(zhuǎn)發(fā)相應(yīng)的target device上。Device mapper處理所有從內(nèi)核中塊一級IO子系統(tǒng)的generic_make_request和submit_bio接口[兩個接口具體的描述可以查看參考文獻(xiàn)[1] 和[2],這兩本書對內(nèi)核中的塊IO層有比較詳盡的講解。] 中定向到mapped device的所有塊讀寫IO請求。IO請求在device mapper的設(shè)備樹中通過請求轉(zhuǎn)發(fā)從上到下地進(jìn)行處理。當(dāng)一個bio請求在設(shè)備樹中的mapped deivce向下層轉(zhuǎn)發(fā)時,一個或者多個bio的克隆被創(chuàng)建并發(fā)送給下層target device。然后相同的過程在設(shè)備樹的每一個層次上重復(fù),只要設(shè)備樹足夠大理論上這種轉(zhuǎn)發(fā)過程可以無限進(jìn)行下去。在設(shè)備樹上某個層次中,target driver結(jié)束某個bio請求后,將表示結(jié)束該bio請求的事件上報給它上層的mapped device,該過程在各個層次上進(jìn)行直到該事件最終上傳到根mapped device的為止,然后device mapper結(jié)束根mapped device上原始bio請求,結(jié)束整個IO請求過程。
Bio在device mapper的設(shè)備樹進(jìn)行逐層的轉(zhuǎn)發(fā)時,最終轉(zhuǎn)發(fā)到一個或多個葉子target節(jié)點(diǎn)終止。因?yàn)橐粋€bio請求不可以跨多個target device(亦即物理空間段), 因此在每一個層次上,device mapper根據(jù)用戶預(yù)先告知的mapped device 的target映射信息克隆一個或者多個bio,將bio進(jìn)行拆分后轉(zhuǎn)發(fā)到對應(yīng)的target device上。這些克隆的bio先交給mapped device上對應(yīng)的target driver上進(jìn)行處理,根據(jù)target driver中定義的IO處理規(guī)則進(jìn)行IO請求的過濾等處理,然后再提交給target device完成。上述過程在dm.c文件中的dm_request函數(shù)中完成。Target driver可以對這些bio做如下處理:
1、 將這些bio在本驅(qū)動內(nèi)部排隊等待以后進(jìn)行處理;
2、 將bio重新定向到一個或多個target device上或者每個target device上的不同扇區(qū);
3、 向device mapper返回error 狀態(tài)。
IO請求就按照上文中描述的過程在圖2中所示的設(shè)備樹中逐層進(jìn)行處理,直到IO請求結(jié)束。
小結(jié)
Device mapper在內(nèi)核中向外提供了一個從邏輯設(shè)備到物理設(shè)備的映射架構(gòu),只要用戶在用戶空間制定好映射策略,按照自己的需要編寫處理具體IO請求的 target driver插件,就可以很方便的實(shí)現(xiàn)一個類似LVM的邏輯卷管理器。Device mapper以ioctl的方式向外提供接口,用戶通過用戶空間的device mapper庫,向device mapper的字符設(shè)備發(fā)送ioctl命令,完成向內(nèi)的通信。它還通過ioctl提供向往的事件通知機(jī)制,允許target driver將IO相關(guān)的某些事件傳送到用戶空間。
用戶空間部分
Device mapper在用戶空間相對簡單,主要包括device mapper庫和dmsetup工具。Device mapper庫就是對ioctl、用戶空間創(chuàng)建刪除device mapper邏輯設(shè)備所需必要操作的封裝,dmsetup是一個提供給用戶直接可用的創(chuàng)建刪除device mapper設(shè)備的命令行工具。因?yàn)樗鼈兊墓δ芎土鞒滔鄬唵?,在本文中對它們的?xì)節(jié)就不介紹了,用戶空間主要負(fù)責(zé)如下工作:
1、 發(fā)現(xiàn)每個mapped device相關(guān)的target device;
2、 根據(jù)配置信息創(chuàng)建映射表;
3、 將用戶空間構(gòu)建好的映射表傳入內(nèi)核,讓內(nèi)核構(gòu)建該mapped device對應(yīng)的dm_table結(jié)構(gòu);
4、 保存當(dāng)前的映射信息,以便未來重新構(gòu)建。
以下我們主要通過實(shí)例來說明dmsetup的使用,同時進(jìn)一步說明device mapper這種映射機(jī)制。用戶空間中最主要的工作就是構(gòu)建并保存映射表,下面給出一些映射表的例子:
1)
0 1024 linear /dev/sda 204
1024 512 linear /dev/sdb 766
1536 128 linear /dev/sdc 0
2) 0 2048 striped 2 64 /dev/sda 1024 /dev/sdb 0
3) 0 4711 mirror core 2 64 nosync 2 /dev/sda 2048 /dev/sdb 1024
例子1中將邏輯設(shè)備0~1023扇區(qū)、1024~1535扇區(qū)以及1536~1663三個地址范圍分別以線形映射的方式映射到/dev/sda設(shè)備第204號扇區(qū)、/dev/sdb設(shè)備第766號扇區(qū)和/dev/sdc設(shè)備的第0號扇區(qū)開始的區(qū)域。
例子2中將邏輯設(shè)備從0號扇區(qū)開始的,長度為2048個扇區(qū)的段以條帶的方式映射的到/dev/sda設(shè)備的第1024號扇區(qū)以及/dev/sdb設(shè)備的第 0號扇區(qū)開始的區(qū)域。同時告訴內(nèi)核這個條帶類型的target driver存在2個條帶設(shè)備與邏輯設(shè)備做映射,并且條帶的大小是64個扇區(qū),使得驅(qū)動可以該值來拆分跨設(shè)備的IO請求。
例子3中將邏輯設(shè)備從0號扇區(qū)開始的,長度為4711個扇區(qū)的段以鏡像的方式映射到/dev/sda設(shè)備的第2048個扇區(qū)以及/dev/sdb設(shè)備的第1024號扇區(qū)開始的區(qū)域。
映射表確定后,創(chuàng)建、刪除邏輯設(shè)備的操作就相對簡單,通過dmsetup如下命令就可以完成相應(yīng)的操作。
dmsetup create 設(shè)備名 映射表文件 /* 根據(jù)指定的映射表創(chuàng)建一個邏輯設(shè)備 */
dmsetup reload 設(shè)備名 映射表文件 /* 為指定設(shè)備從磁盤中讀取映射文件,重新構(gòu)建映射關(guān)系 */
dmsetup remove 設(shè)備名 /* 刪除指定的邏輯設(shè)備 */
圖4 根據(jù)例子1中映射表在內(nèi)核中建立的邏輯設(shè)備
當(dāng)用戶空間根據(jù)映射表下達(dá)創(chuàng)建邏輯設(shè)備命令后,device mapper在內(nèi)核中就根據(jù)傳入的參數(shù)和映射關(guān)系建立邏輯地址到物理地址的映射關(guān)系。根據(jù)映射表例子1中的映射關(guān)系建立的設(shè)備如圖4所示,圖中的下半部分就抽象地描繪出了按照該映射表在內(nèi)核中建立的邏輯地址到物理地址的映射關(guān)系。
Device mapper的用戶空間部分對開發(fā)者要實(shí)現(xiàn)自己的存儲管理工具來說是可選的,事實(shí)上,很多我們常見的邏輯卷管理器,比如LVM2、dmraid等工具都利用device mapper的提供的device mapper用戶空間庫,根據(jù)自己的管理需求建立獨(dú)立的一套管理工具,而并沒有使用它提供的dmsetup工具,甚至IBM的開源項(xiàng)目企業(yè)級的邏輯卷管理系統(tǒng)-EVMS,在實(shí)現(xiàn)中都沒有采用device mapper的用戶空間庫,完全根據(jù)內(nèi)核中的ioctl定義實(shí)現(xiàn)了一套自己的函數(shù)庫。
Target Driver
Device mapper提供了一個統(tǒng)一的架構(gòu),通過target driver 插件的方式允許用戶根據(jù)實(shí)際的需要指定自己的IO處理規(guī)則,因此target driver充分體現(xiàn)了device mapper的靈活性。在上文中我們已經(jīng)不止一次的提到過target driver,也描述過target driver的功能,在這里我們結(jié)合最簡單的linear target driver具體介紹target driver的實(shí)現(xiàn)。
Target driver主要定義對IO請求的處理規(guī)則,在device mapper中對target driver的操作已定義好了統(tǒng)一的接口,在實(shí)現(xiàn)中該接口由我們上文提到的target_type結(jié)構(gòu)中定義,它定義了以下target driver的方法:
1、 構(gòu)建target device 的方法;
2、 刪除target device 的方法;
3、 Target的映射IO請求的方法;
4、 Target結(jié)束IO請求的方法;
5、 暫停target device讀寫的方法;
6、 恢復(fù)target device讀寫的訪問;
7、 獲取當(dāng)前target device狀態(tài)的訪問;
8、 Target 處理用戶消息的方法;
用戶可以根據(jù)具體需求選擇性地實(shí)現(xiàn)上述方法,但一般最少要實(shí)現(xiàn)前3種方法,否則在device mapper下不能夠正常的工作。linear target driver就只實(shí)現(xiàn)了前3種方法和方法7,它完成邏輯地址空間到物理地址空間的線性映射,可以將多個物理設(shè)備以線性連接的方式組成一個邏輯設(shè)備,就如圖 4中描述的那樣,通過linear target driver將/dev/sda、/dev/sdb、/dev/sdc的三段連續(xù)空間組成了一個大的邏輯塊設(shè)備。Linear target的實(shí)現(xiàn)很簡單,它的創(chuàng)建和刪除方法主要完成申請和釋放描述linear target device所用結(jié)構(gòu)的內(nèi)存資源;IO映射處理方法的實(shí)現(xiàn)更是簡單,如下代碼所示:
static int linear_map(struct dm_target *ti, struct bio *bio, union map_info *map_context) { struct linear_c *lc = (struct linear_c *) ti->private; bio->bi_bdev = lc->dev->bdev; bio->bi_sector = lc->start + (bio->bi_sector - ti->begin); return 1; }
該映射方法就是將發(fā)送給邏輯設(shè)備mapped device的bio請求,根據(jù)映射關(guān)系以線性的方式重新定向到linear target device所表示物理設(shè)備的相應(yīng)位置,如代碼所示具體實(shí)現(xiàn)方法就是修改bio的bi_bdev設(shè)備指針為target device對應(yīng)的設(shè)備指針,并根據(jù)target device的起始地址和該bio請求在mapped device設(shè)備上的偏移值改變IO請求開始的扇區(qū)號bi_sector,從而完成IO請求的重定向。其他target driver的實(shí)現(xiàn)也都大同小異,按照device mapper所定義的接口規(guī)范,結(jié)合自己需要的功能進(jìn)行實(shí)現(xiàn)即可,這里就不一一介紹了,有興趣的讀者可以看內(nèi)核中具體的target driver代碼。
總結(jié)
Device Mapper是Linux操作系統(tǒng)中塊設(shè)備一級提供的一種主要映射機(jī)制,現(xiàn)在已被多數(shù)Linux下的邏輯卷管理器所采用。在該機(jī)制下,實(shí)現(xiàn)用戶自定義的存儲資源管理策略變得極其方便。理解device mapper所提供的映射機(jī)制,也是進(jìn)一步理解Linux下一些常見邏輯卷管理器實(shí)現(xiàn)的基礎(chǔ)。
Daniel P. Bovet, Marco Cesati. Understanding the Linux Kernel, 3rd Edition. O'Reilly, 2005
Jonathan Corbet, Alessandro Rubini, Greg Kroah-Hartman. Linux Device Driver, 3rd Edition. O'Reilly, 2005
Linux-2.6.15內(nèi)核源代碼
device-mapper.1.02.05代碼
http://sourceware.org/dm/
Heinz Mauelshagen dmraid - device-mapper RAID tool. Proceedings of the Linux Symposium 2005
E.Goggin, A.Kergon, C.Varoqui, &D.Olien. Linux Multipathing. Proceedings of the Linux Symposium 2005
?
評論
查看更多