日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

Linux块设备IO子系统

發(fā)布時(shí)間:2023/12/20 66 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux块设备IO子系统 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

? ?

塊設(shè)備是Linux三大設(shè)備之一,其驅(qū)動(dòng)模型主要針對(duì)磁盤,Flash等存儲(chǔ)類設(shè)備,塊設(shè)備(blockdevice)是一種具有一定結(jié)構(gòu)的隨機(jī)存取設(shè)備,對(duì)這種設(shè)備的讀寫是按(所以叫塊設(shè)備)進(jìn)行的,他使用緩沖區(qū)來(lái)存放暫時(shí)的數(shù)據(jù),待條件成熟后,從緩存一次性寫入設(shè)備或者從設(shè)備一次性讀到緩沖區(qū)。作為存儲(chǔ)設(shè)備,塊設(shè)備驅(qū)動(dòng)的核心問(wèn)題就是哪些page->segment->block->sector與哪些sector有數(shù)據(jù)交互,本文以3.14為藍(lán)本,探討內(nèi)核中的塊設(shè)備驅(qū)動(dòng)模型。

#框架

下圖是Linux中的塊設(shè)備模型示意圖,應(yīng)用層程序有兩種方式訪問(wèn)一個(gè)塊設(shè)備:

/dev和文件系統(tǒng)掛載點(diǎn),前者和字符設(shè)備一樣,通常用于配置,后者就是我們mount之后通過(guò)文件系統(tǒng)直接訪問(wèn)一個(gè)塊設(shè)備了。

  • read()系統(tǒng)調(diào)用最終會(huì)調(diào)用一個(gè)適當(dāng)?shù)腣FS函數(shù)(read()-->sys_read()-->vfs_read()),將文件描述符fd和文件內(nèi)的偏移量offset傳遞給它。

  • VFS會(huì)判斷這個(gè)SCI的處理方式,如果訪問(wèn)的內(nèi)容已經(jīng)被緩存在RAM中(磁盤高速緩存機(jī)制),就直接訪問(wèn),否則從磁盤中讀取

  • 為了從物理磁盤中讀取,內(nèi)核依賴映射層mapping layer,即上圖中的磁盤文件系統(tǒng)

  • 確定該文件所在文件系統(tǒng)的塊的大小,并根據(jù)文件塊的大小計(jì)算所請(qǐng)求數(shù)據(jù)的長(zhǎng)度。本質(zhì)上,文件被拆成很多塊,因此內(nèi)核需要確定請(qǐng)求數(shù)據(jù)所在的塊

  • 映射層調(diào)用一個(gè)具體的文件系統(tǒng)的函數(shù),這個(gè)層的函數(shù)會(huì)訪問(wèn)文件的磁盤節(jié)點(diǎn),然后根據(jù)邏輯塊號(hào)確定所請(qǐng)求數(shù)據(jù)在磁盤上的位置。

  • 內(nèi)核利用通用塊層(generic block layer)啟動(dòng)IO操作來(lái)傳達(dá)所請(qǐng)求的數(shù)據(jù),通常,一個(gè)IO操作只針對(duì)磁盤上一組連續(xù)的塊。

  • IO調(diào)度程序根據(jù)預(yù)先定義的內(nèi)核策略將待處理的IO進(jìn)行重排和合并

  • 塊設(shè)備驅(qū)動(dòng)程序向磁盤控制器硬件接口發(fā)送適當(dāng)?shù)闹噶?#xff0c;進(jìn)行實(shí)際的數(shù)據(jù)操作

  • #塊設(shè)備 VS 字符設(shè)備

    作為一種存儲(chǔ)設(shè)備,和字符設(shè)備相比,塊設(shè)備有以下幾種不同:

    字符設(shè)備塊設(shè)備
    1byte塊,硬件塊各有不同,但是內(nèi)核都使用512byte描述
    順序訪問(wèn)隨機(jī)訪問(wèn)
    沒有緩存,實(shí)時(shí)操作有緩存,不是實(shí)時(shí)操作
    一般提供接口給應(yīng)用層塊設(shè)備一般提供接口給文件系統(tǒng)
    是被用戶程序調(diào)用由文件系統(tǒng)程序調(diào)用

    此外,大多數(shù)情況下,磁盤控制器都是直接使用DMA方式進(jìn)行數(shù)據(jù)傳送。

    #IO調(diào)度

    就是電梯算法。我們知道,磁盤是的讀寫是通過(guò)機(jī)械性的移動(dòng)磁頭來(lái)實(shí)現(xiàn)讀寫的,理論上磁盤設(shè)備滿足塊設(shè)備的隨機(jī)讀寫的要求,但是出于節(jié)約磁盤,提高效率的考慮,我們希望當(dāng)磁頭處于某一個(gè)位置的時(shí)候,一起將最近需要寫在附近的數(shù)據(jù)寫入,而不是這寫一下。

    IO調(diào)度就是將上層發(fā)下來(lái)的IO請(qǐng)求的順序進(jìn)行重新排序以及對(duì)多個(gè)請(qǐng)求進(jìn)行合并,這樣就可以實(shí)現(xiàn)上述的提高效率、節(jié)約磁盤的目的。這種解決問(wèn)題的思路使用電梯算法,一個(gè)運(yùn)行中的電梯,一個(gè)人從20樓->1樓,另外一個(gè)人從15->5樓,電梯不會(huì)先將第一個(gè)人送到1樓再去15樓接第二個(gè)人將其送到5樓,而是從20樓下來(lái),到15樓的時(shí)候停下接人,到5樓將第二個(gè)放下,最后到達(dá)1樓。

    一句話,電梯算法最終服務(wù)的優(yōu)先順序并不按照按按鈕的先后順序

    Linux內(nèi)核中提供了下面的幾種電梯算法來(lái)實(shí)現(xiàn)IO調(diào)度:

    • No-op I/O scheduler只實(shí)現(xiàn)了簡(jiǎn)單的FIFO的,只進(jìn)行最簡(jiǎn)單的合并,比較適合基于Flash的存儲(chǔ)

    • Anticipatory I/O scheduler推遲IO請(qǐng)求(大約幾個(gè)微秒),以期能對(duì)他們進(jìn)行排序,獲得更高效率

    • Deadline I/O scheduler試圖把每次請(qǐng)求的延遲降到最低,同時(shí)也會(huì)對(duì)BIO重新排序,特別適用于讀取較多的場(chǎng)合,比如數(shù)據(jù)庫(kù)

    • CFQ I/O scheduler為系統(tǒng)內(nèi)所有的任務(wù)分配均勻的IO帶寬,提供一個(gè)公平的工作環(huán)境,在多媒體環(huán)境中,能保證音視頻及時(shí)從磁盤中讀取數(shù)據(jù),是當(dāng)前內(nèi)核默認(rèn)的調(diào)度器

    我們可以通過(guò)內(nèi)核傳參的方式指定使用的調(diào)度算法

    kernel elevator=deadline

    或者,使用如下命令改變內(nèi)核調(diào)度算法

    echo SCHEDULER > /sys/block/DEVICE/queue/scheduler

    #Page->Segment->Block->Sector ?VS ?Sector

    VS左面的是數(shù)據(jù)交互中的內(nèi)存部分。

    Page就是內(nèi)存映射的最小單位;
    Segment就是一個(gè)Page中我們要操作的一部分,由若干個(gè)相鄰的塊組成;
    Block是邏輯上的進(jìn)行數(shù)據(jù)存取的最小單位,是文件系統(tǒng)的抽象,邏輯塊的大小是在格式化的時(shí)候確定的, 一個(gè) Block 最多僅能容納一個(gè)文件(即不存在多個(gè)文件同一個(gè)block的情況)。如果一個(gè)文件比block小,他也會(huì)占用一個(gè)block,因而block中空余的空間會(huì)浪費(fèi)掉。

    而一個(gè)大文件,可以占多個(gè)甚至數(shù)十個(gè)成百上千萬(wàn)的block。Linux內(nèi)核要求 Block_Size = Sector_Size ?* (2的n次方),并且Block_Size <= 內(nèi)存的Page_Size(頁(yè)大小), 如ext2 fs的block缺省是4k。若block太大,則存取小文件時(shí),有空間浪費(fèi)的問(wèn)題;若block太小,則硬盤的 Block 數(shù)目會(huì)大增,而造成 inode 在指向 block 的時(shí)候的一些搜尋時(shí)間的增加,又會(huì)造成大文件讀寫方面的效率較差,block是VFS和文件系統(tǒng)傳送數(shù)據(jù)的基本單位。

    block對(duì)應(yīng)磁盤上的一個(gè)或多個(gè)相鄰的扇區(qū),而VFS將其看成是一個(gè)單一的數(shù)據(jù)單元,塊設(shè)備的block的大小不是唯一的,創(chuàng)建一個(gè)磁盤文件系統(tǒng)時(shí),管理員可以選擇合適的扇區(qū)的大小,同一個(gè)磁盤的幾個(gè)分區(qū)可以使用不同的塊大小。此外,對(duì)塊設(shè)備文件的每次讀或?qū)懖僮魇且环N"原始"訪問(wèn),因?yàn)樗@過(guò)了磁盤文件系統(tǒng),內(nèi)核通過(guò)使用最大的塊(4096)執(zhí)行該操作。Linux對(duì)內(nèi)存中的block會(huì)被進(jìn)一步劃分為Sector,Sector是硬件設(shè)備傳送數(shù)據(jù)的基本單位,這個(gè)Sector就是512byte,和物理設(shè)備上的概念不一樣,如果實(shí)際的設(shè)備的sector不是512byte,而是4096byte(eg SSD),那么只需要將多個(gè)內(nèi)核sector對(duì)應(yīng)一個(gè)設(shè)備sector即可

    VS右邊是物理上的概念,磁盤中一個(gè)Sector是512Byte,SSD中一個(gè)Sector是4K

    #核心數(shù)據(jù)結(jié)構(gòu)

    gendisk是一個(gè)物理磁盤或分區(qū)在內(nèi)核中的描述

    block_device_operations描述磁盤的操作方法集,block_device_operations之于gendisk,類似于file_operations之于cdev

    request_queue對(duì)象表示針對(duì)一個(gè)gendisk對(duì)象的所有請(qǐng)求的隊(duì)列,是相應(yīng)gendisk對(duì)象的一個(gè)域

    request表示經(jīng)過(guò)IO調(diào)度之后的針對(duì)一個(gè)gendisk(磁盤)的一個(gè)"請(qǐng)求",是request_queue的一個(gè)節(jié)點(diǎn)。多個(gè)request構(gòu)成了一個(gè)request_queue

    bio表示應(yīng)用程序?qū)σ粋€(gè)gendisk(磁盤)原始的訪問(wèn)請(qǐng)求,一個(gè)bio由多個(gè)bio_vec,多個(gè)bio經(jīng)過(guò)IO調(diào)度和合并之后可以形成一個(gè)request。

    bio_vec描述的應(yīng)用層準(zhǔn)備讀寫一個(gè)gendisk(磁盤)時(shí)需要使用的內(nèi)存頁(yè)page的一部分,即上文中的"段",多個(gè)bio_vec和bio_iter形成一個(gè)bio

    bvec_iter描述一個(gè)bio_vec中的一個(gè)sector信息

    #核心方法

    set_capacity()設(shè)置gendisk對(duì)應(yīng)的磁盤的物理參數(shù)

    blk_init_queue()分配+初始化+綁定一個(gè)有IO調(diào)度的gendisk的requst_queue,處理函數(shù)是void (request_fn_proc) (struct request_queue *q);類型

    blk_alloc_queue() 分配+初始化一個(gè)沒有IO調(diào)度的gendisk的request_queue,

    blk_queue_make_request()綁定處理函數(shù)到一個(gè)沒有IO調(diào)度的request_queue,處理函數(shù)函數(shù)是void (make_request_fn) (struct request_queue *q, struct bio *bio);類型

    __rq_for_each_bio()遍歷一個(gè)request中的所有的bio

    bio_for_each_segment()遍歷一個(gè)bio中所有的segment

    rq_for_each_segment()遍歷一個(gè)request中的所有的bio中的所有的segment
    最后三個(gè)遍歷算法都是用在request_queue綁定的處理函數(shù)中,這個(gè)函數(shù)負(fù)責(zé)對(duì)上層請(qǐng)求的處理。


    #gendisk結(jié)構(gòu)體

    同樣是面向?qū)ο蟮脑O(shè)計(jì)方法,Linux內(nèi)核使用gendisk對(duì)象描述一個(gè)系統(tǒng)的中的塊設(shè)備,類似于Windows系統(tǒng)中的磁盤分區(qū)和物理磁盤的關(guān)系,OS眼中的磁盤都是邏輯磁盤,也就是一個(gè)磁盤分區(qū),一個(gè)物理磁盤可以對(duì)應(yīng)多個(gè)磁盤分區(qū),在Linux中,這個(gè)gendisk就是用來(lái)描述一個(gè)邏輯磁盤,也就是一個(gè)磁盤分區(qū)。

    165 struct gendisk { 169 int major; /* major number of driver */ 170 int first_minor; 171 int minors; 174 char disk_name[DISK_NAME_LEN]; /* name of major driver */ 175 char *(*devnode)(struct gendisk *gd, umode_t *mode); 177 unsigned int events; /* supported events */ 178 unsigned int async_events; /* async events, subset of all */ 185 struct disk_part_tbl __rcu *part_tbl; 186 struct hd_struct part0; 188 const struct block_device_operations *fops; 189 struct request_queue *queue; 190 void *private_data; 192 int flags; 193 struct device *driverfs_dev; // FIXME: 194 struct kobject *slave_dir; 196 struct timer_rand_state *random; 197 atomic_t sync_io; /* RAID */ 198 struct disk_events *ev; 200 struct blk_integrity *integrity; 202 int node_id; 203 };

    struct gendisk解析

    --169-->驅(qū)動(dòng)的主設(shè)備號(hào)
    --170-->第一個(gè)次設(shè)備號(hào)
    --171-->次設(shè)備號(hào)的數(shù)量,即允許的最大分區(qū)的數(shù)量,1表示不允許分區(qū)
    --174-->設(shè)備名稱
    --185-->分區(qū)表數(shù)組首地址
    --186-->第一個(gè)分區(qū),相當(dāng)于part_tbl->part[0]
    --188-->操作方法集指針
    --189-->請(qǐng)求對(duì)象指針
    --190-->私有數(shù)據(jù)指針
    --193-->表示這是一個(gè)設(shè)備

    gendisk是一個(gè)動(dòng)態(tài)分配的結(jié)構(gòu)體,所以不要自己手動(dòng)來(lái)分配,而是使用內(nèi)核相應(yīng)的API來(lái)分配,其中會(huì)做一些初始化的工作

    struct?gendisk?*alloc_disk(int?minors); void?add_disk(struct?gendisk?*disk); void del_gendisk(struct gendisk *gp);

    上面幾個(gè)API是一個(gè)塊設(shè)備驅(qū)動(dòng)中必不可少的部分,下面的兩個(gè)主要是用來(lái)內(nèi)核對(duì)于設(shè)備管理用的,通常不要驅(qū)動(dòng)來(lái)實(shí)現(xiàn)

    struct kobject *get_disk(struct gendisk *disk);void put_disk(struct gendisk *disk);

    這兩個(gè)API最終回調(diào)用kobject *get_disk() 和kobject_put()來(lái)實(shí)現(xiàn)對(duì)設(shè)備的引用計(jì)數(shù)

    #block_device_operations結(jié)構(gòu)體

    和字符設(shè)備一樣,如果使用/dev接口訪問(wèn)塊設(shè)備,最終就會(huì)回調(diào)這個(gè)操作方法集的注冊(cè)函數(shù)

    1558 struct block_device_operations { 1559 int (*open) (struct block_device *, fmode_t); 1560 void (*release) (struct gendisk *, fmode_t); 1561 int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long); 1562 int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long); 1563 int (*direct_access) (struct block_device *, sector_t, 1564 void **, unsigned long *); 1565 unsigned int (*check_events) (struct gendisk *disk, 1566 unsigned int clearing); 1568 int (*media_changed) (struct gendisk *); 1569 void (*unlock_native_capacity) (struct gendisk *); 1570 int (*revalidate_disk) (struct gendisk *); 1571 int (*getgeo)(struct block_device *, struct hd_geometry *); 1573 void (*swap_slot_free_notify) (struct block_device *, unsigned long); 1574 struct module *owner; 1575 };

    struct block_device_operations

    --1559-->當(dāng)應(yīng)用層打開一個(gè)塊設(shè)備的時(shí)候被回調(diào)
    --1560-->當(dāng)應(yīng)用層關(guān)閉一個(gè)塊設(shè)備的時(shí)候被回調(diào)
    --1562-->相當(dāng)于file_operations里的compat_ioctl,不過(guò)塊設(shè)備的ioctl包含大量的標(biāo)準(zhǔn)操作,所以在這個(gè)接口實(shí)現(xiàn)的操作很少
    --1567-->在移動(dòng)塊設(shè)備中測(cè)試介質(zhì)是否改變的方法,已經(jīng)過(guò)時(shí),同樣的功能被check_event()實(shí)現(xiàn)
    --1571-->即get geometry,獲取驅(qū)動(dòng)器的幾何信息,獲取到的信息會(huì)被填充在一個(gè)hd_geometry結(jié)構(gòu)中
    --1574-->模塊所屬,通常填THIS_MODULE

    #request_queue結(jié)構(gòu)體

    每一個(gè)gendisk對(duì)象都有一個(gè)request_queue對(duì)象,前文說(shuō)過(guò),塊設(shè)備有兩種訪問(wèn)接口,一種是/dev下,一種是通過(guò)文件系統(tǒng),后者經(jīng)過(guò)IO調(diào)度在這個(gè)gendisk->request_queue上增加請(qǐng)求,最終回調(diào)與request_queue綁定的處理函數(shù),將這些請(qǐng)求向下變成具體的硬件操作

    294 struct request_queue {298 struct list_head queue_head;300 struct elevator_queue *elevator;472 }; struct request_queue

    --298-->請(qǐng)求隊(duì)列的鏈表頭
    --300-->請(qǐng)求隊(duì)列使用的IO調(diào)度算法, 通過(guò)內(nèi)核啟動(dòng)參數(shù)來(lái)選擇: kernel elevator=deadline
    request_queue_t和gendisk一樣需要使用內(nèi)核API來(lái)分配并初始化,里面大量的成員不要直接操作, 此外, 請(qǐng)求隊(duì)列如果要正常工作還需要綁定到一個(gè)處理函數(shù)中, 當(dāng)請(qǐng)求隊(duì)列不為空時(shí), 處理函數(shù)會(huì)被回調(diào), 這就是塊設(shè)備驅(qū)動(dòng)中處理請(qǐng)求的核心部分!

    從驅(qū)動(dòng)模型的角度來(lái)說(shuō), 塊設(shè)備主要分為兩類需要IO調(diào)度的和不需要IO調(diào)度的, 前者包括磁盤, 光盤等, 后者包括Flash, SD卡等, 為了保證模型的統(tǒng)一性 , Linux中對(duì)這兩種使用同樣的模型但是通過(guò)不同的API來(lái)完成上述的初始化綁定

    #有IO調(diào)度類設(shè)備API

    struct request_queue *blk_init_queue(request_fn_proc *rfn, spinlock _t *lock)

    #無(wú)IO調(diào)度類設(shè)備API

    struct?request_queue?*blk_alloc_queue(gfp_t?gfp_mask)void blk_queue_make_request(struct request_queue *q, make_request_ fn *mfn)

    #共用API

    針對(duì)請(qǐng)求隊(duì)列的操作是塊設(shè)備的一個(gè)核心任務(wù), 其實(shí)質(zhì)就是對(duì)請(qǐng)求隊(duì)列操作函數(shù)的編寫, 這個(gè)函數(shù)的主要功能就是從請(qǐng)求隊(duì)列中獲取請(qǐng)求并根據(jù)請(qǐng)求進(jìn)行相應(yīng)的操作 內(nèi)核中已經(jīng)提供了大量的API供該函數(shù)使用

    void?blk_cleanup_queue(struct?request_queue?*q) blkdev_dequeue_request() struct?request?*blk_fetch_request(struct?request_queue?*q) struct?request?*blk_peek_request(struct?request_queue?*q) void blk_stop_queue(struct request_queue *q) void blk_start_queue(struct request_queue *q)

    #request

    97 struct request {98 struct list_head queuelist;104 struct request_queue *q;117 struct bio *bio;118 struct bio *biotail;119120 struct hlist_node hash;126 union {127 struct rb_node rb_node;128 void *completion_data;129 };137 union {138 struct {139 struct io_cq *icq;140 void *priv[2];141 } elv;142143 struct {144 unsigned int seq;145 struct list_head list;146 rq_end_io_fn *saved_end_io;147 } flush;148 };149150 struct gendisk *rq_disk;151 struct hd_struct *part;199 };

    struct request
    --98-->將這個(gè)request掛接到鏈表的節(jié)點(diǎn)
    --104-->這個(gè)request從屬的request_queue
    --117-->組成這個(gè)request的bio鏈表的頭指針
    --118-->組成這個(gè)request的bio鏈表的尾指針
    --120-->內(nèi)核hash表頭指針

    #bio

    bio用來(lái)描述單一的I/O請(qǐng)求,它記錄了一次I/O操作所必需的相關(guān)信息,如用于I/O操作的數(shù)據(jù)緩存位置,,I/O操作的塊設(shè)備起始扇區(qū),是讀操作還是寫操作等等

    46 struct bio {47 struct bio *bi_next;48 struct block_device *bi_bdev;49 unsigned long bi_flags;50 unsigned long bi_rw;54 struct bvec_iter bi_iter;59 unsigned int bi_phys_segments;65 unsigned int bi_seg_front_size;66 unsigned int bi_seg_back_size;68 atomic_t bi_remaining;70 bio_end_io_t *bi_end_io;72 void *bi_private;85 unsigned short bi_vcnt;91 unsigned short bi_max_vecs; 104 struct bio_vec bi_inline_vecs[0]; 105 }; struct bio

    --47-->指向鏈表中下一個(gè)bio的指針bi_next
    --50-->bi_rw低位表示讀寫READ/WRITE, 高位表示優(yōu)先級(jí)
    --90-->bio對(duì)象包含bio_vec對(duì)象的數(shù)目
    --91-->這個(gè)bio能承載的最大的io_vec的數(shù)目
    --95-->該bio描述的第一個(gè)io_vec
    --104-->表示這個(gè)bio包含的bio_vec變量的數(shù)組,即這個(gè)bio對(duì)應(yīng)的某一個(gè)page中的一"段"內(nèi)存

    #bio_vec

    描述指定page中的一塊連續(xù)的區(qū)域,在bio中描述的就是一個(gè)page中的一個(gè)"段"(segment)

    25 struct bio_vec {26 struct page *bv_page;27 unsigned int bv_len;28 unsigned int bv_offset;29 };

    struct bio_vec
    --26-->描述的page
    --27-->描述的長(zhǎng)度
    --28-->描述的起始地址偏移量

    #bio_iter

    用于記錄當(dāng)前bvec被處理的情況,用于遍歷bio

    31 struct bvec_iter {32 sector_t bi_sector;34 unsigned int bi_size;3536 unsigned int bi_idx;40 };

    #__rq_for_each_bio()

    遍歷一個(gè)request中的每一個(gè)bio

    ?739?????????if?((rq->bio))??????????????????\740?????????????????for?(_bio?=?(rq)->bio;?_bio;?_bio?=?_bio->bi_next)


    #bio_for_each_segment()

    遍歷一個(gè)bio中的每一個(gè)segment

    242 #define bio_for_each_segment(bvl, bio, iter) \ 243 __bio_for_each_segment(bvl, bio, iter, (bio)->bi_iter)


    #rq_for_each_segment()

    遍歷一個(gè)request中的每一個(gè)segment

    742 #define rq_for_each_segment(bvl, _rq, _iter) \743 __rq_for_each_bio(_iter.bio, _rq) \744 bio_for_each_segment(bvl, _iter.bio, _iter.iter)

    遍歷request_queue,綁定函數(shù)的一個(gè)必要的工作就是將request_queue中的數(shù)據(jù)取出, 所以遍歷是必不可少的, 針對(duì)有IO調(diào)度的設(shè)備, 我們需要從中提取請(qǐng)求再繼續(xù)操作, 對(duì)于沒有IO調(diào)度的設(shè)備, 我們可以直接從request_queue中提取bio進(jìn)行操作, 這兩種處理函數(shù)的接口就不一樣,下面的例子是對(duì)LDD3中的代碼進(jìn)行了修剪而來(lái)的,相應(yīng)的API使用的是3.14版本,可以看出這兩種模式的使用方法的不同。

    sbull_init
    ?? ?? ?? ??└── setup_device
    ?? ?? ?? ?? ?? ?? ?? ?? ├──sbull_make_request
    ?? ?? ?? ?? ?? ?? ?? ?? │?? ?? ?? ?? ├──sbull_xfer_bio
    ?? ?? ?? ?? ?? ?? ?? ?? │?? ?? ?? ?? └──sbull_transfer
    ?? ?? ?? ?? ?? ?? ?? ?? └──sbull_full_request
    ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ├──blk_fetch_request
    ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? └──sbull_xfer_request
    ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??├── __rq_for_each_bio
    ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??└── sbull_xfer_bio
    ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? └──sbull_transfer

    static void sbull_transfer(struct sbull_dev *dev, unsigned long sector,unsigned long nsect, char *buffer, int write) {unsigned long offset = sector*KERNEL_SECTOR_SIZE;unsigned long nbytes = nsect*KERNEL_SECTOR_SIZE;if (write)memcpy(dev->data + offset, buffer, nbytes);elsememcpy(buffer, dev->data + offset, nbytes); }static int sbull_xfer_bio(struct sbull_dev *dev, struct bio *bio) {struct bvec_iter i;struct bio_vec bvec;sector_t sector = bio->bi_iter.bi_sector;bio_for_each_segment(bvec, bio, i) {char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);sbull_transfer(dev, sector, bio_cur_bytes(bio)>>9 ,buffer, bio_data_dir(bio) == WRITE);sector += bio_cur_bytes(bio)>>9;__bio_kunmap_atomic(bio, KM_USER0);}return 0; }static int sbull_xfer_request(struct sbull_dev *dev, struct request *req) {struct bio *bio;int nsect = 0;__rq_for_each_bio(bio, req) {sbull_xfer_bio(dev, bio);nsect += bio->bi_size/KERNEL_SECTOR_SIZE;}return nsect; }static void sbull_full_request(struct request_queue *q) {struct request *req;int nsect;struct sbull_dev *dev ;int i = 0;while ((req = blk_fetch_request(q)) != NULL) {dev = req->rq_disk->private_data;nsect = sbull_xfer_request(dev, req);__blk_end_request(req, 0, (nsect<<9));printk ("i = %d\n", ++i);} }static void sbull_make_request(struct request_queue *q, struct bio *bio) {struct sbull_dev *dev = q->queuedata;int status;status = sbull_xfer_bio(dev, bio);bio_endio(bio, status);return; }static struct block_device_operations sbull_ops = {.owner = THIS_MODULE,.open = sbull_open,.release= sbull_release,.getgeo = sbull_getgeo, };static void setup_device(struct sbull_dev *dev, int which) {memset (dev, 0, sizeof (struct sbull_dev));dev->size = nsectors * hardsect_size;dev->data = vmalloc(dev->size);switch (request_mode) {case RM_NOQUEUE:dev->queue = blk_alloc_queue(GFP_KERNEL);blk_queue_make_request(dev->queue, sbull_make_request);break;case RM_FULL:dev->queue = blk_init_queue(sbull_full_request, &dev->lock);break;}dev->queue->queuedata = dev;dev->gd = alloc_disk(SBULL_MINORS);dev->gd->major = sbull_major;dev->gd->first_minor = which*SBULL_MINORS;dev->gd->fops = &sbull_ops;dev->gd->queue = dev->queue;dev->gd->private_data = dev;snprintf (dev->gd->disk_name, 32, "sbull%c", which + 'a');set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));add_disk(dev->gd);return; }static int __init sbull_init(void) {int i;sbull_major = register_blkdev(sbull_major, "sbull");Devices = (struct sbull_dev *)kmalloc(ndevices*sizeof (struct sbull_dev), GFP_KERNEL);for (i = 0; i < ndevices; i++)setup_device(Devices + i, i);return 0; }

    ? 回復(fù)「?籃球的大肚子」進(jìn)入技術(shù)群聊

    回復(fù)「1024」獲取1000G學(xué)習(xí)資料

    總結(jié)

    以上是生活随笔為你收集整理的Linux块设备IO子系统的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。