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. Office 中的各种小tips(更新中)

    1.Word 中打字输入会擦掉之后原有字符,出现“吃字”的情况? 要将“改写”切换为“插入”,最简单的方法就是点击键盘上小键盘旁边的“insert”键. 其实仔细观察的话,在word文档下方,会看到如 ...

  2. Goldbach

    Description: Goldbach's conjecture is one of the oldest and best-known unsolved problems in number t ...

  3. Golang 入门 : 等待 goroutine 完成任务

    Goroutine 是 Golang 中非常有用的功能,但是在使用中我们经常碰到下面的场景:如果希望等待当前的 goroutine 执行完成,然后再接着往下执行,该怎么办?本文尝试介绍这类问题的解决方 ...

  4. 简化LINUX的命令输入 简化linux命令 快捷键 短路径

    在LINUX中,有很多常用的命令,常用的命令我们可以熟练的记忆,但是对于不经常使用的命令恐怕是需要翻阅手册了,但是我们可以简化这些命令的输入来达到简便记忆的效果. 这里以BSH为例: 编辑/etc/b ...

  5. 【Java TCP/IP Socket】深入剖析socket——TCP套接字的生命周期

    建立TCP连接      新的Socket实例创建后,就立即能用于发送和接收数据.也就是说,当Socket实例返回时,它已经连接到了一个远程终端,并通过协议的底层实现完成了TCP消息或握手信息的交换. ...

  6. Linux下&/jobs/fg/bg命令的使用(转)

    一.& 这个用在一个命令的最后,可以把这个命令放到后台执行. 二.[Ctrl]+[Z] 可以将一个正在前台执行的命令放到后台,并且暂停. 三.jobs 查看当前有多少在后台运行的命令. 四.f ...

  7. 内核调试 SystemTap

    http://www.cnblogs.com/wangkangluo1/archive/2012/06/26/2562971.html   相关技术:utrace, probe, ftrace, dt ...

  8. widows 2008 同步时间命令

    由于windows2008没有提供类似XP的自动同步功能,因此需要使用windows 2008计划任务来运行一行命令进行同步.   首先查看与想要同步时间的internet时间服务器的时差: w32t ...

  9. 【gradle】mac下 gradle默认本地仓库位置

    gradle默认会把包缓存到用户目录的.gradle目录下,如果你打开.gradle\caches\modules-2\files-2.1,你会发现很多的jar包.mac上的话 ,也就是在/Users ...

  10. Activiti 5.17 实体对象与类和数据库表的映射

    一.Activiti 5.17 mybatis的mapping文件声明映射的实体对象关系. <configuration><settings><settingname=& ...