块设备驱动之——block_device_operations以及gendisk(一)

1、块设备驱动往往为磁盘设备的驱动,但是由于磁盘设备的IO性能与CPU相比很差,因此,块设备的数据流往往会引入文件系统的Cache机制。

2、实现角度:Linux为块设备和字符设备提供了两套机制。字符设备的实现:内核例程和用户态API一一对应,用户层的Read函数直接对应内核中的read例程,这种映射关系由file_operation维护。

块设备:read、write API没有直接到块设备层,而是直接到文件系统层,再由文件系统层发起读写请求。

3、blok_device_operations结构体

与字符驱动设备程序一样,块设备驱动程序也包含在<linux/fs.h>中定义的blok_device_operations结构体

struct block_device_operations {
    int (*open) (struct block_device *, fmode_t);
    int (*release) (struct gendisk *, fmode_t);
    int (*locked_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
    int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
    int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
    int (*direct_access) (struct block_device *, sector_t,
                        void **, unsigned long *);
    int (*media_changed) (struct gendisk *);
    int (*revalidate_disk) (struct gendisk *);
    int (*getgeo)(struct block_device *, struct hd_geometry *);
    struct module *owner;
};


从该结构体的定义可以看出块设备并不提供read() write()等函数接口。

对块设备的读写请求都是以异步方式发送到设备相关request队列之中

4.1、gendisk的操作函数:

/* 分配gendisk */
struct gendisk *alloc_disk(int minors);

/* 删除gendisk */
void del_gendisk(struct gendisk *gd);

/* 增加gendisk */
void add_disk(struct gendisk *gd);

 

4.2、gendisk

一个块设备物理实体由一个gendisk结构体来表示(<linux/genhd.h>定义),每个gendisk可以支持多个分区。

每个gendisk包含了本物理实体的全部信息以及操作函数接口。整个块设备的注册是围绕gendisk来展开的。在驱动程序中初始化gendisk成员如下。

struct gendisk {
    int major;            /* major number of driver */
    int first_minor;
    int minors;                     /* maximum number of minors, =1 for
                                         * disks that can't be partitioned. */
    char disk_name[32];        /* name of major driver */
    struct hd_struct **part;  /* 磁盘上的分区信息 */
    int part_uevent_suppress;
    struct block_device_operations *fops;/*块设备操作结构体blok_device_operations*/
    struct request_queue *queue;
    void *private_data;
    sector_t capacity;

    int flags;
    struct device *driverfs_dev;
    struct kobject kobj;
    struct kobject *holder_dir;
    struct kobject *slave_dir;

    struct timer_rand_state *random;
    int policy;

    atomic_t sync_io;        /* RAID */
    unsigned long stamp;
    int in_flight;
#ifdef    CONFIG_SMP
    struct disk_stats *dkstats;
#else
    struct disk_stats dkstats;
#endif
    struct work_struct async_notify;
};


1、major //主设备号,同一磁盘的各个分区共享一个主设备号。

2、register_blkdev()函数用来注册块设备驱动,并申请主设备号

/* 注册块设备驱动 */
int register_blkdev(unsigned int major, const char *name);

/* 注销块设备驱动 */
int unregister_blkdev(unsigned int major, const char *name);

3、int first_minor; //次设备号
如果有分区,每个分区都需要一个次设备号

4、int minors; //次设备的最大数目,未分区则 = 1

5、char disk_name[32]; //磁盘名字

6、struct block_device_operations *fops; //块设备操作函数集合
7、struct request_queue *queue; //请求队列
用来管理该设备的I/O请求,请求队列的相关函数:

/* 初始化请求队列 */
request_queue_t *blk_init_queue(request_fn_proc *request, spinlock_t *lock);

/* 清除请求队列 */
void blk_cleanup_queue(request_queue_t *);

/* 分配请求队列 */
request_queue_t *blk_alloc_queue(int gfp_mask);

/* 绑定制造请求函数 */
void blk_queue_make_request(request_queue_t *q, make_request_fn *mfn);

/* 提取请求 */
struct request *blk_peek_request(struct request_queue *q);

/* 启动请求 */
void blk_start_request(struct request *req);

/* 报告完成 */
void blk_end_request_all(struct request *rq, int error);
void __blk_end_request_all(struct request *rq, int error);  //在持有队列锁的场景下调用

对请求的处理有两种模式,可以使用请求队列,也可以不使用,在使用请求队列时,使用blk_init_queue()函数,不用请求队列时,将blk_alloc_queue()和blk_queue_make_request()结合使用,一般模式为;

xxx_queue = blk_alloc_queue(GFP_KERNEL);
blk_queue_make_request(xxx_queue, xxx_make_request);

8、int flags; //描述驱动器状态的标志
如果为可移动介质:GENHD_FL_REMOVABLE
如果为CD_ROM:GENHD_FL_CD
如果不需要分区信息出现在 /proc/partitions, 设置为
GENHD_FL_SUPPRESS_PARTITIONS_INFO

9、sector_t capacity; //磁盘驱动器容量,以512字节的扇区个数为单位
一般通过set_capacity()函数设置:

void set_capacity(struct gendisk *disk, sector_t size);

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

10、void *private_data; //指向私有数据的指针

可用于指向磁盘的任何私有数据。,用法与file结构体的private_data类似

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值