块设备驱动学习笔记(一)

 

一、块设备与字符设备的I/O操作比较

n         块设备只能以块为单位接受输入和返回输出。字符设备则以字节为单位。(linux中的块可以是字节)

n         块设备对于I/O请求有缓冲区。可以对读写的顺序进行调整。字符设备只能顺序读写。

二、相关概念与重要结构

n         块设备处理的I/O请求用request结构体表征。由于此结构体过于庞大,只列出部分重要成员。

struct request {

……

       sector_t sector;

       unsigned long nr_sectors;

       struct bio *bio;

       struct gendisk *rq_disk;

       char *buffer;

       unsigned char *cmd;

……

              }

 

sector,I/O请求中要处理的扇区号

sectors,要处理的扇区数目

rq_disk,操作对应的磁盘指针

buffer,读写缓冲区

cmd,可以控制语法方向

bio,I/O调度算法可以将连续的bio合并成一个request。request是bio经由块层调整后的结果。

n         请求队列request_uqueue

n         上层传递给块层的I/O请求bio

n         块设备操作集合block_device_operations

struct block_device_operations {

       int (*open) (struct inode *, struct file *);

       int (*release) (struct inode *, struct file *);

       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,

                                          void **, unsigned long *);

       int (*media_changed) (struct gendisk *);

       int (*revalidate_disk) (struct gendisk *);

       int (*getgeo)(struct block_device *, struct hd_geometry *);

       struct module *owner;

};

open和release方法通常在块设备挂载、卸载文件系统到相应目录时调用。

ioctl,一些设备的特殊控制请求

media_changed,介质改变

revalidate_disk,使介质有效

getgeo,获得驱动器信息

n         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; /* [indexed by minor] */

       struct block_device_operations *fops;

       struct request_queue *queue;

       void *private_data;

       sector_t capacity;

       ……

};

part,设备分区

fops,块设备操作集合

private_data,一般会放置设备的结构体。

三、结构体相关操作

n         gendisk

1.       分配gendisk

struct gendisk *alloc_disk(int minors);

minors为磁盘分区的数量

2.       增加gendisk

void add_disk(struct gendisk *disk);

初始化工作完成后,并能响应磁盘请求时,才用此函数来注册设备

3.       释放gendisk

void del_gendisk(struct gendisk *gp);

4.       gendisk引用计数

struct kobject *get_disk(struct gendisk *disk);

void put_disk(struct gendisk *disk);

n         request

1.遍历request中的bio

__rq_for_each_bio(struct bio *_bio,struct request *rq)

#define __rq_for_each_bio(_bio, rq)     \

       if ((rq->bio))                 \

              for (_bio = (rq)->bio; _bio; _bio = _bio->bi_next)

_bio是rq的一个结构体成员

2.request传送的数据方向

       rq_data_dir(struct request *req);

n         request_queue

1.初始化请求队列

request_queue_t *blk_init_queue(request_fn_proc *rfn,spinlock_t *lock);

2.清除请求队列

void blk_cleanup_queue(request_queue_t *q);

3.分配“请求队列”

request_queue_t *blk_alloc_queue(int gfp_mask);

4.绑定请求队列和“制造请求”函数

void blk_queue_make_request(request_queue_t *q,make_request_fn *mfn);

5.       提取请求

struct request *elv_next_request(struct request_queue *q);

6.       去除请求

void blkdev_dequeue_request(struct request *req);

n         bio

1.       遍历bio中的每个段

#define bio_for_each_segment(bvl, bio, i)                        

       __bio_for_each_segment(bvl, bio, i, (bio)->bi_idx)

 

__bio_for_each_segment(struct bio_vec *bvl, struct bio *bio, i,start_idx)

 

bvl, 由bvl=bio_iovec_idx(bio,start_idx)转化而来,意思为将bio结构体中,成员bio_io_vec[start_idx]的值取出。值的结构即struct bio_vec。

start_idx,当前bvl_vec的索引 (bio)->bi_idx

i,一个符号整形的变量。

 

2.       结合request中的__rq_for_each_bio()函数,遍历一个request中的segment(段)函数

#define rq_for_each_segment(bvl, _rq, _iter)                    \

       __rq_for_each_bio(_iter.bio, _rq)                \

              bio_for_each_segment(bvl, _iter.bio, _iter.i)

_iter,结构为struct req_iterator

 

n         块设备

1.注册

int register_blkdev(unsigned int major,const char *name);

2.注销

int unregister_blkdev(unsigned int major,const char *name);

register_blkdev,完成的两件事情

a.如果需要,分配一个动态设备号

b.在/proc/devices中创建一个入口

四、说明与使用

request_queue队列中存储了多个request,I/O调度器可以合并邻近request,排序request,使其高效率的被响应。每个request里面有多个来自上层的bio请求。每个bio中,又有多个bio_vec数组成员。每个bio_vec成员 记录了要访问的页的确实位置。

块设备的驱动程序编写分为以下两种:

使用请求(request)

不使用请求

使用请求对于一个机械的磁盘设备而言,有助于提高系统的性能。对于如数码相机的存在卡,RAM盘等完全可真正随机访问的设备而言,使用请求队列是无益的。在这种情况下,驱动必须提供一个“制造请求”函数。

 

块设备加载函数模板(无使用请求)

static int __int xxx_init(void)

{

//申请磁盘,注册块设备。它们是通过初始化磁盘联系到一起的

       xxx_disks = alloc_disk(1);

       if(!xxx_disk)

              goto out;

       if(register_blkdev(XXX_MAJOR,”xxx”)){

              err=-EIO;

              goto out;

              }

/*申请请求求队列(request_queue),因为请求队列中无请求,所以要与“制造请求函数绑定”*/

       xxx_queue=blk_alloc_queue(GFP_KERNEL);

       if(!xxx_queue)

              goto out_queue;

       blk_queue_make_request(xxx_queue,&xxx_make_request);

//告知内核设备硬件扇区的大小。

       blk_queue_hardsect_size(xxx_queue,xxx_blocksize);

//磁盘初始化

       xxx_disks->major=XXX_MAJOR;

       xxx_disks->first_minor=0;

       xxx_disks->fops=&xxx_op;

       xxx_disks->queue=xxx_queue;

       sprintf(xxx_disks->disk_name,”xxx%d”,i);

//设置磁盘容量

       set_capacity(xxx_disks,xxx_size*2);

       add_disk(xxx_disks);

       return 0;

       out_queue:unregister_blkdev(XXX_MAJOR,”xxx”);

       out:put_disk(xxx_disks);

       blk_cleanup_queue(xxx_queue);

      

       return –ENOMEM;

}

 

若是使用请求,只需要将申请请求队列和绑定“制造请求”函数用以下语句替代

xxx_queue = blk_init_queue(xxx_request,xxx_lock);

if(!xxx_queue)

goto out_queue;

 

卸载函数模板

static void __exit xxx_exit(void)

{

       if(bdev){

              invalidate_bdev(xxx_bdev,1);

              blkdev_put(xxx_bdev);

              }

       del_gendisk(xxx_disks);

       put_disk(xxx_disks);

       blk_cleanup_queue(xxx_queue[i]);

       unregister_blkdev(XXX_MAJOR, “xxx”);

}

 

 

块设备的打开与释放

static int xxx_open(struct block_device *bdev,fmode_t mode)

{

       struct xxx_dev *dev = bdev->bd_disk->private_data;

       ……

       return 0;

}

 

static int xxx_release(struct gendisk *disk,fmode_t mode)

{

       struct xxx_dev *dev = disk->private_data;

       ……

       return 0;

}

 

 

你可能感兴趣的:(驱动笔记)