顾名思义,块设备驱动程序就是支持以块的方式进行读写的设备。块设备和字符设备最大的区别在于读写数据的基本单元不同。块设备读写数据的基本单元为块,例如磁盘通常为一个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);//添加分区

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. 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设备驱动程序>第十六章 块设备驱动程序读书笔记 简介 一个块设备驱动程序主要通过传输固定大小的随机数据来访问设备 Linux内核视块设备为与字符设备相异的基本设备类型 Linu ...

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

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

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

    块设备的处理 一个进程在某个磁盘文件上发出一个 read() 系统调用,内核对进程请求回应的一般步骤: read() 调用一个适当的 VFS 函数,将文件描述符和文件内的偏移量传递给它. 虚拟文件系统 ...

  4. 写一个块设备驱动程序

    ----------------------- Page 1----------------------- 第 1章 +---------------------------------------- ...

  5. IO子系统(一) — 块设备驱动程序

    VFS层(虚拟文件系统层): 由于内核需要跟不同的文件系统打交道,而每一个文件系统所实现的方式和数据结构也不尽相同,所以内核抽象了这一层,专门用来适配各种文件系统,对上提供统一的操作接口,对下层的诸多 ...

  6. linux块设备的IO调度算法和回写机制

    ************************************************************************************** 參考: <Linux ...

  7. [转]linux 块设备驱动

    转自 linux块设备IO栈 http://www.sysnote.org/2015/08/06/linux-io-stack/ linux块设备IO流程 驱动 https://www.cnblogs ...

  8. Linux块设备驱动(二)————块设备的体系架构

    块设备的体系架构从上到下依次为VFS虚拟文件系统.磁盘缓冲.各种类型的磁盘系统.通用块设备层.I/O调度层(优化访问上层的请求(读写请求)).块设备驱动层.块设备硬件层. 1.虚拟文件系统(VFS) ...

  9. 有点意思!Linux 块设备处理模型,基础【簇、柱面、存储的计算】

    http://blog.csdn.net/zplove003/article/details/7020557 簇:簇是指可分配的用来保存文件的最小磁盘空间,扇区是磁盘最小的物理存储单元,但由于操作系统 ...

最新文章

  1. dhcp 中继代理配置方法
  2. 一维二维码的提取、识别和产生
  3. 多线程:interrupted、isinterrupted区别
  4. JAVA数据类型与逻辑练习
  5. SAP CRM BSPWDApplication.do
  6. activiti mysql 版本_Mysql8.0.17版本不能自动创建activiti表的坑
  7. HP服务器ile进系统,HP GEN10服务器UEFI安装Windows Sverver 2012 R2教程
  8. mysqld命令相关介绍
  9. Android源码中常用的系统广播
  10. FAST FW150R软件版本升级解决一些网页无法加载问题
  11. 1.2Android 5.1-7.1 高通驱动修改
  12. 喜欢听音乐应该买什么蓝牙耳机?音质好听的蓝牙耳机推荐
  13. 试题 基础练习 数列排序
  14. 深入理解 LWUIT 框架的 MVC
  15. Python 安装pyqt—tools失败,记录
  16. python opencv 常用增强 dct变换+侵蚀+扩张+索贝尔算子+直方图均衡化+光照平衡+
  17. google、bing ,baidu 等搜索引擎 查询参数
  18. 使用Spark和Pig统计每秒钟微博数量
  19. 月薪3w算法工程师日均7行代码被开除 / AI歌手入学上海音乐学院 / 天文照片被植入恶意代码...
  20. linux系列(十):shell循环结构、for、while

热门文章

  1. Boost.PolyCollection 性能测试
  2. boost::math::tools::sum_series用法的测试程序
  3. boost::hana::just用法的测试程序
  4. boost::intrusive::treap_algorithms用法的测试程序
  5. boost::geometry::strategy::distance::cross_track用法的测试程序
  6. Boost:双图bimap与散列索引的测试程序
  7. VTK:Utilities之ViewportBorders
  8. VTK:Utilities之PCADemo
  9. VTK:Utilities之SparseArray
  10. VTK:PolyData之SurfacePointPlacer