上回最后面介绍了相关数据结构,下面再详细介绍

块设备对象结构 block_device

内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区。如果该结构代表一个分区,则其成员bd_part指向设备的分区结构。如果该结构代表设备,则其成员bd_disk指向设备的通用硬盘结构gendisk

当用户打开块设备文件时,内核创建结构block_device实例,设备驱动程序还将创建结构gendisk实例,分配请求队列并注册结构block_device实例。

块设备对象结构block_device列出如下(在include/Linux/fs.h中)

  1. struct block_device {
  2. dev_t bd_dev;  /* not a kdev_t - it's a search key */
  3. struct inode * bd_inode; /* 分区节点 */
  4. struct super_block * bd_super;
  5. int bd_openers;
  6. struct mutex bd_mutex;/* open/close mutex 打开与关闭的互斥量*/
  7. struct semaphore bd_mount_sem;    /*挂载操作信号量*/
  8. struct list_head bd_inodes;
  9. void * bd_holder;
  10. int bd_holders;
  11. #ifdef CONFIG_SYSFS
  12. struct list_head bd_holder_list;
  13. #endif
  14. struct block_device * bd_contains;
  15. unsigned bd_block_size;     /*分区块大小*/
  16. struct hd_struct * bd_part;
  17. unsigned bd_part_count;   /*打开次数*/
  18. int bd_invalidated;
  19. struct gendisk * bd_disk; /*设备为硬盘时,指向通用硬盘结构*/
  20. struct list_head bd_list;
  21. struct backing_dev_info *bd_inode_backing_dev_info;
  22. unsigned long bd_private;
  23. /* The counter of freeze processes */
  24. int bd_fsfreeze_count;
  25. /* Mutex for freeze */
  26. struct mutex bd_fsfreeze_mutex;
  27. };

通用硬盘结构 gendisk

结构体gendisk代表了一个通用硬盘(generic hard disk)对象,它存储了一个硬盘的信息,包括请求队列、分区链表和块设备操作函数集等。块设备驱动程序分配结构gendisk实例,装载分区表,分配请求队列并填充结构的其他域。

支持分区的块驱动程序必须包含 <linux/genhd.h> 头文件,并声明一个结构gendisk,内核还维护该结构实例的一个全局链表gendisk_head,通过函数add_gendisk、del_gendisk和get_gendisk维护该链表。

结构gendisk列出如下(在include/linux/genhd.h中):

  1. struct gendisk {
  2. int major;            /* 驱动程序的主设备号 */
  3. int first_minor;       /*第一个次设备号*/
  4. int minors;          /*次设备号的最大数量,没有分区的设备,此值为1 */
  5. char disk_name[32];  /* 主设备号驱动程序的名字*/
  6. struct hd_struct **part;   /* 分区列表,由次设备号排序 */
  7. struct block_device_operations *fops;  /*块设备操作函数集*/
  8. struct request_queue *queue;         /*请求队列*/
  9. struct blk_scsi_cmd_filter cmd_filter;
  10. void *private_data;                 /*私有数据*/
  11. sector_t capacity;     /* 函数set_capacity设置的容量,以扇区为单位*/
  12. int flags;                 /*设置驱动器状态的标志,如:可移动介质为
  13. GENHD_FL_REMOVABLE*/
  14. struct device dev;                 /*从设备驱动模型基类结构device继承*/
  15. struct kobject *holder_dir;
  16. struct kobject *slave_dir;
  17. struct timer_rand_state *random;
  18. int policy;
  19. atomic_t sync_io;        /* RAID */
  20. unsigned long stamp;
  21. int in_flight;
  22. #ifdef  CONFIG_SMP
  23. struct disk_stats *dkstats;
  24. #else
  25. /*硬盘统计信息,如:读或写的扇区数、融合的扇区数、在请求队列的时间等*/
  26. struct disk_stats dkstats;
  27. #endif
  28. struct work_struct async_notify;
  29. #ifdef  CONFIG_BLK_DEV_INTEGRITY
  30. struct blk_integrity *integrity;   /*用于数据完整性扩展*/
  31. #endif
  32. };

Linux内核提供了一组函数来操作gendisk,主要包括:
分配gendisk
struct gendisk *alloc_disk(int minors);
minors 参数是这个磁盘使用的次设备号的数量,一般也就是磁盘分区的数量,此后minors不能被修改。
增加gendisk
gendisk结构体被分配之后,系统还不能使用这个磁盘,需要调用如下函数来注册这个磁盘设备:
void add_disk(struct gendisk *gd);
特别要注意的是对add_disk()的调用必须发生在驱动程序的初始化工作完成并能响应磁盘的请求之后。

 释放gendisk
当不再需要一个磁盘时,应当使用如下函数释放gendisk:
void del_gendisk(struct gendisk *gd);

设置gendisk容量
void set_capacity(struct gendisk *disk, sector_t size);
块设备中最小的可寻址单元是扇区,扇区大小一般是2的整数倍,最常见的大小是512字节。扇区的大小是设备的物理属性,扇区是所有块设备的基本单元,块设备 无法对比它还小的单元进行寻址和操作,不过许多块设备能够一次就传输多个扇区。虽然大多数块设备的扇区大小都是512字节,不过其它大小的扇区也很常见, 比如,很多CD-ROM盘的扇区都是2K大小。不管物理设备的真实扇区大小是多少,内核与块设备驱动交互的扇区都以512字节为单位。因此,set_capacity()函数也以512字节为单位。

分区结构hd_struct代表了一个分区对象,它存储了一个硬盘的一个分区的信息,驱动程序初始化时,从硬盘的分区表中提取分区信息,存放在分区结构实例中。

块设备操作函数集结构 block_device_operations

字符设备通过 file_operations 操作结构使它们的操作对系统可用. 一个类似的结构用在块设备上是 struct block_device_operations,
定义在 <linux/fs.h>. 
int (*open)(struct inode *inode, struct file *filp); 
int (*release)(struct inode *inode, struct file *filp); 
就像它们的字符驱动对等体一样工作的函数; 无论何时设备被打开和关闭都调用它们. 一个字符驱动可能通过启动设备或者锁住门(为可移出的介质)来响应一个 open 调用. 如果你将介质锁入设备, 你当然应当在 release 方法中解锁.
int (*ioctl)(struct inode *inode, struct file *filp, 
                          unsigned int cmd, unsigned long arg); 
实现 ioctl 系统调用的方法. 但是, 块层首先解释大量的标准请求; 因此大部分的块驱动 ioctl 方法相当短.

PS:在block_device_operations中没有实际读或写数据的函数. 在块 I/O 子系统, 这些操作由请求函数处理

请求结构request

结构request代表了挂起的I/O请求每个请求用一个结构request实例描述,存放在请求队列链表中,由电梯算法进行排序,每个请求包含1个或多个结构bio实例

  1. struct request {
  2. //用于挂在请求队列链表的节点,使用函数blkdev_dequeue_request访问它,而不能直接访
  3. struct list_head queuelist;
  4. struct list_head donelist;  /*用于挂在已完成请求链表的节点*/
  5. struct request_queue *q;   /*指向请求队列*/
  6. unsigned int cmd_flags;    /*命令标识*/
  7. enum rq_cmd_type_bits cmd_type;  /*命令类型*/
  8. /*各种各样的扇区计数*/
  9. /*为提交i/o维护bio横断面的状态信息,hard_*成员是块层内部使用的,驱动程序不应该改变
  10. 它们*/
  11. sector_t sector;     /*将提交的下一个扇区*/
  12. sector_t hard_sector;        /* 将完成的下一个扇区*/
  13. unsigned long nr_sectors;  /* 整个请求还需要传送的扇区数*/
  14. unsigned long hard_nr_sectors; /* 将完成的扇区数*/
  15. /*在当前bio中还需要传送的扇区数 */
  16. unsigned int current_nr_sectors;
  17. /*在当前段中将完成的扇区数*/
  18. unsigned int hard_cur_sectors;
  19. struct bio *bio;     /*请求中第一个未完成操作的bio*、
  20. struct bio *biotail; /*请求链表中末尾的bio*、
  21. struct hlist_node hash;  /*融合 hash */
  22. /* rb_node仅用在I/O调度器中,当请求被移到分发队列中时,
  23. 请求将被删除。因此,让completion_data与rb_node分享空间*/
  24. union {
  25. struct rb_node rb_node;   /* 排序/查找*/
  26. void *completion_data;
  27. };

request结构体的主要成员包括:
 sector_t hard_sector; 
unsigned long hard_nr_sectors; 
unsigned int hard_cur_sectors; 
上述3个成员标识还未完成的扇区,hard_sector是第1个尚未传输的扇区,hard_nr_sectors是尚待完成的扇区数,hard_cur_sectors是并且当前I/O操作中待完成的扇区数。这些成员只用于内核块设备层,驱动不应当使用它们。

sector_t sector; 
unsigned long nr_sectors; 
unsigned int current_nr_sectors; 
驱动中会经常与这3个成员打交道,这3个成员在内核和驱动交互中发挥着重大作用。它们以512字节大小为1个扇区,如果硬件的扇区大小不是512字节,则需要进行相应的调整。例如,如果硬件的扇区大小是2048字节,则在进行硬件操作之前,需要用4来除起始扇区号。

hard_sector、hard_nr_sectors、hard_cur_sectors与sector、nr_sectors、current_nr_sectors之间可认为是“副本”关系。

struct bio *bio; 
bio是这个请求中包含的bio结构体的链表,驱动中不宜直接存取这个成员,而应该使用后文将介绍的rq_for_each_bio()。

请求队列结构request_queue

每个块设备都有一个请求队列,每个请求队列单独执行I/O调度,请求队列是由请求结构实例链接成的双向链表,链表以及整个队列的信息用结构request_queue描述,称为请求队列对象结构或请求队列结构。它存放了关于挂起请求的信息以及管理请求队列(如:电梯算法)所需要的信息。结构成员request_fn是来自设备驱动程序的请求处理函数。

请求队列结构request_queue列出如下(在/include/linux/blk_dev.h中)

太长了,此处略,其实也看不懂,- -#

Bio结构

通常1个bio对应1个I/O请求,IO调度算法可将连续的bio合并成1个请求。所以,1个请求可以包含多个bio。

内核中块I/O操作的基本容器由bio结构体表示,定义 在<linux/bio.h>中,该结构体代表了正在现场的(活动的)以片段(segment)链表形式组织的块I/O操作。一个片段是一小 块连续的内存缓冲区。这样的好处就是不需要保证单个缓冲区一定要连续。所以通过片段来描述缓冲区,即使一个缓冲区分散在内存的多个位置上,bio结构体也 能对内核保证I/O操作的执行,这样的就叫做聚散I/O.
bio为通用层的主要数据结构,既描述了磁盘的位置,又描述了内存的位置,是上层内核vfs与下层驱动的连接纽带

  1. struct bio {
  2. sector_t        bi_sector;//该bio结构所要传输的第一个(512字节)扇区:磁盘的位置
  3. struct bio        *bi_next;    //请求链表
  4. struct block_device    *bi_bdev;//相关的块设备
  5. unsigned long        bi_flags//状态和命令标志
  6. unsigned long        bi_rw; //读写
  7. unsigned short        bi_vcnt;//bio_vesc偏移的个数
  8. unsigned short        bi_idx;    //bi_io_vec的当前索引
  9. unsigned short        bi_phys_segments;//结合后的片段数目
  10. unsigned short        bi_hw_segments;//重映射后的片段数目
  11. unsigned int        bi_size;    //I/O计数
  12. unsigned int        bi_hw_front_size;//第一个可合并的段大小;
  13. unsigned int        bi_hw_back_size;//最后一个可合并的段大小
  14. unsigned int        bi_max_vecs;    //bio_vecs数目上限
  15. struct bio_vec        *bi_io_vec;    //bio_vec链表:内存的位置
  16. bio_end_io_t        *bi_end_io;//I/O完成方法
  17. atomic_t        bi_cnt; //使用计数
  18. void            *bi_private; //拥有者的私有方法
  19. bio_destructor_t    *bi_destructor;    //销毁方法
  20. };

内存数据段结构bio_vec

结构bio_vec代表了内存中的一个数据段,数据段用页、偏移和长度描
述。I/O需要执行的内存位置用段表示,结构bio指向了一个段的数组。
结构bio_vec列出如下(在include/linux/bio.h中):
struct bio_vec {
       struct page     *bv_page;   /*数据段所在的页*/
       unsigned short  bv_len;     /*数据段的长度*/
       unsigned short  bv_offset;  /*数据段页内偏移*/
};

块设备各个结构体间关系

Linux设备驱动--块设备(二)之相关结构体的更多相关文章

  1. 【转】Linux设备驱动--块设备(一)之概念和框架

    原文地址:Linux设备驱动--块设备(一)之概念和框架 基本概念   块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时 ...

  2. Linux设备驱动--块设备(二)之相关结构体(转)

    上回最后面介绍了相关数据结构,下面再详细介绍 块设备对象结构 block_device 内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区.如果该结构代表一个分区,则其成 ...

  3. Linux设备驱动--块设备(一)之概念和框架(转)

    基本概念   块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性 ...

  4. Linux设备驱动--块设备(四)之“自造请求”

    前面, 我们已经讨论了内核所作的在队列中优化请求顺序的工作; 这个工作包括排列请求和, 或许, 甚至延迟队列来允许一个预期的请求到达. 这些技术在处理一个真正的旋转的磁盘驱动器时有助于系统的性能. 但 ...

  5. Linux设备驱动--块设备(三)之程序设计

    块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为:int register_blkdev(unsigned i ...

  6. Linux设备驱动--块设备(四)之“自造请求”(转)

    前面, 我们已经讨论了内核所作的在队列中优化请求顺序的工作; 这个工作包括排列请求和, 或许, 甚至延迟队列来允许一个预期的请求到达. 这些技术在处理一个真正的旋转的磁盘驱动器时有助于系统的性能. 但 ...

  7. Linux设备驱动--块设备(三)之程序设计(转)

    http://blog.csdn.net/jianchi88/article/details/7212701 块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数 ...

  8. 3.5Linux设备驱动--块设备(一)之概念和框架☆☆

    基本概念   块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性 ...

  9. Linux SPI总线和设备驱动架构之二:SPI通用接口层

    通过上一篇文章的介绍,我们知道,SPI通用接口层用于把具体SPI设备的协议驱动和SPI控制器驱动联接在一起,通用接口层除了为协议驱动和控制器驱动提供一系列的标准接口API,同时还为这些接口API定义了 ...

随机推荐

  1. python常用函数 A

    1.any()   iterable元素是不是全为0 2.all()    iterable元素是不是有0 a = [1, 2, 3] b = [1, 0, 3] c = [0, 0, 0] # an ...

  2. OpenSSH高级功能之端口转发(Port Forwarding)

    在RedHat提供的系统管理员指南中提到OpenSSH不止是一个安全shell,它还具有X11转发(X11 Forwarding)和端口转发(Port Forwarding)的功能.X11功能一般用于 ...

  3. gitHub网站上常见英语翻译2

    repositories资料库 compilers with rich code analysis APIs.编译器具有丰富的代码分析API. plugins插件 With a variety of ...

  4. hexo干货系列:(八)hexo文章自动隐藏侧边栏

    前言 使用Jacman主题的时候发现打开具体文章后,侧边栏还是会展示,我想要的效果是自动隐藏侧边栏,并且展示目录.但是当我修改了主题配置文件里面close_aside属性为true的时候,发现侧边栏隐 ...

  5. [HAOI2006]受欢迎的牛(tarjan缩点)

    洛谷传送门 直接tarjan求scc,然后统计出度为0的缩点,如果多余1个就输出0,只有一个就输出这个缩点里的点. ——代码 #include <cstdio> #include < ...

  6. Hankson 的趣味题(codevs 1172)

    题目描述 Description Hanks 博士是BT (Bio-Tech,生物技术) 领域的知名专家,他的儿子名叫Hankson.现在,刚刚放学回家的Hankson 正在思考一个有趣的问题.今天在 ...

  7. msp430项目编程23

    msp430中项目---风扇控制系统 1.比较器工作原理 2.电路原理说明 3.代码(显示部分) 4.代码(功能实现) 5.项目总结 msp430项目编程 msp430入门学习

  8. delphi操作xml学习笔记 之一 入门必读

    Delphi 对XML的支持---TXMLDocument类       Delphi7 支持对XML文档的操作,可以通过TXMLDocument类来实现对XML文档的读写.可以利用TXMLDocum ...

  9. DELPHI7调用BERLIN中间件的中文字段名乱码的解决办法

    MSSQL数据库的表使用中文字段名,BERLIN开发的DATASNAP中间件,DELPHI7调用中间件的查询方法返回数据给CLIENTDATASET.DATA,发现中文字段名乱码,中文字段名的值可以正 ...

  10. ITIL的考核管理体系

    是的,我们ITIL的考核管理体系,大概是从几个方面进行考核的.阿里巴巴作为一个上市公司,是全球的B2B电子商务的领先者,那么作为我们的运维部,保证完整的可用性是首当其冲的.我们的ITIL考核体系里面, ...