块设备驱动程序的框架

块设备和前面的字符设备驱动主体框架和编程思想上基本相同,但是在操作的时候就不一样了。

框架:

app: open,read,write "1.txt"
--------------------------------------------- 文件的读写
文件系统: vfat, ext2, ext3, yaffs2, jffs2 (把文件的读写转换为扇区的读写)
-----------------ll_rw_block----------------- 扇区的读写
1. 把"读写"放入队列
2. 调用队列的处理函数(优化/调顺序/合并)
块设备驱动程序
---------------------------------------------
硬件: 硬盘,flash

分析ll_rw_block
for (i = 0; i < nr; i++) {
  struct buffer_head *bh = bhs[i];
  submit_bh(rw, bh);
    struct bio *bio; // 使用bh来构造bio (block input/output)
    submit_bio(rw, bio);
      // 通用的构造请求: 使用bio来构造请求(request)
      generic_make_request(bio);
      __generic_make_request(bio);
        request_queue_t *q = bdev_get_queue(bio->bi_bdev); // 找到队列

      // 调用队列的"构造请求函数"
      ret = q->make_request_fn(q, bio);
        // 默认的函数是__make_request
        __make_request
          // 先尝试合并
          elv_merge(q, &req, bio);

          // 如果合并不成,使用bio构造请求
          init_request_from_bio(req, bio);

          // 把请求放入队列
          add_request(q, req);

          // 执行队列
          __generic_unplug_device(q);
          // 调用队列的"处理函数"
          q->request_fn(q);

怎么写块设备驱动程序呢?
1. 分配gendisk: alloc_disk
2. 设置
2.1 分配/设置队列: request_queue_t // 它提供读写能力
blk_init_queue
2.2 设置gendisk其他信息 // 它提供属性: 比如容量
3. 注册: add_disk

参考:
drivers\block\xd.c
drivers\block\z2ram.c

/* 参考:
 * drivers\block\xd.c
 * drivers\block\z2ram.c
 */

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/genhd.h>
#include <linux/hdreg.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/blkdev.h>
#include <linux/blkpg.h>
#include <linux/delay.h>
#include <linux/io.h>

#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/dma.h>

static struct gendisk *ramblock_disk;
static request_queue_t *ramblock_queue;

static int major;

static DEFINE_SPINLOCK(ramblock_lock);

#define RAMBLOCK_SIZE (1024*1024)
static unsigned char *ramblock_buf;

static int ramblock_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
    /* 容量=heads*cylinders*sectors*512 */
    geo->heads     = 2;
    geo->cylinders = 32;
    geo->sectors   = RAMBLOCK_SIZE/2/32/512;
    return 0;
}

static struct block_device_operations ramblock_fops = {
    .owner    = THIS_MODULE,
    .getgeo    = ramblock_getgeo,
};

static void do_ramblock_request(request_queue_t * q)
{
    static int r_cnt = 0;
    static int w_cnt = 0;
    struct request *req;

    //printk("do_ramblock_request %d\n", ++cnt);

    while ((req = elv_next_request(q)) != NULL) {
        /* 数据传输三要素: 源,目的,长度 */
        /* 源/目的: */
        unsigned long offset = req->sector * 512;

        /* 目的/源: */
        // req->buffer

        /* 长度: */
        unsigned long len = req->current_nr_sectors * 512;

        if (rq_data_dir(req) == READ)
        {
            //printk("do_ramblock_request read %d\n", ++r_cnt);
            memcpy(req->buffer, ramblock_buf+offset, len);
        }
        else
        {
            //printk("do_ramblock_request write %d\n", ++w_cnt);
            memcpy(ramblock_buf+offset, req->buffer, len);
        }        

        end_request(req, 1);
    }
}

static int ramblock_init(void)
{
    /* 1. 分配一个gendisk结构体 */
    ramblock_disk = alloc_disk(16); /* 次设备号个数: 分区个数+1 */

    /* 2. 设置 */
    /* 2.1 分配/设置队列: 提供读写能力 */
    ramblock_queue = blk_init_queue(do_ramblock_request, &ramblock_lock);
    ramblock_disk->queue = ramblock_queue;

    /* 2.2 设置其他属性: 比如容量 */
    major = register_blkdev(0, "ramblock");  /* cat /proc/devices */
    ramblock_disk->major       = major;
    ramblock_disk->first_minor = 0;
    sprintf(ramblock_disk->disk_name, "ramblock");
    ramblock_disk->fops        = &ramblock_fops;
    set_capacity(ramblock_disk, RAMBLOCK_SIZE / 512);

    /* 3. 硬件相关操作 */
    ramblock_buf = kzalloc(RAMBLOCK_SIZE, GFP_KERNEL);

    /* 4. 注册 */
    add_disk(ramblock_disk);

    return 0;
}

static void ramblock_exit(void)
{
    unregister_blkdev(major, "ramblock");
    del_gendisk(ramblock_disk);
    put_disk(ramblock_disk);
    blk_cleanup_queue(ramblock_queue);

    kfree(ramblock_buf);
}

module_init(ramblock_init);
module_exit(ramblock_exit);

MODULE_LICENSE("GPL");

测试方法:

在开发板上:
1. insmod ramblock.ko
2. 格式化: mkdosfs /dev/ramblock
3. 挂接: mount /dev/ramblock /tmp/
4. 读写文件: cd /tmp, 在里面vi文件
5. cd /; umount /tmp/
6. cat /dev/ramblock > /mnt/ramblock.bin
7. 在PC上查看ramblock.bin
sudo mount -o loop ramblock.bin /mnt

原文地址:https://www.cnblogs.com/x2i0e19linux/p/11758872.html

时间: 2024-10-09 06:11:04

块设备驱动程序的框架的相关文章

11 Linux 块设备驱动程序

参考:https://www.cnblogs.com/big-devil/p/8590007.html Linux 块设备驱动程序 概念补充: 块 扇区是硬件数据传输的基本单元,块则是虚拟文件系统传输数据的基本单位.Linux内核中,块的大小必须是2的次幂,但不能超过一个页(4K)的大小. 段 同一物理页面中的硬盘存储介质上连续的多个块组成一个段.段的大小与块的个数有关,段包含多个块,块包含多个扇区.段在内核中由bio_vec结构体描述,多个段的信息存放于结构体bio中的biz_io_vec数组

Linux中块设备驱动程序分析

基于<Linux设备驱动程序>书中的sbull程序以对Linux块设备驱动总结分析. 开始之前先来了解这个块设备中的核心数据结构: struct sbull_dev { int size;                       /* Device size in sectors */ u8 *data;                       /* The data array */ short users;                    /* How many users

块设备驱动程序

通用块层 常用数据结构: bio 磁盘描述符 gendisk generic_make_request 是通用块层的入口点 io调度层: 请求队列:request_queue 请求描述符:request 块设备: block_device 注册块设备 register_blkdev    预定主设备号. 块设备文件操作描述符表: open  blkdev_open release blkdev_close llseek block_llseek read genric_file_read wrt

20150310 块设备驱动程序

20150310 块设备驱动程序 2015-03-10 李海沿 接下来我们来实现块设备驱动程序. 一.块设备结构体 1. file_operations 结构体 和字符设备驱动中file_operations 结构体类似,块设备驱动中也有一个 block_device_operations 结构体,它的声明位于/include/linux 录下的fs.h 文件中,它是对 块操作的集合. struct block_device_operations{ int(*open)(struct inode

linux下块设备驱动程序

块设备不能向字符设备那样访问,而是要先将请求放入队列,优化调整顺序后再执行,这种访问方式称为"电梯调度算法". 本篇文章通过ramdisk.nand flash.nor flash来讲解如何写块设备驱动程序. 一.ramdisk 1.因为块设备驱动程序是将请求放入队列然后调整顺序后执行,所以我们需要先定义请求队列: static unsigned char *ramblock_buf; ramblock_buf = kzalloc(RAMBLOCK_SIZE, GFP_KERNEL);

Linux嵌入式驱动学习之路(二十一)字符设备驱动程序总结和块设备驱动程序的引入

字符设备驱动程序 应用程序是调用C库中的open read write等函数.而为了操作硬件,所以引入了驱动模块. 构建一个简单的驱动,有一下步骤. 1. 创建file_operations 2. 申请设备号 3. 注册字符设备驱动, 4. 驱动入口 5. 驱动出口 检查数据是否到来的方式: 1. 查询方式 2. 休眠唤醒方式 如果设备出现异常而无法唤醒时,则将永远处于休眠状态. 3. poll机制 如果没有被唤醒,则在一定时间内可自己唤醒. 4. 异步通知(信号) 而以上的几种方式通用性不高,

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

简介 一个块设备驱动程序主要通过传输固定大小的随机数据来访问设备 Linux内核视块设备为与字符设备相异的基本设备类型 Linux块设备驱动程序接口使得块设备可以发挥其最大的功效,但是其复杂程序又是编程者必须面对的一个问题 一个数据块指的是固定大小的数据,而大小的值由内核确定 数据块的大小通常是4096个字节,但是可以根据体系结构和所使用的文件系统进行改变 与数据块对应的是扇区,它是由底层硬件决定大小的一个块,内核所处理的设备扇区大小是512字节 如果要使用不同的硬件扇区大小,用户必须对内核的扇

深入理解Linux内核-块设备驱动程序

扇区: 1.硬盘控制器将磁盘看成一大组扇区2.扇区就是一组相邻字节3.扇区按照惯例大小设置位512字节4.存放在块设备中的数据是通过它们在磁盘上的位置来标识,即首个扇区的下标和扇区的数目.5.扇区是硬件设备传送数据到基本单位. 块:1.块是VFS和文件系统传送数据到基本单位.它对应磁盘上一个或者多个相邻扇区.2.内核访问一个文件内容时,它先从磁盘上读文件的磁盘索引节点所在的块3.Linux中,块必须是2的幂,且不能超过一个页框(一般4kB),还必须是扇区大小的整数倍.4.对块设备文件的读写操作时

嵌入式驱动开发之--- 虚拟磁盘SBULL块设备驱动程序分析

#define SBULL_MINORS  16         /* 每个sbull设备所支持的次设备号的数量 */ #define KERNEL_SECTOR_SIZE 512  // 本地定义的常量,使用该常量进行内核512字节到实际 // 扇区大小的转换 #define INVALIDATE_DELAY  30*HZ 块设备的核心数据结构(the internal representation of our device): struct sbull_dev{ int size;