Linux设备驱动--块设备(三)之程序设计(转)
http://blog.csdn.net/jianchi88/article/details/7212701
块设备驱动注册与注销
块设备驱动中的第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");
转载于:https://www.cnblogs.com/cainiaoaixuexi/p/3713200.html
Linux设备驱动--块设备(三)之程序设计(转)相关推荐
- Linux设备驱动--块设备(二)之相关结构体
上回最后面介绍了相关数据结构,下面再详细介绍 块设备对象结构 block_device 内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区.如果该结构代表一个分区,则其成 ...
- bio linux 创建_Linux设备驱动--块设备之概念和框架以及相关结构体
基本概念 块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性读到 ...
- Linux驱动笔记-字符设备,块设备,网络设备
在Linux设备驱动开发中,粗略的将设备分为三种类型:字符设备,块设备和网络设备. 1.字符设备:指能够像字节流串行顺序依次进行访问的设备,对它的读写是以字节为单位.字符设备的上层没有磁盘文件系统 ...
- linux内核的块设备驱动框架详解
1.块设备和字符设备的差异 (1)块设备只能以块为单位接受输入和返回输出,而字符设备则以字节为单位.大多数设备是字符设备,因为它们不需要缓冲而且不以固定块大小进行操作; (2)块设备对于 I/O 请求 ...
- linux三大驱动类型:字符设备、块设备、网络设备
1. 字符设备 字符设备指能够像字节流串行顺序依次进行访问的设备,对它的读写是以字节为单位.字符设备的上层没有磁盘文件系统,所以字符设备的file_operations成员函数就直接由字符设备驱动提供 ...
- linux内核驱动之 设备和模块的分类
目录 字符设备 块设备 网络接口 其他划分方式 以 Linux 的方式看待设备可区分为 3 种基本设备类型. 每个模块常常实现 3 种类型中的 1 种, 因此可分类成字符模块, 块模块, 或者一个网络 ...
- linux字符设备和块设备的区别 以及网络设备
一.字符设备 1.字符设备以字节为单位.大多数设备是字符设备,因为他们不需要缓冲而且不以固定块大小进行操作. 2.字符设备无需缓冲直接读写. 3.字符设备只能被顺序读写. 二.块设备 1.块设备只能以 ...
- Linux字符设备与块设备的区别与比较
Linux中I/O设备分为两类:块设备和字符设备.两种设备本身没有严格限制,但是,基于不同的功能进行了分类. (1) 字符设备:提供连续的数据流,应用程序可以顺序读取,通常不支持随机存取.相反,此类设 ...
- linux mtd 块设备,Linux系统中/dev/mtd与/dev/mtdblock的区别,即MTD字符设备和块设备的区别...
转:http://www.crifan.com/linux_system_in__dev__mtd_and__dev__mtdblock_distinction_character_devices_a ...
最新文章
- UE5废墟破坏游戏场景创建学习教程
- Ceryx —— 基于 OpenResty 的动态反向代理
- 美国新桥投资集团(Newbridge Capital) [from baike]
- win10创建新的计算机用户名和密码忘了,Win10 2004中要使用本计算机用户必须输入用户名和密码选项不见了如何恢复?...
- docker 容器 导入 导出
- Sublime Text 2 中怎样查找scope的名称
- 如何实现一个跨库连表SQL生成器?
- 《TCP/IP详解 卷1:协议》第4章 ARP:地址解析协议
- 消费者rebalance机制分析
- Windows Server 2008 将与 Visual Studio 2008 和 SQL Server 2008 于2008年2月27 日在洛杉矶共同发布...
- [转]C++ 智能指针详解
- Oracle的函数大全
- 太强了!GitHub中文开源项目榜单出炉,暴露了程序员的硬性需求!
- 2021年G2电站锅炉司炉最新解析及G2电站锅炉司炉作业考试题库
- 【实战篇】微信公众号网页授权登录实现起来如此简单
- 以太坊为什么又要进行“缪尔冰川”硬分叉?
- C/C++:打印乘法口诀表
- Oracle数字转汉字过程,oracle中如何将字符和数字转成中文
- 饿了么商家开放平台踩坑记录2,php更新商品信息提示attribute:[]不是一个有效的JSON对象 By勤勤学长 Qq318692996
- 云存储相关技术及术语的探讨
热门文章
- 网站收录的提升离不开“方法”和“坚持”
- 网站推广方法众多,对此你了解多少?
- 网站优化时需注意哪些事项可有效防止排名下降?
- 浅析网站SEO优化对长尾关键词保持好感度的四大技巧
- java分解因式_用JAVA因式分解 并以9=3×3格式输出
- service层加需要加锁吗_Redis分布式锁,你真的用对了吗?
- 纽曼皮尔逊准则Matlab实现,基于聂曼-皮尔逊准则的skip模式快速选择方法
- (转载)macOS 解决apue.h不存在的问题
- tensorflow LSTM
- 剑指offer 04:重构二叉树