块设备驱动注册与注销

块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为:
int register_blkdev(unsigned int major, const char *name);

major 参数是块设备要使用的主设备号,name为设备名,它会在/proc/devices中被显示。 如果major为0,内核会自动分配一个新的主设备号register_blkdev()函数的返回值就是这个主设备号。如果返回1个负值,表明发生了一个错误。

与register_blkdev()对应的注销函数是unregister_blkdev(),其原型为:
int unregister_blkdev(unsigned int major, const char *name);
这里,传递给register_blkdev()的参数必须与传递给register_blkdev()的参数匹配,否则这个函数返回-EINVAL。

块设备的请求队列操作

标准的请求处理程序能排序请求,并合并相邻的请求,如果一个块设备希望使用标准的请求处理程序,那它必须调用函数blk_init_queue来初始化请求队列。当处理在队列上的请求时,必须持有队列自旋锁。初始化请求队列
request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock);

该函数的第1个参数是请求处理函数的指针,第2个参数是控制访问队列权限的自旋锁,这个函数会发生内存分配的行为,故它可能会失败,函数调用成
功时,它返回指向初始化请求队列的指针,否则,返回NULL。这个函数一般在块设备驱动的模块加载函数中调用。清除请求队列
void blk_cleanup_queue(request_queue_t * q);

这个函数完成将请求队列返回给系统的任务,一般在块设备驱动模块卸载函数中调用。

提取请求
struct request *elv_next_request(request_queue_t *queue); 
上述函数用于返回下一个要处理的请求(由 I/O 调度器决定),如果没有请求则返回NULL。

去除请求
void blkdev_dequeue_request(struct request *req); 
上述函数从队列中去除1个请求。如果驱动中同时从同一个队列中操作了多个请求,它必须以这样的方式将它们从队列中去除。

分配“请求队列”
request_queue_t *blk_alloc_queue(int gfp_mask);
对于FLASH、RAM盘等完全随机访问的非机械设备,并不需要进行复杂的I/O调度,这个时候,应该使用上述函数分配1个“请求队列”,并使用如下函数来绑定“请求队列”和“制造请求”函数。
void blk_queue_make_request(request_queue_t * q, 
make_request_fn * mfn);

void blk_queue_hardsect_size(request_queue_t *queue, 
unsigned short max); 
该函数用于告知内核块设备硬件扇区的大小,所有由内核产生的请求都是这个大小的倍数并且被正确对界。但是,内核块设备层和驱动之间的通信还是以512字节扇区为单位进行。

步骤:

在块设备驱动的模块加载函数中通常需要完成如下工作:
① 分配、初始化请求队列,绑定请求队列和请求函数。
② 分配、初始化gendisk,给gendisk的major、fops、queue等成
员赋值,最后添加gendisk。
③ 注册块设备驱动。
在块设备驱动的模块卸载函数中通常需要与模块加载函数相反的工作:
① 清除请求队列。
② 删除gendisk和对gendisk的引用。
③ 删除对块设备的引用,注销块设备驱动。

总结:

块设备的I/O操作方式与字符设备存在较大的不同,因而引入了
request_queue、request、bio等一系列数据结构。在整个块设备的I/O操作中,贯穿于始终的就是“请求”,字符设备的I/O操作则是直接进行不绕弯,
块设备的I/O操作会排队和整合。

驱动的任务是处理请求,对请求的排队和整合由I/O调度算法解决,因此,块设备驱动的核心就是请求处理函数或“制造请求”函数。

尽管在块设备驱动中仍然存在block_device_operations结构体及其成员函数,但其不再包含读写一类的成员函数,而只是包含打开、释放及I/O控制等
与具体读写无关的函数。块设备驱动的结构相当复杂的,但幸运的是,块设备不像字符设备那么包罗万象,它通常就是存储设备,而且驱动的主体已经
由Linux内核提供,针对一个特定的硬件系统,驱动工程师所涉及到的工作往往只是编写少量的与硬件直接交互的代码。

  1 #include <linux/init.h>
2 #include <linux/module.h>
3 #include <linux/kernel.h>
4 #include <linux/fs.h>
5 #include <asm/uaccess.h>
6 #include <linux/spinlock.h>
7 #include <linux/sched.h>
8 #include <linux/types.h>
9 #include <linux/fcntl.h>
10 #include <linux/hdreg.h>
11 #include <linux/genhd.h>
12 #include <linux/blkdev.h>
13
14 #define MAXBUF 1024
15
16
17 #define BLK_MAJOR 253
18
19 char blk_dev_name[]="blk_dev";
20 static char flash[1024*16];
21
22
23 int major;
24 spinlock_t lock;
25 struct gendisk *gd;
26
27
28
29 /*块设备数据传输*/
30 static void blk_transfer(unsigned long sector, unsigned long nsect, char *buffer, int write)
31 {
32 int read = !write;
33 if(read)
34 {
35 memcpy(buffer, flash+sector*512, nsect*512);
36 }
37 else
38 {
39 memcpy(flash+sector*512, buffer, nsect*512);
40 }
41 }
42
43 /*块设备请求处理函数*/
44 static void blk_request_func(struct request_queue *q)
45 {
46 struct request *req;
47 while((req = elv_next_request(q)) != NULL)
48 {
49 if(!blk_fs_request(req))
50 {
51 end_request(req, 0);
52 continue;
53 }
54
55 blk_transfer(req->sector, req->current_nr_sectors, req->buffer, rq_data_dir(req));
56 /*rq_data_dir从request获得数据传送的方向*/
57 /*req->current_nr_sectors 在当前段中将完成的扇区数*/
58 /*req->sector 将提交的下一个扇区*/
59 end_request(req, 1);
60 }
61 }
62
63 /*strcut block_device_operations*/
64 static int blk_ioctl(struct block_device *dev, fmode_t no, unsigned cmd, unsigned long arg)
65 {
66 return -ENOTTY;
67 }
68
69 static int blk_open (struct block_device *dev , fmode_t no)
70 {
71 printk("blk mount succeed\n");
72 return 0;
73 }
74 static int blk_release(struct gendisk *gd , fmode_t no)
75 {
76 printk("blk umount succeed\n");
77 return 0;
78 }
79 struct block_device_operations blk_ops=
80 {
81 .owner = THIS_MODULE,
82 .open = blk_open,
83 .release = blk_release,
84 .ioctl = blk_ioctl,
85 };
86
87 //-----------------------------------------------
88
89 static int __init block_module_init(void)
90 {
91
92
93 if(!register_blkdev(BLK_MAJOR, blk_dev_name)) //注册一个块设备
94 {
95 major = BLK_MAJOR;
96 printk("regiser blk dev succeed\n");
97 }
98 else
99 {
100 return -EBUSY;
101 }
102 gd = alloc_disk(1); //分配一个gendisk,分区是一个
103 spin_lock_init(&lock); //初始化一个自旋锁
104 gd->major = major;
105 gd->first_minor = 0; //第一个次设备号
106 gd->fops = &blk_ops; //关联操作函数
107
108 gd->queue = blk_init_queue(blk_request_func, &lock); //初始化请求队列并关联到gendisk
109
110 snprintf(gd->disk_name, 32, "blk%c", 'a');
111 blk_queue_hardsect_size(gd->queue, 512); //设置扇区大小512字节
112 set_capacity(gd, 32); //设置块设备大小 512*32=16K
113 add_disk(gd);
114 printk("gendisk init success!\n");
115 return 0;
116 }
117 static void __exit block_module_exit(void)
118 {
119 blk_cleanup_queue(gd->queue);
120 del_gendisk(gd);
121 unregister_blkdev(BLK_MAJOR, blk_dev_name);
122 printk("block module exit succeed!\n");
123 }
124
125 module_init(block_module_init);
126 module_exit(block_module_exit);
127
128 MODULE_LICENSE("GPL");
129 MODULE_AUTHOR("gec");

linux块设备驱动---程序设计(转)的更多相关文章

  1. linux块设备驱动之实例

    1.注册:向内核注册个块设备驱动,其实就是用主设备号告诉内核这个代表块设备驱动 sbull_major  =  register_blkdev(sbull_major, "sbull&quo ...

  2. Linux块设备驱动详解

    <机械硬盘> a:磁盘结构 -----传统的机械硬盘一般为3.5英寸硬盘,并由多个圆形蝶片组成,每个蝶片拥有独立的机械臂和磁头,每个堞片的圆形平面被划分了不同的同心圆,每一个同心圆称为一个 ...

  3. Linux 块设备驱动 (一)

    1.块设备的I/O操作特点 字符设备与块设备的区别: 块设备只能以块为单位接受输入和返回输出,而字符设备则以字符为单位. 块设备对于I/O请求有对应的缓冲区,因此它们可以选择以什么顺序进行响应,字符设 ...

  4. Linux块设备驱动(一) _驱动模型

    块设备是Linux三大设备之一,其驱动模型主要针对磁盘,Flash等存储类设备,本文以3.14为蓝本,探讨内核中的块设备驱动模型 框架 下图是Linux中的块设备模型示意图,应用层程序有两种方式访问一 ...

  5. Linux块设备驱动(二) _MTD驱动及其用户空间编程

    MTD(Memory Technology Device)即常说的Flash等使用存储芯片的存储设备,MTD子系统对应的是块设备驱动框架中的设备驱动层,可以说,MTD就是针对Flash设备设计的标准化 ...

  6. linux块设备驱动

    块设备驱动程序<1>.块设备和字符设备的区别1.读取数据的单元不同,块设备读写数据的基本单元是块,字符设备的基本单元是字节.2.块设备可以随机访问,字符设备只能顺序访问. 块设备的访问:当 ...

  7. Linux块设备驱动_WDS

    推荐书:<Linux内核源代码情景分析> 1.字符设备驱动和使用中等待某一事件的方法①查询方式②休眠唤醒,但是这种没有超时时间③poll机制,在休眠唤醒基础上加一个超时时间④异步通知,异步 ...

  8. linux 块设备驱动 (三)块设备驱动开发

    一: 块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为:int register_blkdev(unsigne ...

  9. linux块设备驱动(一)——块设备概念介绍

    本文来源于: 1. http://blog.csdn.net/jianchi88/article/details/7212370 2. http://blog.chinaunix.net/uid-27 ...

随机推荐

  1. 18 vue 动态路由传参

    params形式 http://192.168.1.100:8080/#/infoDetailed/231 //定义路由{ path: "/infoDetailed/:newsId" ...

  2. python - 模块调用

    基础 调用模块常见的两种方法 import [模块名] from [模块名] import [属性/方法] 进阶用法 调用父级目录下模块 背景介绍 目录new2(b2.py)调用上级目录new1(b1 ...

  3. C#开发PACS医学影像处理系统(二):界面布局之菜单栏

    在菜单栏布局上,为了使用自定义窗体样式和按钮,我们需要先将窗体设置为无边框,然后添加一个Grid作为菜单栏并置顶,VerticalAlignment="Top" logo图片和标题 ...

  4. 滴滴AR实景导航背后的技术

    桔妹导读:机场.商场.火车站等大型室内场所内GPS信号不稳定.室内面积大.路线复杂.用户判断方向难等问题,给在大型场所内发单的乘客找上车点带来了很大的挑战,用户急需一种操作简单.交互友好的引导功能.本 ...

  5. ui自动化--鼠标操作ActionChains

    需要先引入鼠标操作模块:from selenium.webdriver.common.action_chains import ActionChains 实际上ActionChains这个模块的实现的 ...

  6. python中绑定码云仓库

    1.File——Settings——Version Control——Git——输入git安装路径下bin下的git.exe路径——点击后面的Test测试一下,弹出版本点击ok即可 2.点击工具栏中的 ...

  7. 记录一次OCR程序开发的尝试

    记录一次OCR程序开发的尝试 最近工作中涉及到一部分文档和纸质文档的校验工作,就想把纸质文件拍下来,用文字来互相校验.想到之前调用有道智云接口做了文档翻译.看了下OCR文字识别的API接口,有道提供了 ...

  8. [程序员代码面试指南]二叉树问题-判断t1树是否包含t2树的全部拓扑结构、[LeetCode]572. 另一个树的子树

    题目1 解 先序遍历树1,判断树1以每个节点为根的子树是否包含树2的拓扑结构. 时间复杂度:O(M*N) 注意区分判断总体包含关系.和判断子树是否包含树2的函数. 代码 public class Ma ...

  9. vSphere Client上安装虚拟机工具VMware Tools

    vSphere Client上安装虚拟机工具VMware Tools 1.安装虚拟机 具体安装步骤就不详述了,安装虚拟机之后右击虚拟机名->客户机->安装/升级VMware Tools,这 ...

  10. tomcat源码之概述

    tomcat架构及常用的组件如下: Server Server代表了tomcat服务器,Tomcat启动时即会启动一个server实例,它监听在8005端口以接收shutdown命令,使用 telne ...