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. 源码分析 脱壳神器ZjDroid工作原理

    0. 神器ZjDroid Xposed框架的另外一个功能就是实现应用的简单脱壳,其实说是Xposed的作用其实也不是,主要是模块编写的好就可以了,主要是利用Xposed的牛逼Hook技术实现的,下面就 ...

  2. 请问 内网的 dns服务器 为什么和 外网的dns服务器 一样??

    公司内的内网使用192.169.X.X的内网地址,但是在DNS段填写的是210.34.X.X,显然这是一个公网固定IP,我不明白的是:为什么内部网客户端使用的DNS服务器是公网上的IP呢?内网客户端能 ...

  3. mark一下。hadoop分布式系统搭建

    用于测试,我用4台虚拟机搭建成了hadoop结构 我用了两个台式机.一个xp系统,一个win7系统.每台电脑装两个虚拟机,要不然内存就满了. 1.安装虚拟机环境 Vmware,收费产品,占内存较大. ...

  4. Android Studio升级到3.0,抛出Aapt2Exception异常

    android studiao错误: Android resource linking failedOutput: D:\_ASWorkSpace\phone_new\app\src\main\res ...

  5. win8激活工具,win 8激活工具,windows8激活工具,赶紧来下载咯

    同事前几天买了一个电脑,装的win8的系统,由于装office,需要激活,找了下office的激活工具,那个Office激活工具自带有win8激活,同事点错了,把正版系统给激活了,变成盗版了(悲剧.. ...

  6. Day 12 shell语法及程序若干

    1. 现归纳一下shell中的运算符:       +:对两个变量做加法.    -:对两个变量做减法.    *:对两个变量做乘法.    /:对两个变量做除法.    **:对两个变量做幂运算.  ...

  7. CCPC-Wannafly Winter Camp Day1 (Div2, online mirror) A,B,C,E,F,I,J

    https://www.zhixincode.com/contest/7/problems A题 分类讨论 当B有点需要经过时 穿梭的花费肯定为2*k,也可以发现,我们要找到包含所有需要经过的点(不含 ...

  8. android widgets控件

    1.TextView 类似,C#里的lable,显示一段文本 <TextView android:id="@+id/textView2" android:layout_wid ...

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

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

  10. codevs 2669 简单的试炼

    2.codevs   2669 简单的试炼 题目描述 Description 已知一个数S,求X和Y,使得2^X+3^Y=S. 输入描述 Input Description (多组数据) 每行一个整数 ...