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_write= generic_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. Writing inode tables: done
    43. Creating journal (1024 blocks): done
    44. Writing superblocks and filesystem accounting information: done
    45. This filesystem will be automatically checked every 39 mounts or
    46. 180 days, whichever comes first.  Use tune2fs -c or -i to override.
    47. [root@/home]#mount -t ext3 /dev/sbd /mnt/u
    48. kjournald starting.  Commit interval 5 seconds
    49. EXT3 FS on sbd0, internal journal
    50. EXT3-fs: mounted filesystem with ordered data mode.
    51. [root@/home]#df
    52. Filesystem           1k-blocks      Used Available Use% Mounted on
    53. rootfs                 2063504   1191136    767548  61% /
    54. /dev/root              2063504   1191136    767548  61% /
    55. /dev/sbd                  4955      1063      3636  23% /mnt/u
    56. [root@/home]#cd /mnt/u
    57. [root@/mnt/u]#ls
    58. lost+found

(linux)块设备驱动程序的更多相关文章

  1. 简单linux块设备驱动程序

    本文代码参考<LINUX设备驱动程序>第十六章 块设备驱动程序 本文中的“块设备”是一段大小为PAGE_SIZE的内存空间(两个扇区,每个扇区512字节) 功能:向块设备中输入内容,从块设 ...

  2. 《Linux Device Drivers》第十六章 块设备驱动程序——note

    基本介绍 块设备驱动程序通过主传动固定大小数据的随机访问设备 Linux核心Visual块设备作为基本设备和不同的字符设备类型 Linux块设备驱动程序接口,使块设备最大限度地发挥其效用.一个问题 一 ...

  3. Linux中块设备驱动程序分析

    基于<Linux设备驱动程序>书中的sbull程序以对Linux块设备驱动总结分析. 開始之前先来了解这个块设备中的核心数据结构: struct sbull_dev {         i ...

  4. 深入理解Linux内核-块设备驱动程序

    扇区: 1.硬盘控制器将磁盘看成一大组扇区2.扇区就是一组相邻字节3.扇区按照惯例大小设置位512字节4.存放在块设备中的数据是通过它们在磁盘上的位置来标识,即首个扇区的下标和扇区的数目.5.扇区是硬 ...

  5. linux系统下块设备驱动程序

    顾名思义,块设备驱动程序就是支持以块的方式进行读写的设备.块设备和字符设备最大的区别在于读写数据的基本单元不同.块设备读写数据的基本单元为块,例 如磁盘通常为一个sector,而字符设备的基本单元为字 ...

  6. 嵌入式Linux驱动学习之路(二十一)字符设备驱动程序总结和块设备驱动程序的引入

    字符设备驱动程序 应用程序是调用C库中的open read write等函数.而为了操作硬件,所以引入了驱动模块. 构建一个简单的驱动,有一下步骤. 1. 创建file_operations 2. 申 ...

  7. Linux 0.11源码阅读笔记-块设备驱动程序

    块设备驱动程序 块设备驱动程序负责实现对块设备数据的读写功能.内核代码统一使用缓冲块间接和块设备(如磁盘)交换数据,缓冲区数据通过块设备驱动程序和块设备交换数据. 块设备的管理 块设备表 内核通过一张 ...

  8. Linux 块设备驱动 (一)

    1.块设备的I/O操作特点 字符设备与块设备的区别: 块设备只能以块为单位接受输入和返回输出,而字符设备则以字符为单位. 块设备对于I/O请求有对应的缓冲区,因此它们可以选择以什么顺序进行响应,字符设 ...

  9. Linux块设备驱动(一) _驱动模型

    块设备是Linux三大设备之一,其驱动模型主要针对磁盘,Flash等存储类设备,本文以3.14为蓝本,探讨内核中的块设备驱动模型 框架 下图是Linux中的块设备模型示意图,应用层程序有两种方式访问一 ...

随机推荐

  1. java.util.ResourceBundle 用法小介

    java中读取配置文件的信息可以采用properties这个类,但是当遇到国际化问题的时候还是不好解决,因而还是最好使用 ResourceBundle这个类,其实ResourceBundle本质上和P ...

  2. Query on The Trees(hdu 4010)

    题意: 给出一颗树,有4种操作: 1.如果x和y不在同一棵树上则在xy连边 2.如果x和y在同一棵树上并且x!=y则把x换为树根并把y和y的父亲分离 3.如果x和y在同一棵树上则x到y的路径上所有的点 ...

  3. hdu 1224 最长路

    开始用dijkstra直接求,发现不行,算法问题(1-2,(30),2-4(20),1--3(10),3--4(100)最后一个点无法更新,导致错误),后用取负,加大数法也没过. 现在(寒假了):求负 ...

  4. Elixir与编辑器安装

    安装 Elixir 每个操作系统的安装说明可以在 elixir-lang.org 网站上 Installing Elixir 部分找到. 安装后你可以很轻松地确认所安装的版本. ~$:elixir - ...

  5. NRapid前言

    开发工具 Visual Studio 2017 数据库 SQL Server 2012 相关技术 Asp.net MVC

  6. Redhat 5 无法安装elfutils-libelf-devel-0.137问题

    http://whr25.blog.sohu.com/263584338.html 问题: RHEL5.5安装oracle11gR2的时候需要安装elfutils-libelf-devel-0.137 ...

  7. 【Todo】Java类面试题分析

    Java 面试中的重要话题 多线程,并发及线程基础数据类型转换的基本原则垃圾回收(GC)Java 集合框架数组字符串GOF 设计模式SOLID (单一功能.开闭原则.里氏替换.接口隔离以及依赖反转)设 ...

  8. tech blog link

    http://amitsaha.github.io/site/notes/index.html

  9. 转: CentOS 6 使用 yum 安装MongoDB及服务器端配置

    转: http://www.cnblogs.com/shanyou/archive/2012/07/14/2591838.html CentOS 6 使用 yum 安装MongoDB及服务器端配置   ...

  10. CMS - 认识目录

    Tips:如果网页图片(文字)看不清,请按CTRL+鼠标滚轮 一个完整的小程序模板目录结构如下! 本章节给出的建议: 1.推荐使用flex布局 2.其它图片路径建议引入网络路径(tabBar不支持网络 ...