块设备驱动注册与注销

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

Linux设备驱动--块设备(三)之程序设计的更多相关文章

  1. 【转】Linux设备驱动--块设备(一)之概念和框架

    原文地址:Linux设备驱动--块设备(一)之概念和框架 基本概念   块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时 ...

  2. Linux设备驱动--块设备(三)之程序设计(转)

    http://blog.csdn.net/jianchi88/article/details/7212701 块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数 ...

  3. Linux设备驱动--块设备(四)之“自造请求”

    前面, 我们已经讨论了内核所作的在队列中优化请求顺序的工作; 这个工作包括排列请求和, 或许, 甚至延迟队列来允许一个预期的请求到达. 这些技术在处理一个真正的旋转的磁盘驱动器时有助于系统的性能. 但 ...

  4. Linux设备驱动--块设备(二)之相关结构体

    上回最后面介绍了相关数据结构,下面再详细介绍 块设备对象结构 block_device 内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区.如果该结构代表一个分区,则其成 ...

  5. Linux设备驱动--块设备(四)之“自造请求”(转)

    前面, 我们已经讨论了内核所作的在队列中优化请求顺序的工作; 这个工作包括排列请求和, 或许, 甚至延迟队列来允许一个预期的请求到达. 这些技术在处理一个真正的旋转的磁盘驱动器时有助于系统的性能. 但 ...

  6. Linux设备驱动--块设备(二)之相关结构体(转)

    上回最后面介绍了相关数据结构,下面再详细介绍 块设备对象结构 block_device 内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区.如果该结构代表一个分区,则其成 ...

  7. Linux设备驱动--块设备(一)之概念和框架(转)

    基本概念   块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性 ...

  8. 3.5Linux设备驱动--块设备(一)之概念和框架☆☆

    基本概念   块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性 ...

  9. Linux字符设备与块设备的区别与比较

    Linux中I/O设备分为两类:块设备和字符设备.两种设备本身没有严格限制,但是,基于不同的功能进行了分类. (1) 字符设备:提供连续的数据流,应用程序可以顺序读取,通常不支持随机存取.相反,此类设 ...

随机推荐

  1. Javascript拼接HTML字符串的方法列举及思路

    转载过来,去掉一些废话吧. 目标: 方便的拼接字符串,不使用让人眼晕的+=.使用过程如下: 1,先创建一个作为“模板”的字符串,如:’My name is ${name},I\’m ${age}.’ ...

  2. bzoj 2326 矩阵乘法

    [HNOI2011]数学作业 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2415  Solved: 1413[Submit][Status][Di ...

  3. iOS时钟,秒针扫秒样式

    昨天做一个时钟小demo,发现了一些问题. 描述能力有限,我封装好了一个时钟框架,朋友们可以参考      https://github.com/qianlishun/ClockView 点击这里可以 ...

  4. POJ 3620 Avoid The Lakes

    http://poj.org/problem?id=3620 DFS 从任意一个lake出发 重置联通的lake 并且记录 更新ans #include <iostream> #inclu ...

  5. 巴蜀3540 -- 【Violet 6 最终话】蒲公英

    Description 原题的时间限制是 2s . 亲爱的哥哥: 你在那个城市里面过得好吗? 我在家里面最近很开心呢.昨天晚上奶奶给我讲了那个叫「绝望」的大坏蛋的故事的说!它把人们的房子和田地搞坏,还 ...

  6. URAL 1614. National Project “Trams” [ 构造 欧拉回路 ]

    传送门 1614. National Project “Trams” Time limit: 0.5 secondMemory limit: 64 MB President has declared ...

  7. PatentTips - Solid State Disk (SSD) Device

    BACKGROUND OF THE INVENTION A SSD apparatus is a large-capacity data storage device using a nonvolat ...

  8. Shell脚本的编写,sed的使用以及一些正则表达式

    Shell脚本的简单编写以及sed的使用 标签(空格分隔): 博客文章 前一阵子为了批量修改Web审计规则,故编写了一个Shell脚本,顺便使用了下sed,顺便把正则表达式也重新学习一遍,感觉还是需要 ...

  9. HDU 1402 大数乘法 FFT、NTT

    A * B Problem Plus Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Other ...

  10. Meteor第一个应用程序

    这一个小教程将教你如何建立你的第一个 Meteor 应用程序. 步骤 1 - 创建App 要创建应用程序,我们将从命令提示符窗口运行 meteor create 命令.该应用程序的名称是 meteor ...