Linux块设备驱动详解
 
  
 
<基本概念>
 
| 1 struct bio { 2 sector_t bi_sector; /* 要传输的第一个扇区 */ 3 struct bio *bi_next; /* 下一个 bio */ 4 struct block_device*bi_bdev; 5 unsigned long bi_flags; /* 状态、命令等 */ 6 unsigned long bi_rw; /* 低位表示 READ/WRITE,高位表示优先级*/ 7 8 unsigned short bi_vcnt; /* bio_vec 数量 */ 9 unsigned short bi_idx; /* 当前 bvl_vec 索引 */ 10 11 /* 执行物理地址合并后 sgement 的数目 */ 12 unsigned short bi_phys_segments; 13 14 unsigned int bi_size; 15 16 /* 为了明了最大的 segment 尺寸,我们考虑这个 bio 中第一个和最后一个 17 可合并的 segment 的尺寸 */ 18 unsigned int bi_hw_front_size; 19 unsigned int bi_hw_back_size; 20 21 unsigned int bi_max_vecs; /* 我们能持有的最大 bvl_vecs 数 */ 22 unsigned int bi_comp_cpu; /* completion CPU */ 23 24 struct bio_vec *bi_io_vec; /* 实际的 vec 列表 */ 25 26 bio_end_io_t *bi_end_io; 27 atomic_t bi_cnt; 28 29 void *bi_private; 30 #if defined(CONFIG_BLK_DEV_INTEGRITY) 31 struct bio_integrity_payload *bi_integrity; /* 数据完整性 */ 32 #endif 33 34 bio_destructor_t *bi_destructor; /* 析构 */ 35 }; | 
| 1 struct bio_vec { 2 struct page *bv_page; /* 页指针 */ 3 unsigned int bv_len; /* 传输的字节数 */ 4 unsigned int bv_offset; /* 偏移位置 */ 5 }; | 
bio_vec描述一个特定的片段,片段所在的物理页,块在物理页中的偏移页,整个bio_io_vec结构表示一个完整的缓冲区。当一个块被调用内存时,要储存在一个缓冲区,每个缓冲区与一个块对应,所以每一个缓冲区独有一个对应的描述符,该描述符用buffer_head结构表示:
- struct buffer_head {
- unsigned long b_state; /* buffer state bitmap (see above) */
- struct buffer_head *b_this_page; /* circular list of page's buffers */
- struct page *b_page; /* the page this bh is mapped to */
- sector_t b_blocknr; /* start block number */
- size_t b_size; /* size of mapping */
- char *b_data; /* pointer to data within the page */
- 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 is
- associated with */
- atomic_t b_count; /* users using this buffer_head */
- };
- void ll_rw_block(int rw, int nr, struct buffer_head *bhs[])
- {
- int i;
- for (i = 0; i < nr; i ) {
- struct buffer_head *bh = bhs[i];
- if (!trylock_buffer(bh))
- continue;
- if (rw == WRITE) {
- if (test_clear_buffer_dirty(bh)) {
- bh->b_end_io = end_buffer_write_sync;
- get_bh(bh);
- submit_bh(WRITE, bh);
- continue;
- }
- } else {
- if (!buffer_uptodate(bh)) {
- bh->b_end_io = end_buffer_read_sync;
- get_bh(bh);
- submit_bh(rw, bh);
- continue;
- }
- }
- unlock_buffer(bh);
- }
- }
- int submit_bh(int rw, struct buffer_head * bh)
- {
- struct bio *bio;
- int ret = 0;
- BUG_ON(!buffer_locked(bh));
- BUG_ON(!buffer_mapped(bh));
- BUG_ON(!bh->b_end_io);
- BUG_ON(buffer_delay(bh));
- BUG_ON(buffer_unwritten(bh));
- /*
- * Only clear out a write error when rewriting
- */
- if (test_set_buffer_req(bh) && (rw & WRITE))
- clear_buffer_write_io_error(bh);
- /*
- * from here on down, it's all bio -- do the initial mapping,
- * submit_bio -> generic_make_request may further map this bio around
- */
- bio = bio_alloc(GFP_NOIO, 1);
- bio->bi_sector = bh->b_blocknr * (bh->b_size >> 9);
- bio->bi_bdev = bh->b_bdev;
- ].bv_page = bh->b_page;
- ].bv_len = bh->b_size;
- ].bv_offset = bh_offset(bh);
- bio->bi_vcnt = 1;
- bio->bi_idx = 0;
- bio->bi_size = bh->b_size;
- bio->bi_end_io = end_bio_bh_io_sync;
- bio->bi_private = bh;
- bio_get(bio);
- submit_bio(rw, bio);
- if (bio_flagged(bio, BIO_EOPNOTSUPP))
- ret = -EOPNOTSUPP;
- bio_put(bio);
- return ret;
- }
- void generic_make_request(struct bio *bio)
- {
- struct bio_list bio_list_on_stack;
- if (!generic_make_request_checks(bio))
- return;
- if (current->bio_list) {
- bio_list_add(current->bio_list, bio);
- return;
- }
- BUG_ON(bio->bi_next);
- bio_list_init(&bio_list_on_stack);
- current->bio_list = &bio_list_on_stack;
- do {
- struct request_queue *q = bdev_get_queue(bio->bi_bdev);
- q->make_request_fn(q, bio);
- bio = bio_list_pop(current->bio_list);
- } while (bio);
- current->bio_list = NULL; /* deactivate */
- }
| 1 struct request { 2 struct list_head queuelist; 3 struct call_single_data csd; 4 int cpu; 5 6 struct request_queue *q; 7 8 unsigned int cmd_flags; 9 enum rq_cmd_type_bits cmd_type; 10 unsigned long atomic_flags; 11 12 /* 维护 I/O submission 的 BIO 遍历状态 13 * hard_开头的成员仅用于块层内部,驱动不应该改变它们 14 */ 15 16 sector_t sector; /* 要提交的下一个 sector */ 17 sector_t hard_sector; /* 要完成的下一个 sector */ 18 unsigned long nr_sectors; /* 剩余需要提交的 sector 数 */ 19 unsigned long hard_nr_sectors; /*剩余需要完成的 sector 数*/ 20 /* 在当前 segment 中剩余的需提交的 sector 数 */ 21 unsigned int current_nr_sectors; 22 23 /*在当前 segment 中剩余的需完成的 sector 数 */ 24 unsigned int hard_cur_sectors; 25 26 struct bio *bio; 27 struct bio *biotail; 28 29 struct hlist_node hash; 30 union { 31 struct rb_node rb_node; /* sort/lookup */ 32 void *completion_data; 33 }; 34 35 /* 36 * I/O 调度器可获得的两个指针,如果需要更多,请动态分配 | 
| 37 */ 38 void *elevator_private; 39 void *elevator_private2; 40 41 struct gendisk *rq_disk; 42 unsigned long start_time; 43 44 /* scatter-gather DMA 方式下 addr+len 对的数量(执行物理地址合并后) 45 */ 46 unsigned short nr_phys_segments; 47 48 unsigned short ioprio; 49 50 void *special; 51 char *buffer; 52 53 int tag; 54 int errors; 55 56 int ref_count; 57 58 unsigned short cmd_len; 59 unsigned char __cmd[BLK_MAX_CDB]; 60 unsigned char *cmd; 61 62 unsigned int data_len; 63 unsigned int extra_len; 64 unsigned int sense_len; 65 void *data; 66 void *sense; 67 68 unsigned long deadline; 69 struct list_head timeout_list; 70 unsigned int timeout; 71 int retries; 72 73 /* 74 * 完成回调函数 75 */ 76 rq_end_io_fn *end_io; 77 void *end_io_data; 78 79 struct request *next_rq; 80 }; | 
| 30 return 0; 31 out_queue: unregister_blkdev(XXX_MAJOR, "xxx"); 32 out: put_disk(xxx_disks); 33 blk_cleanup_queue(xxx_queue); 34 35 return -ENOMEM; 36 } | 
 
 

第一种,调用请求队列中自己定义的make_request_fn()函数,那问题来了,系统怎么知道这个自己定义函数在哪里呢?由内核函数blk_queue_make_request()函数指定,函数原形:
void blk_queue_make_request(struct request_queue *q,make_request_fn *mfn);
static int __make_request(struct request_queue *q,struct bio *bio);
| static int Virtual_blkdev_make_request(struct requset_queue *q,structb bio *bio) { //因为不使用I/O调度算法,直接在该函数中完成数据在内存和硬盘之间的数据传输,该函数 //代替了request_fn_proc()函数的功能 ............ } Virtual_blkdev_queue = blk_alloc_queue(GFP_KERNEL) if(!Virtual_blkdev_queue) { ret=-ENOMEN; goto err_alloc_queue; } blk_queue_make_request(Virtual_blkdev_queue,Virtual_blkdev_make_request); | 
| struct request_queue* blk_inti_queue(request_fn_proc *rfn,spinlock_t *lock) | 
 
b:块设备驱动卸载过程



 
 



 
 
 

<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">
Linux块设备驱动详解的更多相关文章
- 【转】草根老师的 linux字符设备驱动详解
		Linux 驱动 之 模块化编程 Linux 驱动之模块参数和符号导出 Linux 设备驱动之字符设备(一) Linux 设备驱动之字符设备(二) Linux 设备驱动之字符设备(三) 
- Linux dts 设备树详解(一) 基础知识
		Linux dts 设备树详解(一) 基础知识 Linux dts 设备树详解(二) 动手编写设备树dts 文章目录 1 前言 2 概念 2.1 什么是设备树 dts(device tree)? 2. ... 
- linux块设备驱动之实例
		1.注册:向内核注册个块设备驱动,其实就是用主设备号告诉内核这个代表块设备驱动 sbull_major = register_blkdev(sbull_major, "sbull&quo ... 
- Linux dts 设备树详解(二) 动手编写设备树dts
		Linux dts 设备树详解(一) 基础知识 Linux dts 设备树详解(二) 动手编写设备树dts 文章目录 前言 硬件结构 设备树dts文件 前言 在简单了解概念之后,我们可以开始尝试写一个 ... 
- 23.Linux-块设备驱动(详解)
		通过上节的块设备驱动分析,本节便通过内存来模拟块设备驱动 参考内核自带的块设备驱动程序: drivers/block /xd.c drivers/block /z2ram.c 1.本节需要的结构体如 ... 
- Linux 块设备驱动 (一)
		1.块设备的I/O操作特点 字符设备与块设备的区别: 块设备只能以块为单位接受输入和返回输出,而字符设备则以字符为单位. 块设备对于I/O请求有对应的缓冲区,因此它们可以选择以什么顺序进行响应,字符设 ... 
- Linux块设备驱动(一) _驱动模型
		块设备是Linux三大设备之一,其驱动模型主要针对磁盘,Flash等存储类设备,本文以3.14为蓝本,探讨内核中的块设备驱动模型 框架 下图是Linux中的块设备模型示意图,应用层程序有两种方式访问一 ... 
- Linux块设备驱动(二) _MTD驱动及其用户空间编程
		MTD(Memory Technology Device)即常说的Flash等使用存储芯片的存储设备,MTD子系统对应的是块设备驱动框架中的设备驱动层,可以说,MTD就是针对Flash设备设计的标准化 ... 
- linux块设备驱动
		块设备驱动程序<1>.块设备和字符设备的区别1.读取数据的单元不同,块设备读写数据的基本单元是块,字符设备的基本单元是字节.2.块设备可以随机访问,字符设备只能顺序访问. 块设备的访问:当 ... 
随机推荐
- java基础知识学习--------之枚举类型(1)
			枚举类型的概念: /** * 目的:枚举类型 * @author chenyanlong * 日期:2017/10/22 * 网址:http://blog.csdn.net/sup_heaven/ar ... 
- 解决小程序中 cover-view无法盖住canvas的问题,仅安卓真机出现
			原因在于系统页面渲染的差异,在安卓中页面dom的渲染并不是完成按照上下顺序来的, 有可能出现写在后面的dom被先渲染出来,因此会随机出现能盖住.不能盖住的情况,很诡异是不是? 开发者工具中并非真机,只 ... 
- Solr记录-solr文档xml
			Solr添加文档(XML) 在上一章中,我们学习解释了如何向Solr中添加JSON和.CSV文件格式的数据.在本章中,将演示如何使用XML文档格式在Apache Solr索引中添加数据. 示例数据 假 ... 
- 工具类 | window批处理杀死指定端口进程
			window批处理杀死指定端口进程,注意保存时使用ansi格式,运行输入端口即可 @echo off setlocal enabledelayedexpansion set /p port=请输入端口 ... 
- Kafka 温故(三):Kafka的内部机制深入(持久化,分布式,通讯协议)
			一.Kafka的持久化 1.数据持久化: 发现线性的访问磁盘(即:按顺序的访问磁盘),很多时候比随机的内存访问快得多,而且有利于持久化: 传统的使用内存做为磁盘的缓存 Kafk ... 
- [转载]AngularJS 指令 用法
			http://book.2cto.com/201312/37782.html http://www.itnose.net/detail/6144038.html http://www.cnblogs. ... 
- C标准库函数中复杂的函数声明
			<signal.h> 中有一个复杂的函数声明.很叫人费解. void (*signal(int sig, void (*handler)(int)))(int); 我们按照向右看向左看的黄 ... 
- 【CodeForces】983 E. NN country 树上倍增+二维数点
			[题目]E. NN country [题意]给定n个点的树和m条链,q次询问一条链(a,b)最少被多少条给定的链覆盖.\(n,m,q \leq 2*10^5\). [算法]树上倍增+二维数点(树状数组 ... 
- 20155233 2016-2017-2 《Java程序设计》第5周学习总结
			20155233 2016-2017-2 <Java程序设计>第5周学习总结 学习目标 理解异常架构 牚握try...catch...finally处理异常的方法 会用throw,thro ... 
- 第10月第13天 xcode ipa
			1. xcodebuild -exportArchive -exportFormat ipa -archivePath RongChatRoomDemo\ 17-7-13\ 下午4.04.xcarch ... 
