日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

十七、块设备驱动

發(fā)布時間:2025/4/16 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 十七、块设备驱动 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

在讀者學習本章以及后續(xù)塊設備相關章節(jié)之前,最好了解塊設備讀寫過程和操作,可以參考:Nor Flash裸機操作。

?

?

一、塊設備概念和讀寫過程

塊設備:

塊設備是I/O設備中的一類,是將信息存儲在固定大小的塊中,每個塊都有自己的地址,還可以在設備的任意位置讀取一定長度的數(shù)據(jù)。如硬盤,U盤,SD卡等。

?

塊設備組成:

段(Segments):由若干個塊組成,是Linux內存管理機制中一個內存頁或者內存頁的一部分。

塊(Blocks):由一個或多個扇區(qū)組成,塊是內核對文件系統(tǒng)的一種抽象,也就是說內核執(zhí)行的所有磁盤操作都是以塊為基本單位的。

扇區(qū)(Sectors):塊設備的基本單位。

扇區(qū)是硬件設備傳輸數(shù)據(jù)的最小單位,而塊是操作系統(tǒng)傳輸數(shù)據(jù)的最小單位。

?

以上圖為例,若我們依次在此Flash的扇區(qū)1和扇區(qū)2上寫數(shù)據(jù),正常的操作步驟一般是:

1. 把扇區(qū)1的數(shù)據(jù)放入緩沖區(qū)中

2. 修改緩沖區(qū)數(shù)據(jù)

3. 擦除整塊

4. 燒寫整塊

寫扇區(qū)2時重復上述操作。

?

但是上述操作讀取、修改、燒寫都進行了兩次,浪費時間。因此內核提供了一個隊列機制,它會將讀寫請求進行優(yōu)化、排序和合并等操作,從而提高訪問硬盤的效率。

若優(yōu)化上述過程,可將操作步驟變更為:

1. 接到兩個寫操作,將其放入隊列

2. 對兩個寫操作進行合并,變成一個寫操作

3. 把扇區(qū)1和扇區(qū)2的數(shù)據(jù)放入緩沖區(qū)中

4. 修改緩沖區(qū)數(shù)據(jù)

5. 擦除整塊

6. 燒寫整塊

?

?

二、塊設備框架分析

當我們對一個文本文件寫入(write)或讀取(read)數(shù)據(jù)時,文件系統(tǒng)會轉換為對塊設備上扇區(qū)的訪問,它會調用ll_rw_block()函數(shù)實現(xiàn)對扇區(qū)的讀寫。

1 void ll_rw_block(int rw, int nr, struct buffer_head *bhs[]) 2 { 3 int i; 4 5 for (i = 0; i < nr; i++) { /* nr表示buffer_head數(shù)組個數(shù) */ 6 struct buffer_head *bh = bhs[i]; 7 8 if (!trylock_buffer(bh)) 9 continue; 10 if (rw == WRITE) { 11 if (test_clear_buffer_dirty(bh)) { 12 bh->b_end_io = end_buffer_write_sync; 13 get_bh(bh); 14 submit_bh(WRITE, bh); /* 提交寫標志的buffer_head */ 15 continue; 16 } 17 } else { 18 if (!buffer_uptodate(bh)) { 19 bh->b_end_io = end_buffer_read_sync; 20 get_bh(bh); 21 submit_bh(rw, bh); /* 提交其它標志的buffer_head */ 22 continue; 23 } 24 } 25 unlock_buffer(bh); 26 } 27 }

其中,buffer_head用于存儲I/O操作數(shù)據(jù)和信息。

struct buffer_head {unsigned long b_state; /* 緩沖區(qū)狀態(tài)標志 */struct buffer_head *b_this_page; /* 當前頁面緩沖區(qū) */struct page *b_page; /* 緩沖區(qū)所位于的頁面 */sector_t b_blocknr; /* 起始的塊號 */size_t b_size; /* 塊的大小 */char *b_data; /* 頁面的緩沖數(shù)據(jù) */struct block_device *b_bdev; /* 塊設備 */bh_end_io_t *b_end_io; /* I/O completion */void *b_private; /* reserved for b_end_io */struct list_head b_assoc_buffers; /* associated with another mapping */struct address_space *b_assoc_map; /* mapping this buffer isassociated with */atomic_t b_count; /* users using this buffer_head */ }; View Code

?

接下來,我們來分析submit_bio(rw, bio)函數(shù)的調用過程。

submit_bh(rw, bh);-> bio = bio_alloc(GFP_NOIO, 1); /* 分配并設置struct bio */-> submit_bio(rw, bio);-> generic_make_request(bio); /* 構造請求隊列描述符 */-> __generic_make_request(bio);-> struct request_queue *q = bdev_get_queue(bio->bi_bdev);-> q->make_request_fn(q, bio); /* 在blk_init_allocated_queue_node()函數(shù)中初始化為__make_request()函數(shù) */-> __make_request(q, bio);-> elv_merge(q, &req, bio); /* 電梯算法融合,原理與我們所乘坐電梯載客方式相同 */-> init_request_from_bio(req, bio); /* 若無法合成,則使用bio構造I/O請求 */-> add_acct_request(q, req, where); /* 將請求加入隊列 */-> __blk_run_queue(q); /* 執(zhí)行處理隊列 */-> q->request_fn(q); /* 執(zhí)行處理函數(shù),在blk_init_allocated_queue_node()函數(shù)中初始化為傳入?yún)?shù)rfn */

其中,

1. 一般一個struct bio對應一個I/O請求。一個bio由多個bio_vec組成。兩結構體定義如下:

1 struct bio_vec { 2 struct page *bv_page; /* 頁指針 */ 3 unsigned int bv_len; /* 傳輸?shù)淖止?jié)數(shù) */ 4 unsigned int bv_offset; /* 偏移位置 */ 5 }; 6 7 struct bio { 8 sector_t bi_sector; /* 要傳輸?shù)牡谝粋€扇區(qū) */ 9 struct bio *bi_next; /* 下一個bio */ 10 struct block_device *bi_bdev; 11 unsigned long bi_flags; /* 狀態(tài)、命令等 */ 12 unsigned long bi_rw; /* 低位表示寫或讀,高位表示優(yōu)先級 */ 13 14 unsigned short bi_vcnt; /* bio_vec的數(shù)量 */ 15 unsigned short bi_idx; /* 當前bvl_vec索引 */ 16 17 unsigned int bi_phys_segments; 18 19 unsigned int bi_size; /* 剩余I/O請求個數(shù) */ 20 21 unsigned int bi_seg_front_size; 22 unsigned int bi_seg_back_size; 23 unsigned int bi_max_vecs; /* max bvl_vecs we can hold */ 24 unsigned int bi_comp_cpu; /* completion CPU */ 25 atomic_t bi_cnt; /* pin count */ 26 struct bio_vec *bi_io_vec; /* the actual vec list */ 27 bio_end_io_t *bi_end_io; 28 void *bi_private; 29 ... 30 bio_destructor_t *bi_destructor; /* destructor */ 31 struct bio_vec bi_inline_vecs[0]; 32 }; View Code?

2. struct request_queue *q即為第一節(jié)優(yōu)化中提到的隊列。

3. blk_init_allocated_queue_node()函數(shù)被頂層的blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)和blk_alloc_queue(gfp_t gfp_mask)函數(shù)調用,因此我們在編寫驅動程序時需要調用兩函數(shù)初始化等待隊列。

4. 電梯算法的原理與我們所乘坐電梯載客方式相同,比如電梯在1層處于上升狀態(tài),2、4、5樓層的人要上樓,3、6、7樓層的人要下樓,那么電梯上升過程中并不會在3、6、7層停留,因此他可以一個來回完成所有載客任務。

?

調用關系如下圖:

圖中的I/O調度器會對bio進行調度處理,bio結構或被合并到請求隊列的一個請求結構的request中。最后調用blk_init_queue()的request_fn_proc()將數(shù)據(jù)讀取或寫入塊設備。I/O調用器所使用的調度方法就是電梯算法。

另一方面,blk_alloc_queue()并不會啟動I/O調度器,bio的流程完全由我們控制。

?

struct request_queue定義如下:

struct request_queue {/** Together with queue_head for cacheline sharing*/struct list_head queue_head;struct request *last_merge;struct elevator_queue *elevator;/** the queue request freelist, one for reads and one for writes*/struct request_list rq;request_fn_proc *request_fn;make_request_fn *make_request_fn;prep_rq_fn *prep_rq_fn;unprep_rq_fn *unprep_rq_fn;merge_bvec_fn *merge_bvec_fn;softirq_done_fn *softirq_done_fn;rq_timed_out_fn *rq_timed_out_fn;dma_drain_needed_fn *dma_drain_needed;lld_busy_fn *lld_busy_fn;/** Dispatch queue sorting*/sector_t end_sector;struct request *boundary_rq;/** Delayed queue handling*/struct delayed_work delay_work;struct backing_dev_info backing_dev_info;void *queuedata;gfp_t bounce_gfp;unsigned long queue_flags;spinlock_t __queue_lock;spinlock_t *queue_lock;struct kobject kobj;/** queue settings*/unsigned long nr_requests; /* Max # of requests */unsigned int nr_congestion_on;unsigned int nr_congestion_off;unsigned int nr_batching;void *dma_drain_buffer;unsigned int dma_drain_size;unsigned int dma_pad_mask;unsigned int dma_alignment;struct blk_queue_tag *queue_tags;struct list_head tag_busy_list;unsigned int nr_sorted;unsigned int in_flight[2];unsigned int rq_timeout;struct timer_list timeout;struct list_head timeout_list;struct queue_limits limits; ... }; View Code

根據(jù)上述分析可以確定struct request_queue用于實現(xiàn)對塊設備的合并操作。

根據(jù)分離原則,必然會有結構體用于表示塊設備的屬性,此結構體為struct gendisk。

struct gendisk {int major; /* 主設備號,使用register_blkdev()申請 */int first_minor; /* 起始次設備號 */int minors; /* 有多少個次設備號,也就是多少個分區(qū),若minors為1,表示此塊設備沒有分區(qū) */char disk_name[DISK_NAME_LEN]; /* 塊設備名字 */char *(*devnode)(struct gendisk *gd, mode_t *mode);unsigned int events; /* supported events */unsigned int async_events; /* async events, subset of all */struct disk_part_tbl __rcu *part_tbl;struct hd_struct part0; /* 分區(qū)表信息 */const struct block_device_operations *fops; /* 塊設備操作函數(shù) */struct request_queue *queue; /* 隊列 */void *private_data; ... };

其中,

1. 分區(qū)信息struct hd_struct定義如下:

struct hd_struct {sector_t start_sect; /* 起始扇區(qū),typedef unsigned long sector_t; */sector_t nr_sects; /* 扇區(qū)大小 */sector_t alignment_offset;unsigned int discard_alignment;struct device __dev;struct kobject *holder_dir;int policy, partno;struct partition_meta_info *info; ... };

此結構體中最重要的信息就是分區(qū)的起始扇區(qū)號和分區(qū)的大小。分區(qū)大小可以使用set_capacity()函數(shù)設置。

2. 塊設備底層操作函數(shù)結構體struct block_device_operations定義如下:

1 struct block_device_operations { 2 int (*open) (struct block_device *, fmode_t); 3 int (*release) (struct gendisk *, fmode_t); 4 int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long); 5 int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long); 6 int (*direct_access) (struct block_device *, sector_t, 7 void **, unsigned long *); 8 unsigned int (*check_events) (struct gendisk *disk, 9 unsigned int clearing); 10 /* ->media_changed() is DEPRECATED, use ->check_events() instead */ 11 int (*media_changed) (struct gendisk *); 12 void (*unlock_native_capacity) (struct gendisk *); 13 int (*revalidate_disk) (struct gendisk *); 14 int (*getgeo)(struct block_device *, struct hd_geometry *); 15 /* this callback is with swap_lock and sometimes page table lock held */ 16 void (*swap_slot_free_notify) (struct block_device *, unsigned long); 17 struct module *owner; 18 }; View Code

?

?

三、塊設備驅動編寫步驟

在初始化函數(shù)中:

1. 使用register_blkdev()創(chuàng)建塊設備,創(chuàng)建方式與字符設備類似(可省略)

2. 使用blk_init_queue()不使用等待隊列 ?或 ?使用blk_alloc_queue()使用等待隊列

3. 若使用blk_alloc_queue(),則需要使用blk_queue_make_request()函數(shù)綁定請求隊列函數(shù)

4. 使用alloc_disk()分配struct gendisk

5. 設置struct gendisk成員

6. 使用add_disk()注冊struct gendisk

在隊列函數(shù)中:

1. 使用bio_for_each_segment(bvl, bio, i)遍歷每一個bio_vec

2. 使用bio_rw(bio)獲取每個申請的讀寫標志,READ表示讀,WRITE表示寫

3. 使用memcpy()讀寫扇區(qū)或緩沖區(qū)

4. 使用bio_endio()結束bio處理

在注銷函數(shù)中:

1. 使用put_disk()和del_gendisk()刪除對gendisk設備的引用并刪除設備

2. 使用blk_cleanup_queue()清除隊列

3. 使用unregister_blkdev()注銷塊設備(可省略)

?

步驟中所使用函數(shù)聲明如下:

/* 1. 初始化函數(shù) */ int register_blkdev(unsigned int major, const char *name) struct request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock) /* 不使用請求隊列 */ typedef void (request_fn_proc)(struct reqest_queue *q) /* blk_init_queue()對應的處理函數(shù) */struct request_queue *blk_alloc_queue(gfp_t gfp_mask) /* 使用請求隊列,與上面函數(shù)二選一 */ void blk_queue_make_request(struct request_queue * q, make_request_fn * mfn) /* 綁定請求隊列函數(shù),與blk_alloc_queue()配合使用 */ typedef int (make_request_fn)(struct request_queue *q,struct bio *bio) /* blk_queue_make_request()對應的處理函數(shù) */struct gendisk *alloc_disk(int minors) void add_disk(struct gendisk *disk)/* 2. 隊列函數(shù) */ #define bio_for_each_segment(bvl, bio, i) #define bio_rw(bio) ((bio)->bi_rw & (RW_MASK | RWA_MASK)) void *memcpy(void *destin, void *source, unsigned n); void bio_endio(struct bio *bio, int error) /* error=0表示讀寫操作正常,其它表示錯誤 *//* 3. 注銷函數(shù) */ void put_disk(struct gendisk *disk); /* 刪除對gendisk設備的引用 */ void del_gendisk(struct gendisk *disk); /* 刪除gendisk設備 */ void blk_cleanup_queue(request_queue_t *q); unregister_blkdev(unsigned int major, const char *name);

其中,add_disk()函數(shù)需要我們進一步分析。

void add_disk(struct gendisk *disk)-> blk_alloc_devt(&disk->part0, &devt);/* 獲取第一個設備號的地址 */-> *devt = MKDEV(disk->major, disk->first_minor + part->partno);-> disk->major = MAJOR(devt);-> disk->first_minor = MINOR(devt);-> register_disk(disk); /* 將塊設備注冊進入文件系統(tǒng) */-> blk_register_queue(disk); /* 把隊列注冊進入系統(tǒng) */

?

有了以上基礎,下面我們來實現(xiàn)通過內存來模擬塊設備驅動。

內存來模擬塊設備驅動源代碼:

1 #include <linux/module.h> 2 #include <linux/errno.h> 3 #include <linux/interrupt.h> 4 #include <linux/mm.h> 5 #include <linux/fs.h> 6 #include <linux/kernel.h> 7 #include <linux/timer.h> 8 #include <linux/genhd.h> 9 #include <linux/hdreg.h> 10 #include <linux/ioport.h> 11 #include <linux/init.h> 12 #include <linux/wait.h> 13 #include <linux/blkdev.h> 14 #include <linux/blkpg.h> 15 #include <linux/delay.h> 16 #include <linux/io.h> 17 18 #include <asm/system.h> 19 #include <asm/uaccess.h> 20 #include <asm/dma.h> 21 22 23 #define BLOCKSIZE (1024*1024) 24 #define SECTORSIZE (512) 25 26 static struct gendisk *ramdisk; 27 static struct request_queue *queue; 28 static int major; 29 30 unsigned char *blkdev_data[BLOCKSIZE]; /* 1M內存空間 */ 31 32 static struct block_device_operations ramblock_fops = { 33 .owner = THIS_MODULE, 34 }; 35 36 static int ramdisk_request(struct request_queue *q,struct bio *bio) 37 { 38 struct bio_vec *bvec; 39 int i; 40 void *src_mem; /* 物理塊設備存儲空間 */ 41 42 if ((bio->bi_sector << 9) + bio->bi_size > BLOCKSIZE) { 43 bio_endio(bio, -EIO); 44 return 0; 45 } 46 47 src_mem = blkdev_data + (bio->bi_sector << 9); /* 要讀寫的塊設備位置,<< 9 為 * 512 */ 48 49 bio_for_each_segment(bvec, bio, i) { /* 對每一個bio_vec進行操作 */ 50 void *iovec_mem; /* 請求對應的內存 */ 51 switch (bio_rw(bio)) { 52 case READ: 53 case READA: 54 iovec_mem = kmap(bvec->bv_page) + bvec->bv_offset; 55 memcpy(iovec_mem, src_mem, bvec->bv_len); 56 kunmap(bvec->bv_page); 57 break; 58 case WRITE: 59 iovec_mem = kmap(bvec->bv_page) + bvec->bv_offset; 60 memcpy(src_mem, iovec_mem, bvec->bv_len); 61 kunmap(bvec->bv_page); 62 break; 63 default: 64 printk("unknown value of bio_rw: %lu\n", bio_rw(bio)); 65 bio_endio(bio, -EIO); 66 return 0; 67 } 68 src_mem += bvec->bv_len; 69 } 70 bio_endio(bio, 0); 71 72 return 0; 73 } 74 75 static int __init ramdisk_init(void) 76 { 77 major = register_blkdev(0, "ramdisk"); 78 79 queue = blk_alloc_queue(GFP_KERNEL); 80 blk_queue_make_request(queue, ramdisk_request); 81 ramdisk = alloc_disk(1); /* 該磁盤最多一個分區(qū) */ 82 ramdisk->major = major; 83 ramdisk->first_minor = 0; 84 ramdisk->queue = queue; 85 ramdisk->fops = &ramblock_fops; 86 strcpy(ramdisk->disk_name, "ramdisk"); 87 set_capacity(ramdisk, BLOCKSIZE/SECTORSIZE); /* 扇區(qū)數(shù) */ 88 89 add_disk(ramdisk); 90 91 return 0; 92 } 93 94 static void __exit ramdisk_exit(void) 95 { 96 put_disk(ramdisk); 97 del_gendisk(ramdisk); 98 blk_cleanup_queue(queue); 99 unregister_blkdev(major, "ramblock"); 100 } 101 102 /* 聲明段屬性 */ 103 module_init(ramdisk_init); 104 module_exit(ramdisk_exit); 105 106 MODULE_LICENSE("GPL"); View Code

Makefile:

1 KERN_DIR = /work/itop4412/tools/linux-3.5 2 3 all: 4 make -C $(KERN_DIR) M=`pwd` modules 5 6 clean: 7 make -C $(KERN_DIR) M=`pwd` modules clean 8 rm -rf modules.order 9 10 obj-m += ramdisk.o View Code

?

測試:

在編譯并在開發(fā)板上insmod后執(zhí)行:

# mkdosfs /dev/ramdisk    /* 格式化 */

# mount /dev/ramdisk /tmp   /* 掛接 */

# vi /tmp/a.txt        /* 創(chuàng)建文件 */

# unmount /tmp        /* 重新掛接 */

# mount /dev/ramdisk /tmp

# ls /tmp           /* 查看文件是否仍舊存在 */

?

?

下一章 ?十八、Nand Flash驅動和Nor Flash驅動

?

轉載于:https://www.cnblogs.com/Lioker/p/11248902.html

總結

以上是生活随笔為你收集整理的十七、块设备驱动的全部內容,希望文章能夠幫你解決所遇到的問題。

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

主站蜘蛛池模板: sm在线看 | 久久精品香蕉视频 | 欧美日韩精品一区二区三区四区 | 91精品国产乱码在线观看 | 欧美性福利 | 欧美激情一区二区三区蜜桃视频 | 日韩女优在线观看 | 亚洲少妇激情 | 日韩在线视频看看 | 人人草超碰 | 97爱爱| 苏晴忘穿内裤坐公交车被揉到视频 | 男女羞羞的视频 | 欧美区国产区 | 一区二区三区日韩在线 | 欧美偷拍一区二区三区 | 人人人妻人人澡人人爽欧美一区 | 成年在线观看视频 | 天天澡天天狠天天天做 | 久久特级毛片 | 什么网站可以看毛片 | 91激情视频在线 | 懂色av一区二区三区四区五区 | 一久久 | 久草视频在线观 | 他揉捏她两乳不停呻吟动态图 | 国产又粗又猛又爽又黄的视频在线观看动漫 | 超碰在线进入 | 免费在线黄网站 | 欧美日韩中文字幕一区二区 | 日本成人在线免费 | 亚洲黄色在线观看视频 | 亚洲A∨无码国产精品 | 国产免费中文字幕 | 在线视频国产一区 | 色桃视频| 超碰免费在 | 69精品一区二区三区 | 欧美第一夜 | 屁屁影院第一页 | 亚洲第一二三区 | 久久观看最新视频 | 老子影院午夜精品无码 | 日本黄色免费 | 日韩伦理视频 | 久久一区国产 | 国产亚洲欧美日韩精品 | 91福利在线观看 | 夜夜综合| 免费看成人毛片 | 无码人妻丰满熟妇区毛片蜜桃精品 | 在线播放小视频 | 欧美在线aa| 亚洲国产精品毛片 | 精品视频| 在线播放日韩av | 日本高清精品 | 超在线视频 | 国产福利不卡 | 日本公与丰满熄 | 三大队在线观看 | 都市激情校园春色亚洲 | 茄子av在线| 丰满白嫩尤物一区二区 | 91天天操 | 影音先锋成人资源 | www.亚洲免费 | 午夜精品久久久久久久99黑人 | 亚洲最大成人综合网 | 亚洲天堂影院在线观看 | 天天操夜夜操狠狠操 | 黄色网页观看 | 伊人久久五月 | 大乳巨大乳j奶hd | 潘金莲激情呻吟欲求不满视频 | 特级淫片裸体免费看 | 992av| 免费乱淫视频 | 日本一区二区三区免费看 | 亚洲欧美日韩精品色xxx | 欧美大片xxxx| 成人小视频免费看 | 国产精品一区二区视频 | 国产熟女一区二区三区四区 | 正在播放欧美 | 一区二区三区不卡在线观看 | 中文字幕日韩欧美一区二区 | 免费久久| 中文字幕av亚洲精品一部二部 | 红桃成人在线 | 亚洲一区精品视频在线观看 | 亚洲男人的天堂av | 三级黄片毛片 | 动漫美女视频 | 久久亚洲国产 | 日本色视| 日日日日干 | 国产av人人夜夜澡人人爽麻豆 | 日本中文字幕一区二区 |