块设备

来源:互联网 发布:office 2013 mac 编辑:程序博客网 时间:2024/04/28 16:46

一 。注册模块
  1。注册 register_blkdev(major,name,struct block_device_operation *p);
  2.初始化队列:
   
      多队列  blk_init_queue(request_queue_t *,request_fn);
                blk_dev[major]=sbull_find_queue;
     无队列  blk_queue_make_request(BLK_DEFAULT_QUEUE(major),sbull_make_request);
     单队列  blk_init_queue(BLK_DEFAULT_QUEUE(major),sbull_request);
  3.注册分区设备  register_disk(NULL,MKDEV(major,i),1,&sbull_bdops,sbull_size<<1);
二  。结构
   struct  block_device_operation  sbull_bdops={
           open:                 sbull_open,
           release:              sbull_release,
           ioctl:                sbull_ioctl,
            check_media_change:  sbull_check_change,
            revalidate:          sbull_revalidate,
           };
     在linux/blk.h (MAJOR_NR,CURRENT等macro变量);linux/blkdev.h(blk_dev[MAX],blk_size, blksize_size,request_queue_t等结构申明);在drivers/block/ll_rw_blk.c(包含 blk_init_queue等函数的实现);

三 请求处理函数
      sbull_request(request_queue_t *p);
四  支持常用的对快设备操作的命令
      fdisk mount mkfs
     例如  mount 的过程  1。open设备;2。调用request处理方法,传输数据块。

五  可分区设备
     1。在driver/block/genhd.c中的struct gendisk *gendisk_head 为static 所以虽然在linux/genhd.h中申明为extern struct gendisk *gendisk_head ;但在我们的程式中还是出现unresolved symbol ,所以在我们的程式中还得申明一下struct gendisk *gendisk_head 。
    2。申明一个struct gendisk  spull_gendisk 并初始化各成员。
    3。在revalidate函数中重新调用register_disk(struct gendisk *gd,int driver,unsigned minors,struct block_device_operation *ops,long size)注册分区。
   4。模块注册过程和sbull相同。
   5. 中断处理过程,在请求处理函数中启动定时器,然后在定时器函数中end_request(1);
       普通的请求处理函数是在本函数中直接调用end_request(1)的。
       假如驱动程式是中断驱动的,request 函数应该提交一次数据传输并立即返回,而无需调用 end_request。但是,在没有调用 end_request(或其组成部分)之前,不会认为请求已       完成。因此,在设备告诉驱动程式已完成数据传输时,顶半或底半中断处理程式需要调用 end_request。
请求队列
struct request_queue
{
....
spinlock_t __queue_lock;
spinlock_t *queue_lock;
struct kobject kobj;
unsigned long nr_requests; //max req
unsigned int nr_congestion_on;
unsigned int nr_congestion_off;
unsigned int nr_batching;
unsigned short max_sectors ;
unsigned short max_hw_sectors;
unsigned short max_phys_segments;
unsigned short max_hw_segment;
unsigned short hardsect_size;
unsigned int max_segment_size;
unsigned long seg_boundary_mask;
unsigned dma_alignment;
struct blk_queue_tag *queue_tags;
atomic_t refcnt;
unsigned int sg_timeout;
unsigned int sg_reserved_size;
int node;
struct list_head drain_list;
struct request *flush_rq;
unsigned char ordered;
}
请求队列跟踪等候的块请求。
4个IO调度器:
No-op  基本的合并和排序
Anticipatory  默认,但复杂,且不适合 数据库
Deadline 精简上一个
CFQ  Scheduler 为系统内所有任务分配相同带宽,适合桌面。
可以个kernel 添加参数 elevator= deadline 选取IO调度算法。
=======================================================
初始化请求队列
request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t lock);
第一个参数为请求处理函数的指针,第二个为控制访问队列的自旋锁,注意检查返回值
void blk_cleanup_queue(request_queue_t *q); blk_put_queue;
分配
request_queue_t * blk_alloc_queue(int gfp_mask);
void blk_queue_make_request(request_queue_t *q, make_request_fn  *mfn);
提取请求
struct request *elv_next_request(request_queue_t *queue);
上述函数返回下一个要处理的请求。
去除请求
struct request *elv_dequeue_request(struct request *req);
struct request *elv_requeue_request(request_queue_t *queue,struct request *req);
启动停止请求队列
void blk_stop_queue(request_queue_t *queue);
void blk_start_queue(request_queue_t *queue);
参数设置
  void blk_queue_max_sectors(request_queue_t *queue, unsigned short max);
任一请求可包含的最大扇区数
  void blk_queue_max_phys_segments(request_queue_t *queue, unsigned short max);
可包含的物理段(内存不相连)128
void blk_queue_max_hw_segments(request_queue_t *queue, unsigned short max);
考虑系统IO内存管理单元的重映射 128
void blk_queue_max_segment_size(request_queue_t *queue, unsigned short max);
段最大字节数   65536
通告内核
void blk_queue_bounce_limit(request_queue_t *queue, u64 dma_addr);
 告知内核块设备执行DMA时可使用最高物理地址dma_addr
 BLK_BOUNCE_HIGH(对高端内存使用反弹缓冲)默认
 BLK_BOUNCE_ISA 16M得ISA区DMA
 BLK_BOUNCE_ANY 可在任何地方DMA
blk_queue_segment_boundary(request_t *queue, unsigned long mask);
如果设备无法跨越一个特殊大小的内存边界, 应该使用这个函数告知内核这个边界。
void blk_queue_dma_alignment(request_queue_t *queue, int mask) 缺省0x1ff
告知内核块设备施加于DMA传送的堆存对齐限制,所有请求都匹配这个对齐
void blk_queue_hardsect_size(request_queue_t *queue,unsigned short max);
告知内核块设备的硬件扇区大小,则产生的请求都是大小的整数倍,且对齐  512
===========================================================================
通常一个bio对应一个IO请求
struct bio
{
sector_t bi_sector;//要传送的第一个扇区
struct bio *bi_next;
struct block_device *bi_bdev;
unsigned long bi_flags;//状态命令
usnigned long bi_rw//地位RW,高位优先级
unsigned short bi_vcnt;//biovec 数量
unsigned short bi_idx;//当前bio_vec索引
unsigned short bi_phys_segments;//不相连的物理段数
unsigned short bi_hw_segments;
unsigned int bi_size;
unsigned  int bi_hw_front_size;
unsigned int bi_hw_back_size;
unsigned int  bi_max_vecs;//能持有最大 bvl_vecs
struct bio_vec *bio_io_vec;
bio_end_io_t *bi_end_io;
atomic bi_cnt;
void *bi_private;
bio_destructor_t *bio_destructor;
};
struct bio_vec
{struct page *bv_page;//页指针
  unsigned int bv_len;//传送的字节数
  unsigned int bv_offset;//偏移位置
}
应该使用 bio_for_each_segment()宏来便利 bio
#define __bio_for_each_segment(bvl,bio,i,start_idx)   /
 for(bvl=bio_iovec_idx((bio),(start_idx)), i=(start_idx) /
  i< ((bio)->bi_vcnt; /
  bvl++,i++)
#define bio_for_each_segment(bvl, bio,i)  /
  __bio_for_each_segment(bvl, bio, i, (bio)->bi_idx)
内核提供的函数用于bio:
int bio_data_dir(struct bio *bio);数据传送方向READ WRITE
struct  page * bio_page(struct bio *bio);目前页指针
int bio_offset(struct bio *bio); 页偏移
int bio_cur_sectors(struct bio *bio); 要传送的扇区数目
char *bio_data(struct bio *bio);数据缓冲的内核虚拟地址
char *bvec_kmap_irq(struct bio *bio,unsigned long flags); 可用于存取被
给定的 bio_vec入口指向的数据缓冲区,会屏蔽中断并返回原子kamp,调用前,不该睡眠
char *bvec_kunmap_irq(char *buffer,unsigned long flags); 撤销
char * bio_kamp_irq(struct bio *bio,unsigned long flags)
返回给定BIO的当前bio_vec入口地址映射
char * __bio_kmap_atomic (struct bio *bio, int i, enum km_type type)
返回给定BIO的第i个缓冲区的虚拟地址
char * __bio_kmunap_atomic (char *addr, enum km_type type)
void bio_get
void bio_put
===============
块设备注册
int register_blkdev(unsigned int major, const char *name);0
分配主设备号/proc/devices创建入口
int unregister_blkdev(unsigned int major, const char *name);

原创粉丝点击