《Linux Device Drivers》第十六章 块设备驱动程序——note

  • 基本介绍
    • 块设备驱动程序通过主传动固定大小数据的随机访问设备
    • Linux核心Visual块设备作为基本设备和不同的字符设备类型
    • Linux块设备驱动程序接口,使块设备最大限度地发挥其效用。一个问题
    • 一个数据块指的是固定大小的数据,而大小的值由内核确定
    • 数据块的大小一般是4096个字节。可是能够依据体系结构和所使用的文件系统进行改变
    • 与数据块相应的是扇区,它是由底层硬件决定大小的一个块,内核所处理的设备扇区大小是512字节
    • 假设要使用不同的硬件扇区大小。用户必须对内核的扇区数做相应的改动
  • 注冊
    • 注冊块设备驱动程序
      • <linux/fs.h>
      • int register_blkdev(unsigned int major, const char *name);
        • 假设须要的话分配一个动态的主设备号
        • 在/proc/devices中创建一个入口项
      • int unregister_blkdev(unsigned int major, const char *name);
    • 注冊磁盘
      • struct block_device_operations
        • int (*open) (struct inode *inode, struct file *filp);
        • int (*release) (struct inode *inode, struct file *filp);
        • int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
        • int (*media_changed) (struct gendisk *gd);
        • int (*revalidate_disk) (struct gendisk *gd);
        • struct module *owner;
      • gendisk结构
        • <linux/genhd.h>
        • struct gendisk
          • int major;
          • int first_minor;
          • int minors;
            • 常取16
          • char disk_name[32]
            • 显示在/proc/partitions和sysfs中
          • struct block_device_operations *fops;
          • struct request_queue *queue;
          • int flags;
          • sector_t capacity;
          • void *private_data;
        • struct gendisk *alloc_disk(int minors);
        • void del_gendisk(struct gendisk *gd);
        • void add_disk(struct gendisk *gd);

  • 块设备操作
    • open和release函数
      • 对于那些操作实际硬件设备的驱动程序,open和release函数可以设置驱动程序和硬件的状态。这些操作包含使磁盘開始或者停止旋转,锁住可移动介质的仓门以及分配DMA缓存等
      • 有一些操作可以让块设备在用户空间内被直接打开,这些操作包含给磁盘分区。或者在分区上创建文件系统,或者执行文件系统检查程序
    • 对可移动介质的支持
      • 调用media_changed函数以检查介质是否被改变
      • 在介质改变后将调用revalideate函数
    • ioctl函数
      • 高层的块设备子系统在驱动程序获得ioctl命令前。已经截取了大量的命令
      • 实际上在一个现代驱动程序中。很多ioctl命令根本就不用实现
  • 请求处理
    • 每一个块设备驱动程序的核心是它的请求函数
    • 驱动程序所须要知道的不论什么关于请求的信息。都包括在通过请求队列传递给我们的结构中
    • request函数介绍
      • void request(request_queue_t *queue);
        • 当内核须要驱动程序处理读取、写入以及其它对设备的操作时。就会调用该函数
      • 每一个设备都有一个请求队列
        • dev->queue = blk_init_queue(test_request, &dev->lock);
      • 对request函数的调用是与用户空间进程中的动作全然异步的
    • 一个简单的request函数
      • struct request * elv_next_request(request_queue_t queue);
      • void end_request(struct request *req, int succeeded);
      • struct request
        • sector_t secotr;
        • unsigned long nr_sectors;
        • char *buffer
        • rq_data_dir(struct request *req);
    • 请求队列
      • 一个块设备请求队列能够这样描写叙述:包括块设备I/O请求的序列
      • 请求队列跟踪未完毕的块设备的I/O请求
      • 请求队列还实现了插件接口
      • I/O调度器还负责合并邻近的请求
      • 请求队列拥有request_queue或request_queue_t结构类型
      • <linux/blkdev.h>
      • 队列的创建与删除
        • request_queue_t *blk_init_queue(request_fn_proc *request, spinlock_t *lock);
        • void blk_cleanup_queue(request_queue_t *queue);
      • 队列函数
        • struct request *elv_next_request(request_queue_t *queue);
        • void blkdev_dequeue_request(struct request *req);
        • void 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_bounce_limit(request_queue_t *queue, u64 dma_addr);
        • 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);
        • void blk_queue_max_hw_segments(request_queue_t *queue, unsigned short max);
        • void blk_queue_max_segment_size(request_queue_t *queue, unsigned short max);
        • void blk_queue_segment_boundary(request_queue_t *queue, unsigned long mask);
        • void blk_queue_dma_alignment(request_queue_t *queue, int mask);
        • void blk_queue_hardsect_size(request_queue_t *queue, unsigned short max);
      • 请求过程剖析
        • 从本质上讲,一个request结构是作为一个bio结构的链表实现的
        • bio结构
          • bio结构包括了驱动程序运行请求的所有信息,而不必与初始化这个请求的用户空间的进程相关联
          • <linux/bio.h>
          • struct bio
            • sector_t bi_sector;
            • unsigned int bi_size;
              • 以字节为单位所须要传输的数据大小
            • unsigned long bi_flags;
            • unsigned short bio_phys_segments;
            • unsigned short bio_hw_segments;
            • struct bio_vec *bi_io_vec
          • struct bio_vec
            • struct page *vb_page;
            • unsigned int bv_len;
            • unsigned int bv_offset;
          • example
            • int segno;
            • struct bio_vec *bvec;
            • bio_for_each_segment(bvec, bio, segno)
            • {
              • /* 使用该段进行一定的操作 */
            • }
          • char *__bio_kmap_atomic(struct bio *bio, int i, enum km_type type);
          • void __bio_kunmap_atomic(char *buffer, enum km_type type):
          • 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 *bio_kmap_irq(struct bio *bio, unsigned long *flags);
          • void bio_kunmap_irq(char *buffer, unsigned long *flags);
        • request结构成员
          • struct request
            • sector_t hard_sector;
            • unsigned long hard_nr_sectors;
            • unsigned int hard_cur_sectors;
            • struct bio *bio;
            • char *buffer;
            • unsigned short nr_phys_segments;
            • struct list_head queuelist;
        • 屏障请求
          • 在驱动程序接收到请求前。块设备层又一次组合了请求以提高I/O性能
          • 出于相同的目的,驱动程序也能够又一次组合请求
          • 但在无限制又一次组合请求时面临了一个问题:一些应用程序的某些操作。要在另外一些操作開始前完毕
          • 2.6版本号的块设备层使用屏障(barrier)请求来解决问题
          • 假设一个请求被设置了REQ_HARDBARRER标志。那么在其它兴许请求被初始化前,它必须被写入驱动器
          • void blk_queue_ordered(request_queue_t *queue, int flag);
          • int blk_barrier_rq(sruct request *req);
            • 假设返回一个非零值,该请求是一个屏障请求
        • 不可重试请求
          • int blk_noretry_request(struct request *req);
      • 请求完毕函数
        • int end_that_request_first(struct request *req, int success, int count);
        • void end_that_request_last(struct request *req);
        • example
          • void end_request(struct request *req, int uptodate)
          • {
            • if (!end_that_request(req, uptodate, req->hard_cur_sectors)
            • {
              • add_disk_randomness(req->rq_disk);
              • blkdev_dequeue_request(req);
              • end_that_request_last(req);
            • }
          • }
        • 使用bio
          • example
            • struct request *req
            • struct bio *bio;
            • rq_for_each_bio(bio, req)
            • {
              • /* 使用该bio结构进行一定的操作 */
            • }
        • 块设备请求和DMA
          • int blk_rq_map_sg(request_queue_t *queue, struct request *req, struct scatterlist *list);
          • clear_bit(QUEUE_FLAG_CLEAR, &queue->queue_flags);
        • 不使用请求队列
          • typedef int (make_request_fn) (request_queue_t *q, struct bio *bio);
          • void bio_endio(struct bio *bio, unsigned int bytes, int error);
          • request_queue_t *blk_alloc_queue(int flags);
            • 并未真正地建立一个保存请求的队列
          • void blk_queue_make_request(request_queue_t *queue, make_request_fn *func);
          • drivers/block/ll_rw_block.c
  • 其它一些细节
    • 命令预处理
      • typedef int (prep_rq_fn) (request_queue_t *queue, struct request *req);
        • 该函数要能返回以下的值之中的一个
          • BLKPREP_OK
          • BLKPREP_KILL
          • BLKPREP_DEFER
      • void blk_queue_prep_rq(request_queue_t *queue, prep_rq_fn *func);
    • 标记命令队列
      • 同一时候拥有多个活动请求的硬件通常支持某种形式的标记命令队列(Tagged Command Queueing, TCQ)
      • TCQ仅仅是为每一个请求加入一个整数(标记)的技术,这样当驱动器完毕它们中的一个请求后,它就能够告诉驱动程序完毕的是哪个
      • int blk_queue_int_tags(request_queue_t *queue, int depth, struct blk_queue_tag *tags);
      • int blk_queue_resize_tags(request_queue_t *queue, int new_depth);
      • int blk_queue_start_tag(request_queue_t *queue, struct request *req);
      • void blk_queue_end_tag(request_queue_t *queue, struct request *req);
      • struct request *blk_queue_find_tag(request_queue_t *queue, int tag);
      • void blk_queue_invalidate_tags(request_queue_t *queue);

版权声明:本文博客原创文章,博客,未经同意,不得转载。

posted @ 2015-07-03 19:57  blfshiye  阅读(171)  评论(0编辑  收藏  举报