Lover雪儿
想念时,就看看天空,无论距离有多远,我们总在同一片天空下!

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.定义各种结构体指针

2.实现fileoperation结构体

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

3.在初始化函数中

如图所示:

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

4.实现读写函数

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

5.在exit函数中

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

6.编译命令

Insmod blk.ko

 

ll /dev/blkdev

 

mkfs.ext3 /dev/blkdev

 

fdisk /dev/blkdev

 

 

 

附驱动源程序:

  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

 


 
 

 

posted on 2015-03-10 20:35  Lover雪儿  阅读(335)  评论(0编辑  收藏  举报