0
点赞
收藏
分享

微信扫一扫

20150310 块设备驱动程序

耶也夜 2022-03-30 阅读 39


20150310 块设备驱动程序

2015-03-10 李海沿

    接下来我们来实现块设备驱动程序。

一、块设备结构体

1. file_operations 结构体

和字符设备驱动中file_operations 结构体类似,块设备驱动中也有一个

block_device_operations 结构体,它的声明位于/include/linux 录下的fs.h 文件中,它是对

块操作的集合。


struct block_device_operations{

int(*open)(struct inode *, struct file*); //打开设备

int(*release)(struct inode *, struct file*); //关闭设备

//实现ioctl 系统调用

int(*ioctl)(struct inode *, struct file *, unsigned, unsigned long);

long(*unlocked_ioctl)(struct file *, unsigned, unsigned long);

long(*compat_ioctl)(struct file *, unsigned, unsigned long);

int(*direct_access)(struct block_device *, sector_t, unsigned long*);

//调用该函数用以检查用户是否更换了驱动器的介质

int(*media_changed)(struct gendisk*);

int(*revalidate_disk)(struct gendisk*); //当介质被更换时,调用该函数做出响应

int(*getgeo)(struct block_device *, struct hd_geometry*);//获取驱动器信息

struct module *owner; //指向拥有这个结构体模块的指针,通常被初始化位THIS_MODULE

};


与字符驱动不同的是在这个结构体中缺少了read()和write()函数,那是因为块设备的I/O 子系统中,这些操作都是有request 函数进行处理。

request 函数的原型如下:

void request(request_queue_t *queue);

2.gendisk结构体

gendisk 结构体的定义位于/include/linux 目录下的genhd.h 文件中,如下所示。


struct gendisk {

/*

*这三个成员的定义依次是:主设备号、第一个次设备号,次设备号。一个驱动中至有一个次设备号,

*如果驱动器是一个可被分区,那么每一个分区都将分配一个次设号。

*/

int major;

int first_minor;

int minors;

//这个数组用以存储驱动设备的名字

char disk_name[DISK_NAME_LEN];

char *(*devnode)(struct gendisk *gd, umode_t *mode);

unsigned int events;

unsigned int async_events;

struct disk_part_tbl __rcu *part_tbl;

struct hd_struct part0;

//这个结构体用以设置驱动中的各种设备操作

const struct block_device_operations *fops;

//Linux 内核使用这个结构体为设备管理I/O 请求,具体详解见request_queue 结构。

struct request_queue *queue;

void *private_data;

int flags;

struct device *driverfs_dev;

struct kobject *slave_dir;

struct timer_rand_state *random;

atomic_t sync_io;

struct disk_events *ev;

#ifdef CONFIG_BLK_DEV_INTEGRITY

struct blk_integrity *integrity;

#endif

int node_id;

};


gendisk 结构体是是动态分配,但是驱动程序自己不能动态分配该结构,而是通过调用

alloc_disk()函数进行动态分配。

struct gendisk *alloc_disk(int minors);

其中minors 是该磁盘使用的次设备号。

但是分配了gendisk 结构并不意味着该磁盘就对系统可用,使用之前得初始化结构体并

且调用add_disk()函数。

//初始化结构体

struct request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)

//添加分区

void add_disk(struct gendisk *gd)

如果不再需要这个磁盘,则对其进行卸载。

//删除分区

Void del_gendisk(struct gendisk *gd)

void blk_cleanup_queue(struct request_queue *q)

3.bio结构体

bio 结构体的定义位于/include/linux 目录下的linux_blk_types.h 文件中。


struct bio {

//需要传输的第一个(512byte)扇区

sector_t bi_sector;

struct bio *bi_next;

struct block_device *bi_bdev;

unsigned long bi_flags;

unsigned long bi_rw;

unsigned short bi_vcnt;

unsigned short bi_idx;

//BIO 中所包含的物理段数目

unsigned int bi_phys_segments;

//所传输的数据大小(以byte 为单位)

unsigned int bi_size;

unsigned int bi_seg_front_size;

unsigned int bi_seg_back_size;

unsigned int bi_max_vecs;

atomic_t bi_cnt;

struct bio_vec *bi_io_vec;

bio_end_io_t *bi_end_io;

void *bi_private;

#ifdef CONFIG_BLK_CGROUP

struct io_context *bi_ioc;

struct cgroup_subsys_state *bi_css;

#endif

#if defined(CONFIG_BLK_DEV_INTEGRITY)

struct bio_integrity_payload *bi_integrity;

#endif

bio_destructor_t *bi_destructor;

struct bio_vec bi_inline_vecs[0];

};


bio 结构体包含了驱动程序执行请求的所有信息。既描述了磁盘的位置,又描述了内存

的位置,是上层内核与下层驱动的连接纽带。

struct bio_vec *bi_io_vec;

而bio_vec 结构体的声明为:结构bio_vec 代表了内存中的一个数据段,数据段用页、偏移和长度描述


struct bio_vec {

struct page *bv_page; /*数据段所在的页*/

unsigned short bv_len; /*数据段的长度*/

unsigned short bv_offset; /*数据段页内偏移*/

};


4. requeset 结构体

request 结构体代表了挂起的I/O 请求,每个请求用一个结构request 实例描述,存放

在请求队列链表中,由电梯算法进行排序,每个请求包含一个或多个结构bio 实例。requeest

结构体声明位于/include/linux 目录下的blkdev.h 文件中。


struct request {

struct list_head queuelist;

struct call_single_data csd;

struct request_queue *q;

unsigned int cmd_flags;

enum rq_cmd_type_bits cmd_type;

unsigned long atomic_flags;

int cpu;

unsigned int __data_len;

sector_t __sector;

struct bio *bio;

struct bio *biotail;

struct hlist_node hash;

union {

struct rb_node rb_node;

void *completion_data;

};

union {

struct {

struct io_cq *icq;

void *priv[2];

} elv;

struct {

unsigned int seq;

struct list_head list;

rq_end_io_fn *saved_end_io;

} flush;

};

struct gendisk *rq_disk;

struct hd_struct *part;

unsigned long start_time;

#ifdef CONFIG_BLK_CGROUP

struct request_list *rl;

unsigned long long start_time_ns;

unsigned long long io_start_time_ns;

#endif

unsigned short nr_phys_segments;

#if defined(CONFIG_BLK_DEV_INTEGRITY)

unsigned short nr_integrity_segments;

#endif

unsigned short ioprio;

int ref_count;

void *special;

char *buffer;

int tag;

int errors;

unsigned char __cmd[BLK_MAX_CDB];

unsigned char *cmd;

unsigned short cmd_len;

unsigned int extra_len;

unsigned int sense_len;

unsigned int resid_len;

void *sense;

unsigned long deadline;

struct list_head timeout_list;

unsigned int timeout;

int retries;

rq_end_io_fn *end_io;

void *end_io_data;

struct request *next_rq;

};


5. request_queue 结构体

每个块设备都有一个请求队列,每个请求队列单独执行I/O 调度,请求队列是由请求结

构实例链接成的双向链表,链表以及整个队列的信息用request_queue 结构体描述,称为请

求队列对象结构或请求队列结构。request_queue 结构体声明位于/include/linux 目录下的

blkdev.h 文件中。


struct request_queue{

    /*

     * Together with queue_head for cacheline sharing

     */

    struct list_head    queue_head;

    struct request        *last_merge;

    struct elevator_queue    *elevator;

    /*

     * the queue request freelist, one for reads and one for writes

     */

    struct request_list    rq;


    request_fn_proc        *request_fn;

    make_request_fn        *make_request_fn;

    prep_rq_fn        *prep_rq_fn;

    unplug_fn        *unplug_fn;

    prepare_discard_fn    *prepare_discard_fn;

    merge_bvec_fn        *merge_bvec_fn;

    prepare_flush_fn    *prepare_flush_fn;

    softirq_done_fn        *softirq_done_fn;

    rq_timed_out_fn        *rq_timed_out_fn;

    dma_drain_needed_fn    *dma_drain_needed;

    lld_busy_fn        *lld_busy_fn;


    /*

     * Dispatch queue sorting

     */

    sector_t        end_sector;

    struct request        *boundary_rq;


    /*

     * Auto-unplugging state

     */

    struct timer_list    unplug_timer;

    int            unplug_thresh;    /* After this many requests */

    unsigned long        unplug_delay;    /* After this many jiffies */

    struct work_struct    unplug_work;


    struct backing_dev_info    backing_dev_info;


    /*

     * The queue owner gets to use this for whatever they like.

     * ll_rw_blk doesn't touch it.

     */

    void            *queuedata;


    /*

     * queue needs bounce pages for pages above this limit

     */

    gfp_t            bounce_gfp;


    /*

     * various queue flags, see QUEUE_* below

     */

    unsigned long        queue_flags;


    /*

     * protects queue structures from reentrancy. ->__queue_lock should

     * _never_ be used directly, it is queue private. always use

     * ->queue_lock.

     */

    spinlock_t        __queue_lock;

    spinlock_t        *queue_lock;


    /*

     * queue kobject

     */

    struct kobject kobj;


    /*

     * queue settings

     */

    unsigned long        nr_requests;    /* Max # of requests */

    unsigned int        nr_congestion_on;

    unsigned int        nr_congestion_off;

    unsigned int        nr_batching;


    void            *dma_drain_buffer;

    unsigned int        dma_drain_size;

    unsigned int        dma_pad_mask;

    unsigned int        dma_alignment;


    struct blk_queue_tag    *queue_tags;

    struct list_head    tag_busy_list;


    unsigned int        nr_sorted;

    unsigned int        in_flight[2];


    unsigned int        rq_timeout;

    struct timer_list    timeout;

    struct list_head    timeout_list;


    struct queue_limits    limits;


    /*

     * sg stuff

     */

    unsigned int        sg_timeout;

    unsigned int        sg_reserved_size;

    int            node;

#ifdef CONFIG_BLK_DEV_IO_TRACE

    struct blk_trace    *blk_trace;

#endif

    /*

     * reserved for flush operations

     */

    unsigned int        ordered, next_ordered, ordseq;

    int            orderr, ordcolor;

    struct request        pre_flush_rq, bar_rq, post_flush_rq;

    struct request        *orig_bar_rq;


    struct mutex        sysfs_lock;


#if defined(CONFIG_BLK_DEV_BSG)

    struct bsg_class_device bsg_dev;

#endif

};



二、程序分析

1.定义各种结构体指针

20150310 块设备驱动程序_块设备

2.实现fileoperation结构体

20150310 块设备驱动程序_linux_02

如图所示,block_getgeo函数中的功能是伪装磁盘的磁头信息,磁头个数,柱面,容量等信息,是为了支持fdisk进行分区

3.在初始化函数中

20150310 块设备驱动程序_块设备_03

如图所示:

首先分配一个动态分配一个gendisk结构体,然后初始化各种设置,分配内存,最后试用add_disk注册。

4.实现读写函数

由于块设备没字符设备中所谓的read和write函数,所以我们的读写函数都是在do_block_request函数中实现的。

20150310 块设备驱动程序_#include_04

5.在exit函数中

最后就是在exit函数中释放我们前面的申请的内存等资源

20150310 块设备驱动程序_#include_05

6.编译命令


Insmod blk.ko


ll /dev/blkdev


mkfs.ext3 /dev/blkdev


fdisk /dev/blkdev





附驱动源程序:

20150310 块设备驱动程序_#include_0620150310 块设备驱动程序_linux_07

1 /* blkdev.c */
2 #include <linux/genhd.h>
3 #include <linux/init.h>
4 #include <linux/module.h>
5 #include <linux/blkdev.h>
6 #include <linux/types.h>
7 #include <linux/fs.h>
8 #include <linux/hdreg.h> //geo
9 #include <linux/vmalloc.h>
10
11 #define BLKDEV_SIZE 2*1024*1024
12
13 /* 定义一个指向请求队列的结构体指针 */
14 static struct request_queue *blkdev_queue;
15 /* 定义一个指向独立分区(磁盘)的结构体指针 */
16 static struct gendisk *blkdev_disk;
17 /* 定义一个自旋锁 */
18 static DEFINE_SPINLOCK(blkdev_lock);
19 /* 主设备号 */
20 static int blkdev_major;
21 static unsigned char blkdev_data[BLKDEV_SIZE];
22 static unsigned long lock_buf; //保存分配内存的指针
23
24 //由于没有老式的磁头等信息,但是为了支持fdisk分区工具,我们必须伪装有
25 static int block_getgeo(struct block_device *bdev, struct hd_geometry *geo){
26 //容量: heads * cvlinders * 512
27 geo->heads = (unsigned char)2; //磁头个数 ,2面
28 geo->cylinders = (unsigned short)32; //柱面, 32环
29 geo->sectors = (unsigned char)BLKDEV_SIZE / 2 / 32 / 512; //容量
30 return 0;
31 }
32 /*
33 * 块设备操作的集合
34 */
35 struct block_device_operations blkdev_fops = {
36 .owner = THIS_MODULE,
37 .getgeo = block_getgeo,
38 };
39
40 static void do_block_request(struct request_queue *q){
41 struct request *req;
42 static int r_cnt=0;
43 static int w_cnt=0;
44
45 while((req = blk_fetch_request(q)) != NULL){
46 /*数据传输*/
47 unsigned long offset = blk_rq_pos(req) * 512; //源
48 unsigned long len = blk_rq_sectors(req) * 512;//长度
49 /*
50 * 结束一个队列请求,第二个参数表示请求处理结果
51 * 成功的话设定为1,失败的话设定为0 或者错误号
52 */
53 /*
54 * rq_data_dir()函数返回该请求的方向:读还是写
55 */
56 if(rq_data_dir(req) == READ){
57 printk(KERN_ALERT "read %dth offset=%ld len=%ld\n",r_cnt,offset,len);
58 /* 从块设备读取数据 */
59 memcpy(req->buffer, blkdev_data+offset,len);
60 }else{
61 printk(KERN_ALERT "write %dth offset=%ld len=%ld\n",w_cnt,offset,len);
62 /* 把缓冲区的数据写入块设备 */
63 memcpy(blkdev_data + offset, req->buffer,len);
64 }
65 __blk_end_request_all(req, 1);
66 }
67 }
68
69 static int lhy_blkdev_init(void){
70
71 printk("<0>lhy_blkdev_init!\n");
72 /* 注册分区 */
73 /*1. 分配一个gendisk结构体*/
74 blkdev_disk = alloc_disk(16); //此设备号个数,分区个数+1
75 /*2.设置 */
76 /*2.1 分配/设置队列: 提供读写能力 */
77 blkdev_queue = blk_init_queue(do_block_request, &blkdev_lock);
78 blkdev_disk->queue = blkdev_queue;
79 /*2.2 其他设置 */
80 blkdev_major = register_blkdev(0,"blkdev");
81 blkdev_disk->major = blkdev_major;
82 blkdev_disk->first_minor = 0;
83 sprintf(blkdev_disk->disk_name, "blkdev");
84 blkdev_disk->fops = &blkdev_fops;
85 set_capacity(blkdev_disk, BLKDEV_SIZE >> 9);
86 /*3. 硬件相关,分配内存*/
87 lock_buf = vmalloc(BLKDEV_SIZE);
88 /*4.注册 */
89 add_disk(blkdev_disk);
90
91 return 0;
92 }
93
94 static void lhy_blkdev_exit(void){
95 printk("<0>lhy_blkdev_init!\n");
96 unregister_blkdev(blkdev_major,"blkdev");
97 /* 释放删除分区 add_disk() */
98 del_gendisk(blkdev_disk);
99 /* add_disk() */
100 printk("<0>putdisk\n");
101 put_disk(blkdev_disk);
102 /* blk_init_queue() */
103 blk_cleanup_queue(blkdev_queue);
104 printk("<0>kfree\n");
105 if(blkdev_data)
106 vfree(blkdev_data);
107 }
108
109 module_init(lhy_blkdev_init);
110 module_exit(lhy_blkdev_exit);
111 MODULE_LICENSE("GPL");

blkdev.c





举报

相关推荐

0 条评论