• 基本介绍

    • 块设备驱动程序通过主传动固定大小数据的随机访问设备
    • 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);

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

《Linux Device Drivers》第十六章 块设备驱动程序——note的更多相关文章

  1. 鸟哥的linux私房菜——第十六章学习(程序管理与 SELinux 初探)

    第十六章.程序管理与 SE Linux 初探 在 Linux 系统当中:"触发任何一个事件时,系统都会将他定义成为一个程序,并且给予这个程序一个 ID ,称为 PID,同时依据启发这个程序的 ...

  2. Linux 0.11源码阅读笔记-块设备驱动程序

    块设备驱动程序 块设备驱动程序负责实现对块设备数据的读写功能.内核代码统一使用缓冲块间接和块设备(如磁盘)交换数据,缓冲区数据通过块设备驱动程序和块设备交换数据. 块设备的管理 块设备表 内核通过一张 ...

  3. 《Linux Device Drivers》 第十七章 网络驱动程序——note

    基本介绍 第三类是标准的网络接口Linux设备,本章介绍的内核,其余的交互网络接口描述 网络接口,必须使用特定的内核数据结构本身注册,与外部分组交换数据线打电话时准备 经常使用的文件上的网络接口操作是 ...

  4. 《Linux Device Drivers》第十一章 核心数据类型——note

    基本介绍 因为Linux多平台特性,不管是哪一个重要驱动力应该是便携 与内核代码相关的核心问题应该是访问的同时是数据项的已知长度.能力和利用不同的处理器 内核使用的数据类型主要分为三类 类似int这种 ...

  5. 鸟哥的Linux私房菜——第十六章:学习Shell Scripts

    视频链接:http://www.bilibili.com/video/av10565321/ 1. 什么是 Shell Script       (shell写的脚本)1.1 干嘛学习 shell s ...

  6. 简单linux块设备驱动程序

    本文代码参考<LINUX设备驱动程序>第十六章 块设备驱动程序 本文中的“块设备”是一段大小为PAGE_SIZE的内存空间(两个扇区,每个扇区512字节) 功能:向块设备中输入内容,从块设 ...

  7. 《Linux命令行与shell脚本编程大全》 第十六章 学习笔记

    第十六章:创建函数 基本的脚本函数 创建函数 1.用function关键字,后面跟函数名 function name { commands } 2.函数名后面跟空圆括号,标明正在定义一个函数 name ...

  8. Gradle 1.12用户指南翻译——第三十六章. Sonar Runner 插件

    本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  9. 鸟哥的Linux私房菜笔记第六章(二)

    文件内容查询 直接查询文件内容 查阅一个文件的内容可以使用指令cat/tac/nl. # [cat|tac|nl] 文件 区别: 1.cat是直接把文件内容输出到屏幕上,并且从第一行开始输出到末行 2 ...

随机推荐

  1. 2014 CSDN博文大赛终于获奖名单发布

    博文大赛第二阶段(2014年7月15日-2014年8月10日)已经结束,决赛获奖名单已在8月11日出炉. 现将获奖名单发布: 移动开发 NO.1    罗升阳    Luoshengyang    S ...

  2. C++--allocator类的使用

    C++为我们提供了安全的内存空间申请方式与释放方式,可是new与delete表达式却是把空间的分配回收与对象的构建销毁紧紧的关联在一起.实际上,作为与C语言兼容的语言,C++也为我们提供了更加底层的内 ...

  3. C#的百度地图开发(二)转换JSON数据为相应的类

    原文:C#的百度地图开发(二)转换JSON数据为相应的类 在<C#的百度地图开发(一)发起HTTP请求>一文中我们向百度提供的API的URL发起请求,并得到了返回的结果,结果是一串JSON ...

  4. IntelliJ IDEA中怎样使用JUnit4

     背景 近期參与了一个Anroid医疗项目,当中项目底层有非常多基础类及通讯类,并且非常多涉及复杂的字节操作还有多线程同步及状态机处理.这种项目做一下TDD还是必要的,尽量项眼下期把风险减少一些. ...

  5. prepareCall()运行存储过程

    CallableStatement 对象为全部的 DBMS 提供了一种以标准形式调用已储存过程的方法.已储存过程储存在数据库中.对已储存过程的调用是 CallableStatement对象所含的内容. ...

  6. cfa,cpa,

    CFA考试内容分为三个不同级别,分别是方式是Level I.Level II和Level III. 考试在全球各个地点统一举行,每个考生必须依次完成三个不同级别的考试.CFA资格考试采用全英文,候选人 ...

  7. Windows Phone开发(37):动画之ColorAnimation

    原文:Windows Phone开发(37):动画之ColorAnimation 上一节中我们讨论了用double值进行动画处理,我们知道动画是有很多种的,今天,我向大家继续介绍一个动画类--Colo ...

  8. python之字符串的分割和拼接

    关于string的split 和 join 方法 对导入os模块进行os.path.splie()/os.path.join() 貌似是处理机制不一样,但是功能上一样. 1.string.split( ...

  9. 跨平台移动框架iMAG开发入门

    iMAG是一个非常简洁高效的移动跨平台开发框架,开发一次能够同一时候兼容Android和iOS平台,有点儿Web开发基础就能非常快上手.当前移动端跨平台开发的框架有非常多,但用iMAG另一个优点,就是 ...

  10. leetCode Min Stack解决共享

    原标题:https://oj.leetcode.com/problems/min-stack/ Design a stack that supports push, pop, top, and ret ...