Linux设备驱动--块设备(三)之程序设计
块设备驱动注册与注销
块设备驱动中的第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内核提供,针对一个特定的硬件系统,驱动工程师所涉及到的工作往往只是编写少量的与硬件直接交互的代码。
- #include <linux/init.h>
 - #include <linux/module.h>
 - #include <linux/kernel.h>
 - #include <linux/fs.h>
 - #include <asm/uaccess.h>
 - #include <linux/spinlock.h>
 - #include <linux/sched.h>
 - #include <linux/types.h>
 - #include <linux/fcntl.h>
 - #include <linux/hdreg.h>
 - #include <linux/genhd.h>
 - #include <linux/blkdev.h>
 - #define MAXBUF 1024
 - #define BLK_MAJOR 253
 - char blk_dev_name[]="blk_dev";
 - static char flash[1024*16];
 - int major;
 - spinlock_t lock;
 - struct gendisk *gd;
 - /*块设备数据传输*/
 - static void blk_transfer(unsigned long sector, unsigned long nsect, char *buffer, int write)
 - {
 - int read = !write;
 - if(read)
 - {
 - memcpy(buffer, flash+sector*512, nsect*512);
 - }
 - else
 - {
 - memcpy(flash+sector*512, buffer, nsect*512);
 - }
 - }
 - /*块设备请求处理函数*/
 - static void blk_request_func(struct request_queue *q)
 - {
 - struct request *req;
 - while((req = elv_next_request(q)) != NULL)
 - {
 - if(!blk_fs_request(req))
 - {
 - end_request(req, 0);
 - continue;
 - }
 - blk_transfer(req->sector, req->current_nr_sectors, req->buffer, rq_data_dir(req));
 - /*rq_data_dir从request获得数据传送的方向*/
 - /*req->current_nr_sectors 在当前段中将完成的扇区数*/
 - /*req->sector 将提交的下一个扇区*/
 - end_request(req, 1);
 - }
 - }
 - /*strcut block_device_operations*/
 - static int blk_ioctl(struct block_device *dev, fmode_t no, unsigned cmd, unsigned long arg)
 - {
 - return -ENOTTY;
 - }
 - static int blk_open (struct block_device *dev , fmode_t no)
 - {
 - printk("blk mount succeed\n");
 - return 0;
 - }
 - static int blk_release(struct gendisk *gd , fmode_t no)
 - {
 - printk("blk umount succeed\n");
 - return 0;
 - }
 - struct block_device_operations blk_ops=
 - {
 - .owner = THIS_MODULE,
 - .open = blk_open,
 - .release = blk_release,
 - .ioctl = blk_ioctl,
 - };
 - //-----------------------------------------------
 - static int __init block_module_init(void)
 - {
 - if(!register_blkdev(BLK_MAJOR, blk_dev_name)) //注册一个块设备
 - {
 - major = BLK_MAJOR;
 - printk("regiser blk dev succeed\n");
 - }
 - else
 - {
 - return -EBUSY;
 - }
 - gd = alloc_disk(1); //分配一个gendisk,分区是一个
 - spin_lock_init(&lock); //初始化一个自旋锁
 - gd->major = major;
 - gd->first_minor = 0; //第一个次设备号
 - gd->fops = &blk_ops; //关联操作函数
 - gd->queue = blk_init_queue(blk_request_func, &lock); //初始化请求队列并关联到gendisk
 - snprintf(gd->disk_name, 32, "blk%c", 'a');
 - blk_queue_hardsect_size(gd->queue, 512); //设置扇区大小512字节
 - set_capacity(gd, 32); //设置块设备大小 512*32=16K
 - add_disk(gd);
 - printk("gendisk init success!\n");
 - return 0;
 - }
 - static void __exit block_module_exit(void)
 - {
 - blk_cleanup_queue(gd->queue);
 - del_gendisk(gd);
 - unregister_blkdev(BLK_MAJOR, blk_dev_name);
 - printk("block module exit succeed!\n");
 - }
 - module_init(block_module_init);
 - module_exit(block_module_exit);
 - MODULE_LICENSE("GPL");
 - MODULE_AUTHOR("gec");
 - //------------------------------------------------------------------------------
 
Linux设备驱动--块设备(三)之程序设计的更多相关文章
- 【转】Linux设备驱动--块设备(一)之概念和框架
		
原文地址:Linux设备驱动--块设备(一)之概念和框架 基本概念 块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时 ...
 - Linux设备驱动--块设备(三)之程序设计(转)
		
http://blog.csdn.net/jianchi88/article/details/7212701 块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数 ...
 - Linux设备驱动--块设备(四)之“自造请求”
		
前面, 我们已经讨论了内核所作的在队列中优化请求顺序的工作; 这个工作包括排列请求和, 或许, 甚至延迟队列来允许一个预期的请求到达. 这些技术在处理一个真正的旋转的磁盘驱动器时有助于系统的性能. 但 ...
 - Linux设备驱动--块设备(二)之相关结构体
		
上回最后面介绍了相关数据结构,下面再详细介绍 块设备对象结构 block_device 内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区.如果该结构代表一个分区,则其成 ...
 - Linux设备驱动--块设备(四)之“自造请求”(转)
		
前面, 我们已经讨论了内核所作的在队列中优化请求顺序的工作; 这个工作包括排列请求和, 或许, 甚至延迟队列来允许一个预期的请求到达. 这些技术在处理一个真正的旋转的磁盘驱动器时有助于系统的性能. 但 ...
 - Linux设备驱动--块设备(二)之相关结构体(转)
		
上回最后面介绍了相关数据结构,下面再详细介绍 块设备对象结构 block_device 内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区.如果该结构代表一个分区,则其成 ...
 - Linux设备驱动--块设备(一)之概念和框架(转)
		
基本概念 块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性 ...
 - 3.5Linux设备驱动--块设备(一)之概念和框架☆☆
		
基本概念 块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性 ...
 - Linux字符设备与块设备的区别与比较
		
Linux中I/O设备分为两类:块设备和字符设备.两种设备本身没有严格限制,但是,基于不同的功能进行了分类. (1) 字符设备:提供连续的数据流,应用程序可以顺序读取,通常不支持随机存取.相反,此类设 ...
 
随机推荐
- 【java基础 3】树形结构数据呈现的递归算法实现
			
一.基本概况 在我的项目中,常常会用到树形结构的数据,最为明显的就是左边菜单栏,类似于window folder一样的东西. 而我之前一直是借助前端封装好的ZTree等工具实现展示,而后台则通常使用递 ...
 - 虚拟机(Virtual Machine)和容器(Container)的对比
			
目前云计算平台常用的虚拟化技术有虚拟机(Virtual Machine)和容器(Container)两种.虚拟机已经是比较成熟的技术,容器技术作为下一代虚拟化技术,国内的各厂商应用还不广,但似乎其代表 ...
 - 【2018 Multi-University Training Contest 1】
			
01:https://www.cnblogs.com/myx12345/p/9362221.html 02:https://www.cnblogs.com/myx12345/p/9382267.htm ...
 - msp430入门编程46
			
msp430中C语言的人机交互--基于状态机菜单
 - [NOI2012(bzoj2879)(vijos1726)]美食节 (费用流)
			
2879: [Noi2012]美食节 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 2288 Solved: 1207[Submit][Status ...
 - Ubuntu 16.04安装QQ(不一定成功)
			
注意1:如果是刚新装的系统,可以正常安装,但是,如果你已经装了很多软件,千万不要安装,因为会把系统上一般的依赖包和你之前装的软件全部卸载掉!甚至将桌面Dock都会卸载!最终只能重装Ubuntu解决. ...
 - Web容器自己主动对HTTP请求中參数进行URLDecode处理
			
这篇文章转载自 : Web容器自己主动对HTTP请求中參数进行URLDecode处理 如题.在Java中或许非常多人都没有注意到当我们发送一个http请求时,假设附带的參数被URLEncode之后,到 ...
 - android KE or NE分析
			
使用arm-eabi-addr2line工具跟踪Android调用堆栈 在通常的C/C++代码中,可以通过响应对内存操作不当引起的Segmentation Fault错误即信号SIGSEGV(11)做 ...
 - JSP中的编译指令和动作指令的差别
			
JSP中的编译指令和动作指令的差别 1.编译指令是通知Servlet引擎的处理消息.而动作指令仅仅是执行时的脚本动作 2.编译指令是在将JSP编译成Servlet时起作用,而动作指令可替换成JSP脚本 ...
 - jpa删除根据对象删除失败,报Removing a detached instance 错
			
引用:https://blog.csdn.net/zhanggnol/article/details/6307936 常用数据库表的删除办法,一般都会在DAO类中提供delete.如下例: publi ...