分享

Linux块设备驱动程序原理

 microee 2015-07-05

1.4  块设备驱动程序

1.4.1  Linux块设备驱动程序原理(1)

顾名思义,块设备驱动程序就是支持以块的方式进行读写的设备。块设备和字符设备最大的区别在于读写数据的基本单元不同。块设备读写数据的基本单元为块,例如磁盘通常为一个sector,而字符设备的基本单元为字节。从实现角度来看,字符设备的实现比较简单,内核例程和用户态API一一对应,这种映射关系由字符设备的file_operations维护。块设备接口则相对复杂,读写API没有直接到块设备层,而是直接到文件系统层,然后再由文件系统层发起读写请求。

block_device结构代表了内核中的一个块设备。它可以表示整个磁盘或一个特定的分区。当这个结构代表一个分区时,它的bd_contains成员指向包含这个分区的设备,bd_part成员指向设备的分区结构。当这个结构代表一个块设备时,bd_disk成员指向设备的gendisk结构。

  1. struct block_device {  
  2.     dev_t           bd_dev;  
  3.     struct inode *  bd_inode;   /*分区结点*/  
  4.     int         bd_openers;  
  5.     struct semaphore    bd_sem; /*打开/关闭锁*/  
  6.     struct semaphore    bd_mount_sem;   /* 加载互斥锁*/  
  7.     struct list_head    bd_inodes;  
  8.     void *      bd_holder;  
  9.     int         bd_holders;  
  10.     struct block_device *   bd_contains;  
  11.     unsigned        bd_block_size;//分区块大小  
  12.     struct hd_struct *  bd_part;  
  13.     unsigned        bd_part_count;//打开次数  
  14.     int         bd_invalidated;  
  15.     struct gendisk *    bd_disk;  
  16.     struct list_head    bd_list;  
  17.     struct backing_dev_info *bd_inode_backing_dev_info;  
  18.     unsigned long   bd_private;  
  19. };  

gendisk是一个单独的磁盘驱动器的内核表示。内核还使用gendisk来表示分区。
  1. struct gendisk {  
  2.     int major;          //主设备号  
  3.     int first_minor;     
  4.     int minors;         //最大的次设备号数量,如果设备不能分区,该值为1                                   
  5.     char disk_name[32]; //主设备名  
  6.     struct hd_struct **part;    //分区信息,有minors个  
  7.     struct block_device_operations *fops;//设备操作  
  8.     struct request_queue *queue;    //设备管理I/O请求  
  9.     void *private_data;  
  10.     sector_t capacity;  
  11.     int flags;  
  12.     char devfs_name[64];  
  13.     int number;  
  14.     struct device *driverfs_dev;  
  15.     struct kobject kobj;  
  16.     struct timer_rand_state *random;  
  17.     int policy;  
  18.     atomic_t sync_io;     
  19.     unsigned long stamp, stamp_idle;  
  20.     int in_flight;  
  21. #ifdef  CONFIG_SMP  
  22.     struct disk_stats *dkstats;  
  23. #else  
  24.     struct disk_stats dkstats;  
  25. #endif  
  26. };  

gendisk结构的操作函数包括以下几个:

  1. struct gendisk *alloc_disk(int minors);     //分配磁盘  
  2. void add_disk(struct gendisk *disk);        //增加磁盘信息  
  3. void unlink_gendisk(struct gendisk *disk)   //删除磁盘信息  
  4. void delete_partition(struct gendisk *disk, int part);  //删除分区  
  5. void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len, int flags);//添加分区  
  6. 1.4.1  Linux块设备驱动程序原理(2)

    block_device_operations结构是块设备对应的操作接口,是连接抽象的块设备操作与具体块设备操作之间的枢纽。

    1. struct block_device_operations {  
    2.     int (*open) (struct inode *, struct file *);  
    3.     int (*release) (struct inode *, struct file *);  
    4.     int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long);  
    5.     long (*unlocked_ioctl) (struct file *, unsigned, unsigned long);  
    6.     long (*compat_ioctl) (struct file *, unsigned, unsigned long);  
    7.     int (*direct_access) (struct block_device *, sector_t, unsigned long *);  
    8.     int (*media_changed) (struct gendisk *);  
    9.     int (*revalidate_disk) (struct gendisk *);  
    10.     int (*getgeo)(struct block_device *, struct hd_geometry *);  
    11.     struct module *owner;  
    12. };  

    block_device_operations并不能完全提供文件操作全部的API,实际上只提供了open、release等函数,其他的文件操作依赖于def_blk_fops:
    1. const struct file_operations def_blk_fops = {  
    2.     .open   = blkdev_open,  
    3.     .release    = blkdev_close,  
    4.     .llseek = block_llseek,  
    5.     .read       = do_sync_read,  
    6.     .write  = do_sync_write,  
    7.     .aio_read   = generic_file_aio_read,  
    8.     .aio_writegeneric_file_aio_write_nolock,  
    9.     .mmap   = generic_file_mmap,  
    10.     .fsync  = block_fsync,  
    11.     .unlocked_ioctl = block_ioctl,  
    12. #ifdef CONFIG_COMPAT  
    13.     .compat_ioctl   = compat_blkdev_ioctl,  
    14. #endif  
    15.     .splice_read        = generic_file_splice_read,  
    16.     .splice_write   = generic_file_splice_write,  
    17. };  

    系统对块设备进行读写操作时,通过块设备通用的读写操作函数将一个请求保存在该设备的操作请求队列(request queue)中,然后调用这个块设备的底层处理函数,对请求队列中的操作请求进行逐一执行。request_queue结构描述了块设备的请求队列,该结构定义如下:
    1. struct request_queue  
    2. {  
    3.     struct list_head    queue_head;  
    4.     struct request      *last_merge;  
    5.     elevator_t      elevator;  
    6.     /*请求队列列表*/  
    7.     struct request_list     rq;  
    8.     request_fn_proc     *request_fn;  
    9.     merge_request_fn    *back_merge_fn;  
    10.     merge_request_fn    *front_merge_fn;  
    11.     merge_requests_fn   *merge_requests_fn;  
    12.     make_request_fn     *make_request_fn;  
    13.     prep_rq_fn          *prep_rq_fn;  
    14.     unplug_fn           *unplug_fn;  
    15.     merge_bvec_fn       *merge_bvec_fn;  
    16.     activity_fn         *activity_fn;  
    17.     /*自动卸载状态*/  
    18.     struct timer_list   unplug_timer;  
    19.     int         unplug_thresh;    
    20.     unsigned long       unplug_delay;   /*自动卸载延时*/  
    21.     struct work_struct  unplug_work;  
    22.     struct backing_dev_info backing_dev_info;  
    23.     void                *queuedata;  
    24.     void                *activity_data;  
    25.     unsigned long       bounce_pfn;  
    26.     int             bounce_gfp;  
    27.     unsigned long       queue_flags;//各种队列标志  
    28.     /*保护队列结构,避免重入*/  
    29.     spinlock_t          *queue_lock;  
    30.     /* 请求的核心结构*/  
    31.     struct kobject kobj;  
    32.     /*请求的配置*/  
    33.     unsigned long       nr_requests;    /* 请求的最大数*/  
    34.     unsigned int        nr_congestion_on;  
    35.     unsigned int        nr_congestion_off;  
    36.     unsigned short      max_sectors;  
    37.     unsigned short      max_phys_segments;  
    38.     unsigned short      max_hw_segments;  
    39.     unsigned short      hardsect_size;  
    40.     unsigned int        max_segment_size;  
    41.     unsigned long       seg_boundary_mask;  
    42.     unsigned int        dma_alignment;  
    43.     struct blk_queue_tag    *queue_tags;  
    44.     atomic_t        refcnt;  
    45.     unsigned int        in_flight;  
    46.     /*sg 参数配置*/  
    47.     unsigned int        sg_timeout;  
    48.     unsigned int        sg_reserved_size;  
    49. };  

    请求队列相关的处理函数包括:
    1. //创建队列时提供了一个自旋锁。  
    2. request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock);  
    3. //获得队列中第一个未完成的请求。  
    4. struct request *elv_next_request(request_queue_t *q);  
    5. void end_request(struct request *req, int uptodate);//请求完成  
    6. void blk_stop_queue(request_queue_t *queue); //停止请求  
    7. void blk_start_queue(request_queue_t *queue); //开始请求  
    8. void blk_cleanup_queue(request_queue_t *);//清除请求队列

    1.4.2  简单的块设备驱动程序实例

    向内核注册和注销一个块设备可使用如下函数:

    1. int register_blkdev(unsigned int major, const char *name);  
    2. int unregister_blkdev(unsigned int major, const char *name); 

    例1.10  简单的块设备驱动程序实例

    代码见光盘\src\1drivermodel\1-10block。核心代码如下所示:

    1. static struct request_queue *Queue;  
    2. //自定义块设备结构  
    3. static struct simpleblockdevice   
    4. {  
    5.     unsigned long size;  
    6.     spinlock_t lock;  
    7.     u8 *data;  
    8.     struct gendisk *gd;  
    9. } Device;  
    10. //处理I/O请求  
    11. static void simpleblocktransfer(struct simpleblockdevice *dev, unsigned long sector,  
    12.                                 unsigned long nsect, char *buffer, int write)  
    13. {  
    14.     unsigned long offset = sector*hardsect_size;  
    15.     unsigned long nbytes = nsect*hardsect_size;  
    16.     //判断I/O请求是否超出范围  
    17.     if ((offset + nbytes) > dev->size)  
    18.     {  
    19.         printk (KERN_NOTICE "sbd: Beyond-end write (%ld %ld)\n", offset, nbytes);  
    20.         return;  
    21.     }  
    22.     if (write)  
    23.         memcpy(dev->data + offset, buffer, nbytes);  
    24.     else  
    25.         memcpy(buffer, dev->data + offset, nbytes);  
    26. }  
    27. //简单请求处理  
    28. static void simpleblockrequest(struct request_queue *q)  
    29. {  
    30.     struct request *req;  
    31.     //获取下一个请求  
    32.     while ((req = elv_next_request(q)) != NULL)   
    33.     {  
    34.         if (! blk_fs_request(req))   
    35.         {  
    36.             printk (KERN_NOTICE "Skip non-CMD request\n");  
    37.             end_request(req, 0);  
    38.             continue;  
    39.         }  
    40.         simpleblocktransfer(&Device, req->sector, req->current_nr_sectors,  
    41.             req->buffer, rq_data_dir(req));  
    42.         end_request(req, 1);  
    43.     }  
    44. }  
    45. //简单的块设备ioctl函数  
    46. int simpleblockioctl (struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg)  
    47. {  
    48.     long size;  
    49.     struct hd_geometry geo;  
    50.     switch(cmd)   
    51.     {  
    52.     //获取磁盘信息  
    53.     case HDIO_GETGEO:  
    54.         size = Device.size*(hardsect_size/KERNEL_SECTOR_SIZE);  
    55.         geo.cylinders = (size & ~0x3f) >> 6;  
    56.         geo.heads = 4;  
    57.         geo.sectors = 16;  
    58.         geo.start = 4;  
    59.         if (copy_to_user((void *) arg, &geo, sizeof(geo)))  
    60.             return -EFAULT;  
    61.         return 0;  
    62.     }  
    63.     return -ENOTTY; /* 未知命令 */  
    64. }  
    65. //设备操作结构  
    66. static struct block_device_operations simpleblockops = {  
    67.     .owner           = THIS_MODULE,  
    68.     .ioctl       = simpleblockioctl 
    69. };  
    70. static int __init simpleblockinit(void)  
    71. {  
    72.     Device.size = nsectors*hardsect_size;  
    73.     spin_lock_init(&Device.lock);  
    74.     Device.data = vmalloc(Device.size);  
    75.     if (Device.data == NULL)  
    76.         return -ENOMEM;  
    77.     //初始化请求队列,配置处理函数为sbd_request  
    78.     Queue = blk_init_queue(simpleblockrequest, &Device.lock);  
    79.     if (Queue == NULL)  
    80.         goto out;  
    81.     blk_queue_hardsect_size(Queue, hardsect_size);  
    82.     //注册块设备  
    83.     major_num = register_blkdev(major_num, "sbd");  
    84.     if (major_num <= 0) {  
    85.         printk(KERN_WARNING "sbd: unable to get major number\n");  
    86.         goto out;  
    87.     }  
    88.     Device.gd = alloc_disk(16);  
    89.     if (! Device.gd)  
    90.         goto out_unregister;  
    91.     Device.gd->major = major_num;  
    92.     Device.gd->first_minor = 0;  
    93.     Device.gd->fops = &simpleblockops;  
    94.     Device.gd->private_data = &Device;  
    95.     strcpy (Device.gd->disk_name, "sbd0");  
    96.     //配置容量  
    97.     set_capacity(Device.gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));  
    98.     Device.gd->queue = Queue;  
    99.     add_disk(Device.gd);  
    100.     return 0;  
    101. out_unregister:  
    102.     unregister_blkdev(major_num, "sbd");  
    103. out:  
    104.     vfree(Device.data);  
    105.     return -ENOMEM;  
    106. }  
    107. static void __exit simpleblockexit(void)  
    108. {  
    109.     del_gendisk(Device.gd);  
    110.     put_disk(Device.gd);  
    111.     unregister_blkdev(major_num, "sbd");  
    112.     blk_cleanup_queue(Queue);  
    113.     vfree(Device.data);  
    114. }  
    115. module_init(simpleblockinit);  
    116. module_exit(simpleblockexit);  

    运行结果如下:
    1. [root@/home]#cat /proc/filesystems  
    2. nodev   sysfs  
    3. nodev   rootfs  
    4. nodev   bdev  
    5. nodev   proc  
    6. nodev   binfmt_misc  
    7. nodev   debugfs  
    8. nodev   securityfs  
    9. nodev   sockfs  
    10. nodev   usbfs  
    11. nodev   pipefs  
    12. nodev   anon_inodefs  
    13. nodev   futexfs  
    14. nodev   tmpfs  
    15. nodev   inotifyfs  
    16.         ext3  
    17.         cramfs  
    18. nodev   ramfs  
    19.         msdos  
    20.         vfat  
    21.         iso9660  
    22. nodev   nfs  
    23. nodev   nfs4  
    24. nodev   mqueue  
    25. nodev   rpc_pipefs  
    26. [root@/home]#insmod demo.ko  
    27.  sbd0: unknown partition table  
    28. [root@/home]#mknod /dev/sbd b 253 0  
    29. [root@/home]#./mkfs.ext3 /dev/sbd  
    30. mke2fs 1.40.9 (27-Apr-2008)  
    31. Filesystem label=  
    32. OS type: Linux  
    33. Block size=1024 (log=0)  
    34. Fragment size=1024 (log=0)  
    35. 1280 inodes, 5120 blocks  
    36. 256 blocks (5.00%) reserved for the super user  
    37. First data block=1 
    38. Maximum filesystem blocks=5242880 
    39. 1 block group  
    40. 8192 blocks per group, 8192 fragments per group  
    41. 1280 inodes per group  
    42.  
    43. Writing inode tables: done  
    44. Creating journal (1024 blocks): done  
    45. Writing superblocks and filesystem accounting information: done  
    46.  
    47. This filesystem will be automatically checked every 39 mounts or  
    48. 180 days, whichever comes first.  Use tune2fs -c or -i to override.  
    49. [root@/home]#mount -t ext3 /dev/sbd /mnt/u  
    50. kjournald starting.  Commit interval 5 seconds  
    51. EXT3 FS on sbd0, internal journal  
    52. EXT3-fs: mounted filesystem with ordered data mode.  
    53. [root@/home]#df  
    54. Filesystem           1k-blocks      Used Available Use% Mounted on  
    55. rootfs                 2063504   1191136    767548  61% /  
    56. /dev/root              2063504   1191136    767548  61% /  
    57. /dev/sbd                  4955      1063      3636  23% /mnt/u  
    58. [root@/home]#cd /mnt/u  
    59. [root@/mnt/u]#ls  
    60. lost+found  

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多