Chinaunix首页 | 论坛 | 博客
  • 博客访问: 351580
  • 博文数量: 159
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 182
  • 用 户 组: 普通用户
  • 注册时间: 2013-11-02 10:42
文章分类

全部博文(159)

文章存档

2015年(18)

2014年(132)

2013年(9)

分类: LINUX

2014-06-02 16:09:44

    Linux驱动编写(块设备驱动代码)

    按照ldd的说法,linux的设备驱动包括了char,block,net三种设备。char设备是比较简单的,
只要分配了major、minor号,就可以进行读写处理了。相对而言,block和net要稍微复杂些。net设备
姑且按下不谈,我们在以后的博文中会有涉及。今天,我们可以看看一个简单的block是怎么设计的。


    为了将block和fs分开,kernel的设计者定义了request queue这一种形式。换一句话说,所有
fs对block设备的请求,最终都会转变为request的形式。所以,对于block设备驱动开发的朋友来说,
处理好了request queue就掌握了block设备的一半。当然,block设备很多,hd、floppy、ram都可
以这么来定义,有兴趣的朋友可以在drivers/block寻找相关的代码来阅读。兴趣没有那么强的同学,可
以看看我们这篇博文,基本上也能学个大概。有个基本的概念,再加上一个简单浅显的范例,对于一般的朋
友来说,已经足够了。

    硬盘基本知识(磁道、扇区、柱面、磁头数、簇、MBR、
再讨论Ramdisk代码驱动前先了解一下Ramdisk:
1 什么是Ramdisk

       Ramdisk是一种模拟磁盘,其数据实际上是存储在RAM中,它使用一部分内存空间来模拟出一个磁
盘设备,并以块设备的方式来组织和访问这片内存。对于用户来说可以把Ramdisk与通常的硬盘分区同等对
待来使用。那些经常被访问、并且不会被更改的文件,可以通过Ramdisk被存放在内存中,这样能够明显地
提高系统的响应性能。

2  Ramdisk的产生过程

    近几年来,计算机的CPU、内存和显卡等主要配件的性能都提升得很快,而与之相对应的磁盘系统
性能正越来越严重地成为整个电脑系统性能提升的瓶颈。虽然磁盘外部接口也从以前的ATA33发展到今天的
SATA 6Gbit/s。但是,这还是不能彻底解决磁盘瓶颈的问题,特别是在运行一些对数据存取速度要求很高
的程序,如数字影像处理或玩3D游戏装入纹理数据时,受磁盘存取速度的影响,屏幕画面时常会出现延迟和停
顿。于是,虚拟磁盘技术(Ramdisk)应运而生,它可解上述问题的“燃眉之急”。

3  Ramdisk的特点

    Ramdisk是基于内存的块设备,以内存作为实际的存储介质,但以块设备的方式组织,所以它具有
比实际磁盘更快的存取速度。但这也带来了另一个问题,当系统重启,内存掉电后,Ramdisk中存储的数据也
将会随之消失。所以Ramdisk不适合作为长期保存文件的介质。

3  Ramdisk的特点

    Ramdisk是基于内存的块设备,以内存作为实际的存储介质,但以块设备的方式组织,所以它具有
比实际磁盘更快的存取速度。但这也带来了另一个问题,当系统重启,内存掉电后,Ramdisk中存储的数据也
将会随之消失。所以Ramdisk不适合作为长期保存文件的介质。

4  Ramdisk的作用

     Ramdisk磁盘对于保存加密数据来说是一个福音,因为我们如果将加密的文件解密到普通的磁盘的话,即使
我们随后删除了解密文件,数据仍然会留在磁盘上。这样是非常不安全的。而对于Ramdiak来说,就不存在这
样的问题。另外,假设有几个文件要频繁的使用,你如果将它们加到内存当中,程序运行速度会大幅提高,这
是由存储介质的特性决定的(因为内存的读写速度远高于硬盘)。像Web服务器这样的计算机,需要大量的读
取和交换特定的文件,因此,在Web服务器上建立Ramdisk会大大提高网络读取的速度。


闲话不多说,我们看看一个ramdisk代码驱动是怎么写的,代码来自《深入linux 设备驱动程序内核机制》,

点击(此处)折叠或打开

  1. #include <linux/module.h>
  2. #include <linux/kernel.h>
  3. #include <linux/init.h>

  4. #include <linux/fs.h>
  5. #include <linux/types.h>
  6. #include <linux/fcntl.h>
  7. #include <linux/vmalloc.h>
  8. #include <linux/blkdev.h>
  9. #include <linux/hdreg.h>

  10. #define RAMHD_NAME "ramhd"
  11. #define RAMHD_MAX_DEVICE 2
  12. #define RAMHD_MAX_PARTITIONS 4

  13. #define RAMHD_SECTOR_SIZE 512
  14. #define RAMHD_SECTORS 16
  15. #define RAMHD_HEADS 4
  16. #define RAMHD_CYLINDERS 256

  17. #define RAMHD_SECTOR_TOTAL (RAMHD_SECTORS * RAMHD_HEADS *RAMHD_CYLINDERS)
  18. #define RAMHD_SIZE (RAMHD_SECTOR_SIZE * RAMHD_SECTOR_TOTAL) //8mb

  19. typedef struct {
  20.     unsigned char* data;
  21.     struct request_queue* queue;
  22.     struct gendisk* gd;
  23. }RAMHD_DEV;

  24. static char* sdisk[RAMHD_MAX_DEVICE] = {NULL};
  25. static RAMHD_DEV* rdev[RAMHD_MAX_DEVICE] = {NULL};

  26. static dev_t ramhd_major;

  27. static int ramhd_space_init(void)
  28. {
  29.     int i;
  30.     int err = 0;
  31.     for(i = 0; i < RAMHD_MAX_DEVICE; i++){
  32.         sdisk[i] = vmalloc(RAMHD_SIZE);
  33.         if(!sdisk[i]){
  34.             err = -ENOMEM;
  35.             return err;
  36.         }
  37.         
  38.         memset(sdisk[i], 0, RAMHD_SIZE);
  39.     }
  40.     
  41.     return err;
  42. }

  43. static void ramhd_space_clean(void)
  44. {
  45.     int i;
  46.     for(i = 0; i < RAMHD_MAX_DEVICE; i++){
  47.         vfree(sdisk[i]);
  48.     }
  49. }

  50. static int ramhd_open(struct block_device* bdev, fmode_t mode)
  51. {
  52.     return 0;
  53. }

  54. static int ramhd_release(struct gendisk*gd, fmode_t mode)
  55. {
  56.     return 0;
  57. }

  58. static int ramhd_ioctl(struct block_device* bdev, fmode_t mode, unsigned int cmd, unsigned long arg)
  59. {
  60.     int err;
  61.     struct hd_geometry geo;
  62.     
  63.     switch(cmd)
  64.     {
  65.         case HDIO_GETGEO:
  66.             err = !access_ok(VERIFY_WRITE, arg, sizeof(geo));
  67.             if(err)
  68.                 return -EFAULT;
  69.                 
  70.             geo.cylinders = RAMHD_CYLINDERS;
  71.             geo.heads = RAMHD_HEADS;
  72.             geo.sectors = RAMHD_SECTORS;
  73.             geo.start = get_start_sect(bdev);
  74.             
  75.             if(copy_to_user((void*)arg, &geo, sizeof(geo)))
  76.                 return -EFAULT;
  77.             
  78.             return 0;
  79.     }
  80.     
  81.     return -ENOTTY;
  82. }

  83. static struct block_device_operations ramhd_fops = {
  84.     .owner = THIS_MODULE,
  85.     .open = ramhd_open,
  86.     .release = ramhd_release,
  87.     .ioctl = ramhd_ioctl,
  88. };

  89. static int ramhd_make_request(struct request_queue* q, struct bio* bio)
  90. {
  91.     char* pRHdata;
  92.     char* pBuffer;
  93.     struct bio_vec* bvec;
  94.     int i;
  95.     int err = 0;
  96.     
  97.     struct block_device* bdev = bio->bi_bdev;
  98.     RAMHD_DEV* pdev = bdev->bd_disk->private_data;
  99.     
  100.     if(((bio->bi_sector * RAMHD_SECTOR_SIZE) + bio->bi_size) > RAMHD_SIZE){
  101.         err = -EIO;
  102.         return err;
  103.     }
  104.     
  105.     pRHdata = pdev->data + (bio->bi_sector * RAMHD_SECTOR_SIZE);
  106.     bio_for_each_segment(bvec, bio, i){
  107.         pBuffer = kmap(bvec->bv_page) + bvec->bv_offset;
  108.         switch(bio_data_dir(bio)){
  109.             case READ:
  110.                 memcpy(pBuffer, pRHdata, bvec->bv_len);
  111.                 flush_dcache_page(bvec->bv_page);
  112.                 break;
  113.                 
  114.             case WRITE:
  115.                 flush_dcache_page(bvec->bv_page);
  116.                 memcpy(pRHdata, pBuffer, bvec->bv_len);
  117.                 break;
  118.                 
  119.             default:
  120.                 kunmap(bvec->bv_page);
  121.                 goto out;
  122.         }
  123.         
  124.         kunmap(bvec->bv_page);
  125.         pRHdata += bvec->bv_len;
  126.     }
  127.     
  128. out:
  129.     bio_endio(bio, err);
  130.     return 0;
  131. }

  132. static int alloc_ramdev(void)
  133. {
  134.     int i;
  135.     for(i = 0; i < RAMHD_MAX_DEVICE; i++){
  136.         rdev[i] = kzalloc(sizeof(RAMHD_DEV), GFP_KERNEL);
  137.         if(!rdev[i]){
  138.             return -ENOMEM;
  139.         }
  140.     }
  141.     
  142.     return 0;
  143. }

  144. static void clean_ramdev(void)
  145. {
  146.     int i;
  147.     
  148.     for(i = 0; i < RAMHD_MAX_DEVICE; i++){
  149.         if(rdev[i])
  150.             kfree(rdev[i]);
  151.     }
  152. }

  153. static int __init ramhd_init(void)
  154. {
  155.     int i;
  156.     
  157.     ramhd_space_init();
  158.     alloc_ramdev();
  159.     
  160.     ramhd_major = register_blkdev(0, RAMHD_NAME);
  161.     
  162.     for(i = 0; i < RAMHD_MAX_DEVICE; i++){
  163.         rdev[i]->data = sdisk[i];
  164.         rdev[i]->queue = blk_alloc_queue(GFP_KERNEL);
  165.         blk_queue_make_request(rdev[i]->queue, ramhd_make_request);
  166.         
  167.         rdev[i]->gd = alloc_disk(RAMHD_MAX_PARTITIONS);
  168.         rdev[i]->gd->major = ramhd_major;
  169.         rdev[i]->gd->first_minor = i * RAMHD_MAX_PARTITIONS;
  170.         rdev[i]->gd->fops = &ramhd_fops;
  171.         rdev[i]->gd->queue = rdev[i]->queue;
  172.         rdev[i]->gd->private_data = rdev[i];
  173.         sprintf(rdev[i]->gd->disk_name, "ramhd%c", 'a' +i);
  174.         rdev[i]->gd->flags |= GENHD_FL_SUPPRESS_PARTITION_INFO;
  175.         set_capacity(rdev[i]->gd, RAMHD_SECTOR_TOTAL);
  176.         add_disk(rdev[i]->gd);
  177.     }
  178.     
  179.     return 0;
  180. }

  181. static void __exit ramhd_exit(void)
  182. {
  183.     int i;
  184.     for(i = 0; i < RAMHD_MAX_DEVICE; i++){
  185.         del_gendisk(rdev[i]->gd);
  186.         put_disk(rdev[i]->gd);
  187.         blk_cleanup_queue(rdev[i]->queue);
  188.     }
  189.     
  190.     clean_ramdev();
  191.     ramhd_space_clean();
  192.     unregister_blkdev(ramhd_major, RAMHD_NAME);
  193. }

  194. module_init(ramhd_init);
  195. module_exit(ramhd_exit);

  196. MODULE_AUTHOR("laizibi1220@gmail.com");
  197. MODULE_DESCRIPTION("The ramdisk implementation with request function");
  198. MODULE_LICENSE("GPL");
为了大家方便,顺便也把Makefile放出来,这其实很简单。

点击(此处)折叠或打开

  1. ifneq ($(KERNELRELEASE),)
  2. obj-m := ramdisk.o # 产生ramdisk 模块的目标文件
  3. else
  4. PWD := $(shell pwd) #模块所在的当前路径
  5. LINUX_KERNEL := $(shell uname -r) #Linux内核源代码的当前版本
  6. LINUX_KERNEL_PATH := /usr/src/kernels/$(LINUX_KERNEL)/ #Linux内核源代码的绝对路径
  7. all:
  8. $(MAKE) -C $(LINUX_KERNEL_PATH) M=$(PWD) modules
  9. clean:
  10. rm -rf *.o *.mod.c *.ko *.ko.* modules.* Module.*
  11. endif
这段代码究竟有没有用呢?可以按照下面的步骤来做,

    a)make 一下,生成ramdisk.ko;

    b)编译好了之后,就可以安装驱动了,在linux下是这么做的,sudo insmod ramdisk.ko;

    c)安装好了,利用ls /dev/ramhd*, 就会发现在/dev下新增两个结点,即/dev/ramhda和/dev/ramhdb;

    d)不妨选择其中一个节点进行分区处理, sudo fdisk /dev/ramhda,简单处理的话就建立一个分区, 生成/dev/ramhda1;

    e)创建文件系统,sudo mkfs.ext3 /dev/ramhda1;

    f)有了上面的文件系统,就可以进行mount处理,不妨sudo mount /dev/ramhda1 /mnt;

    g)上面都弄好了,大家就可以copy、delete文件试试了,是不是很简单。
具体操作步骤如下:

点击(此处)折叠或打开

  1. [root@localhost Desktop]# make
  2. make -C /usr/src/kernels/2.6.32-279.el6.i686 / M=/root/Desktop modules
  3. make[1]: Entering directory `/usr/src/kernels/2.6.32-279.el6.i686'
  4. LD /root/Desktop/built-in.o
  5. CC [M] /root/Desktop/ramdisk.o
  6. Building modules, stage 2.
  7. MODPOST 1 modules
  8. CC /root/Desktop/ramdisk.mod.o
  9. LD [M] /root/Desktop/ramdisk.ko.unsigned
  10. NO SIGN [M] /root/Desktop/ramdisk.ko
  11. make[1]: Leaving directory `/usr/src/kernels/2.6.32-279.el6.i686'
  12. [root@localhost Desktop]# insmod ramdisk.ko
  13. [root@localhost Desktop]# ls /dev/ramhd*
  14. /dev/ramhda /dev/ramhdb
  15. [root@localhost Desktop]# fdisk /dev/ramhda
  16. Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabel
  17. Building a new DOS disklabel with disk identifier 0x73569d74.
  18. Changes will remain in memory only, until you decide to write them.
  19. After that, of course, the previous content won't be recoverable.

  20. Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite)

  21. WARNING: DOS-compatible mode is deprecated. It's strongly recommended to
  22. switch off the mode (command 'c') and change display units to
  23. sectors (command 'u').

  24. Command (m for help): m
  25. Command action
  26. a toggle a bootable flag
  27. b edit bsd disklabel
  28. c toggle the dos compatibility flag
  29. d delete a partition
  30. l list known partition types
  31. m print this menu
  32. n add a new partition
  33. o create a new empty DOS partition table
  34. p print the partition table
  35. q quit without saving changes
  36. s create a new empty Sun disklabel
  37. t change a partition's system id
  38. u change display/entry units
  39. v verify the partition table
  40. w write table to disk and exit
  41. x extra functionality (experts only)

  42. Command (m for help): p

  43. Disk /dev/ramhda: 8 MB, 8388608 bytes
  44. 255 heads, 63 sectors/track, 1 cylinders
  45. Units = cylinders of 16065 * 512 = 8225280 bytes
  46. Sector size (logical/physical): 512 bytes / 512 bytes
  47. I/O size (minimum/optimal): 512 bytes / 512 bytes
  48. Disk identifier: 0x73569d74

  49. Device Boot Start End Blocks Id System

  50. Command (m for help): n
  51. Command action
  52. e extended
  53. p primary partition (1-4)
  54. p
  55. Partition number (1-4): 1
  56. First cylinder (1-1, default 1): 1

  57. Command (m for help): p

  58. Disk /dev/ramhda: 8 MB, 8388608 bytes
  59. 255 heads, 63 sectors/track, 1 cylinders
  60. Units = cylinders of 16065 * 512 = 8225280 bytes
  61. Sector size (logical/physical): 512 bytes / 512 bytes
  62. I/O size (minimum/optimal): 512 bytes / 512 bytes
  63. Disk identifier: 0x73569d74

  64. Device Boot Start End Blocks Id System
  65. /dev/ramhda1 1 1 8001 83 Linux

  66. Command (m for help): w
  67. The partition table has been altered!

  68. Calling ioctl() to re-read partition table.
  69. Syncing disks.
  70. [root@localhost Desktop]# mkfs.ext3 /dev/ramhda1
  71. mke2fs 1.41.12 (17-May-2010)
  72. 文件系统标签=
  73. 操作系统:Linux
  74. 块大小=1024 (log=0)
  75. 分块大小=1024 (log=0)
  76. Stride=0 blocks, Stripe width=0 blocks
  77. 2000 inodes, 8000 blocks
  78. 400 blocks (5.00%) reserved for the super user
  79. 第一个数据块=1
  80. Maximum filesystem blocks=8388608
  81. 1 block group
  82. 8192 blocks per group, 8192 fragments per group
  83. 2000 inodes per group

  84. 正在写入inode表: 完成
  85. Creating journal (1024 blocks): 完成
  86. Writing superblocks and filesystem accounting information: 完成

  87. This filesystem will be automatically checked every 33 mounts or
  88. 180 days, whichever comes first. Use tune2fs -c or -i to override.
  89. [root@localhost Desktop]# mount /dev/ramhda1 /mnt
  90. [root@localhost Desktop]# cd /mnt
  91. [root@localhost mnt]# ll
  92. 总用量 12
  93. drwx------. 2 root root 12288 5月 30 15:29 lost+found
  94. [root@localhost mnt]# gedit hello.c
  95. [root@localhost mnt]# gcc -o hello  hello.c
  96. [root@localhost mnt]# ./hello
  97. Hello World!
  98. [root@localhost mnt]#


阅读(961) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~