Chinaunix首页 | 论坛 | 博客

分类: 嵌入式

2018-06-04 19:50:41

本文乃fireaxe原创,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,并注明原作者及原链接。内容可任意使用,但对因使用该内容引起的后果不做任何保证。

作者:fireaxe.hq@outlook.com
博客:fireaxe.blog.chinaunix.net 


1. 前言

      本文实现了一个最简单的块设备驱动程序,块设备通过内存进行模拟。
      块设备与字符设备的区别是什么?简单来讲,就是字符设备中间层非常薄,只需要为用户io的read、write、open等接口提供回调即可;快速设备则是先挂到block层,向上提供用户io的操作。而block层,则可以自由挂接各种文件系统。
对于有块设备驱动,需要了解的是需要向linux block层提供什么样的接口。

2. 内存虚拟块设备注册

(1)首先是申请块设备号,使用下面的接口:
int register_blkdev(unsigned int major, const char *name)

(2)然后通过下面的接口把驱动挂入block层。
void add_disk(struct gendisk *disk)

block对下看到了就是一个个gendisk,其中需要开发这设置的主要是如下的几个参数:
  • majoR:块设备号,来自于register_blkdev。
  • disk_name:设备名,未来从dev目录下看到的就是这个名字。
  • fops:用于直接操作块设备的接口,不添加也可以,因为通常都是通过block层下发request进行操作,不会直接使用该接口,但某些如geoget与ioctl接口,则需要通过这里挂接处理函数。本例子中没有添加。
  • private_data:通常用于存储驱动数据结构入口。当文件系统操作某文件时,会从这个参数找到需要操作的设备。
  • queue:这个是与block层交互的主要方式,是一个request_queue,block层的request都会被挂入这个queue,再进行处理。
(3)设备数据结构
设备数据结构用于维护设备相关的信息,这里只维护最基本的:设备大小与入口指针。
我们这里是用内存虚拟的块设备,因此入口指针就是vmalloc的返回值,大小就是vmalloc申请的大小。
我们认为的把这块内存划分成n个sector,
reques分解到最后时,对应的是对某一块sector的读写操作,我们根据sector的编号,使用这块内存对应的sector即可。



3. 数据流:request

     request到达驱动时,会调用驱动注册的request queue处理函数进行处理,例程中有request的处理函数的实现,其核心是通过requestqueue,找到需要处理的数据。要理解这段代码,则需要了解request queue的结构。
      

     应用程序对文件的read与write操作,会在block 层中生成request请求,并挂入对应设备的reqeust_queue队列,然后会触发驱动来处理request。这样做的目的是为了避免每次read与write都触发设备的读写。因为外设的读写比较耗时间,但每次读取时,读取一个sector与读取多个sector的速度相差不多。通过request queue,可以把多次请求合并成一次请求。
      每个request中可以挂多个bio,每个bio中又可以有多个segment,每个segment中对应着一块或连续多块sector。驱动开发者添加为request queue添加的处理函数wiccablk_request就是为了找打所有需要处理的sector,一次性处理它们。为了方便编写,例程中按照这种层级关系实现了一串函数:
wiccablk_request() -> wiccablk_xfer_request() -> wiccablk_xfer_bio() -> wiccablk_transfer()
      注:request的构成涉及到文件系统的实现原理,涉及到io请求合并、cache处理等内容。具体分析会在后续分析文件系统时写,这里只要理解request到sector的过称就可以了。


4. 测试
# 添加并格式化我们用内存虚拟的块设备
insmod test_wiccablk.ko
ls /dev/wiccablk*
mkfs.ext4 /dev/wiccablka

# 测一下是否能够像块设备一样使用
mount /dev/wiccablka abc
echo 123456 > abc/123
cat abc/123
umount abc

# umount后再次mount,看看原来的内容还在不在
mount /dev/wiccablka abc
cat abc/123
umount abc


5. 代码参考

点击(此处)折叠或打开

  1. /*basedonkernel 4.10*/

  2. #include/*formodules*/
  3. #include
  4. #include
  5. #include
  6. #include
  7. #include
  8. #include
  9. #include
  10. #include/*file_operations*/
  11. #include/*copy_(to,from)_user*/
  12. #include/*module_init,module_exit*/
  13. #include/*kmalloc,kfree*/
  14. #include/*kmalloc,kfree*/
  15. #include
  16. #include
  17. #include
  18. #include
  19. #include
  20. #include
  21. #include
  22. #include
  23. #include
  24. #include

  25. #define KERNEL_SECTOR_SIZE 512
  26. #define WICCA_MINOR_MAX 1
  27. #define SECTOR_SHIFT 9

  28. staticintwiccablk_major=0;
  29. staticinthardsect_size=512;
  30. staticintnsectors=1024;/*How big the driveis*/
  31. staticintndevices=1;

  32. struct wiccablk_dev{
  33.     intsize;
  34.     u8*data;
  35.     struct gendisk*gd;
  36. };

  37. struct wiccablk_dev*Devices;

  38. static struct block_device_operations wiccablk_ops={
  39.     .owner=THIS_MODULE,
  40. };

  41. static void wiccablk_transfer(struct wiccablk_dev*dev,unsigned long sector,
  42.         unsigned long nsect,char*buffer,intwrite)
  43. {
  44.     unsigned long offset=sector*hardsect_size;
  45.     unsigned long nbytes=nsect*hardsect_size;

  46.     if((offset+nbytes)>dev->size){
  47.         printk("wiccablk write (%ld %ld)\n",offset,nbytes);
  48.         return;
  49.     }

  50.     if(write)
  51.         memcpy(dev->data+offset,buffer,nbytes);
  52.     else
  53.         memcpy(buffer,dev->data+offset,nbytes);
  54. }

  55. staticintwiccablk_xfer_bio(struct wiccablk_dev*dev,struct bio*bio)
  56. {
  57.     struct bvec_iter i;
  58.     struct bio_vec bvec;
  59.     sector_t sector=bio->bi_iter.bi_sector;

  60.     bio_for_each_segment(bvec,bio,i){
  61.         unsignedintnsect=bvec.bv_len/hardsect_size;
  62.         char*buffer=__bio_kmap_atomic(bio,i);
  63.         wiccablk_transfer(dev,sector,nsect,
  64.                 buffer,bio_data_dir(bio)==WRITE);
  65.         sector+=nsect;
  66.         __bio_kunmap_atomic(bio);
  67.     }
  68.     return 0;
  69. }

  70. staticintwiccablk_xfer_request(struct wiccablk_dev*dev,struct request*req)
  71. {
  72.     struct bio*bio;
  73.     intnsect=0;

  74.     __rq_for_each_bio(bio,req){
  75.         wiccablk_xfer_bio(dev,bio);
  76.         nsect+=bio->bi_iter.bi_size/hardsect_size;
  77.     }
  78.     return nsect;
  79. }

  80. static void wiccablk_request(struct request_queue*q)
  81. {
  82.     struct request*req;
  83.     struct wiccablk_dev*dev=q->queuedata;

  84.     req=blk_fetch_request(q);
  85.     while(req!=NULL){
  86.         wiccablk_xfer_request(dev,req);
  87.         if(!__blk_end_request_cur(req,0))
  88.             req=blk_fetch_request(q);
  89.     }
  90. }

  91. static void setup_device(struct wiccablk_dev*dev,intwhich)
  92. {
  93.     struct request_queue*queue;

  94.     memset(dev,0,sizeof(struct wiccablk_dev));
  95.     dev->size=nsectors*hardsect_size;

  96.     dev->data=vmalloc(dev->size);
  97.     if(dev->data==NULL){
  98.         printk("wiccablk vmalloc failure\n");
  99.         return;
  100.     }

  101.     dev->gd=alloc_disk(WICCA_MINOR_MAX);
  102.     if(!dev->gd){
  103.         printk("wiccablk alloc_disk failed\n");
  104.         vfree(dev->data);
  105.         return;
  106.     }

  107.     queue=blk_init_queue(wiccablk_request,NULL);
  108.     if(queue==NULL){
  109.         printk("wiccablk blk_init_queue failed\n");
  110.         del_gendisk(dev->gd);
  111.         vfree(dev->data);
  112.         return;
  113.     }

  114.     blk_queue_logical_block_size(queue,hardsect_size);
  115.     queue->queuedata=dev;
  116.     dev->gd->queue=queue;

  117.     dev->gd->major=wiccablk_major;
  118.     dev->gd->first_minor=which*WICCA_MINOR_MAX;
  119.     dev->gd->fops=&wiccablk_ops;
  120.     dev->gd->private_data=dev;

  121.     snprintf(dev->gd->disk_name,32,"wiccablk%c",which+'a');
  122.     set_capacity(dev->gd,nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));
  123.     printk("wiccablk add_disk %s\n",dev->gd->disk_name);
  124.     add_disk(dev->gd);
  125. }

  126. staticint__init init_wiccablk(void)
  127. {
  128.     inti;

  129.     wiccablk_major=register_blkdev(0,"wiccablk");
  130.     if(wiccablk_major<=0){
  131.         printk("wiccablk register_blkdev failed\n");
  132.         return-EBUSY;
  133.     }

  134.     Devices=kmalloc(ndevices*sizeof(struct wiccablk_dev),GFP_KERNEL);
  135.     if(Devices==NULL){
  136.         goto out_unregister;
  137.     }

  138.     for(i=0;i
  139.         printk("wiccablk setup_device %d\n",i);
  140.         setup_device(Devices+i,i);
  141.     }

  142.     printk("test_wiccablk init ok\n");

  143.     return 0;/*everythingisok*/

  144. out_unregister:
  145.     unregister_blkdev(wiccablk_major,"wiccablk");
  146.     return-ENOMEM;
  147. }

  148. static void __exit cleanup_wiccablk(void)
  149. {
  150.     inti;

  151.     for(i=0;i
  152.         struct wiccablk_dev*dev=Devices+i;

  153.         blk_cleanup_queue(dev->gd->queue);
  154.         del_gendisk(dev->gd);
  155.         vfree(dev->data);
  156.     }

  157.     unregister_blkdev(wiccablk_major,"wiccablk");
  158.     kfree(Devices);

  159.     printk("test_wiccablk exit ok\n");
  160. }

  161. module_init(init_wiccablk);
  162. module_exit(cleanup_wiccablk);

  163. MODULE_LICENSE("GPL");
  164. MODULE_AUTHOR("Marco Hao");

本文乃fireaxe原创,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,并注明原作者及原链接。内容可任意使用,但对因使用该内容引起的后果不做任何保证。

作者:fireaxe.hq@outlook.com
博客:fireaxe.blog.chinaunix.net 


阅读(11) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
请登录后评论。

登录 注册