参考:【块设备】通用块层 struct bio 详解 | zzm (aliez22.github.io)

一、Linux 中块设备驱动框架

二、块设备基本概念

1、扇区的概念来自硬件,扇区是硬件最小操作单位。

2、的概念来自文件系统,是文件系统数据处理的最小单位。

3、的概念来自操作系统,是内核对内存管理机制的最小单位。

4、的概念来自操作系统,是内核内存映射管理的最小单位。

三、磁盘分区相关概念

硬盘分区的相关概念(主分区,扩展分区,逻辑分区,MBR,DBR) - 假程序猿 - 博客园 (cnblogs.com)

四、块设备驱动框架中几个重要对象

1、逻辑块设备

struct block_device {dev_t           bd_dev;  /* not a kdev_t - it's a search key */int         bd_openers;struct inode *       bd_inode;   /* will die */struct super_block *  bd_super;struct mutex       bd_mutex;   /* open/close mutex */struct list_head  bd_inodes;void *            bd_claiming;void *          bd_holder;int           bd_holders;bool         bd_write_holder;
#ifdef CONFIG_SYSFSstruct list_head bd_holder_disks;
#endifstruct block_device * bd_contains;unsigned        bd_block_size;struct hd_struct *    bd_part;/* number of times partitions within this device have been opened. */unsigned       bd_part_count;int           bd_invalidated;struct gendisk * bd_disk;struct request_queue *  bd_queue;struct list_head   bd_list;/** Private data.  You must have bd_claim'ed the block_device* to use this.  NOTE:  bd_claim allows an owner to claim* the same device multiple times, the owner must take special* care to not mess up bd_private for that case.*/unsigned long       bd_private;/* The counter of freeze processes */int         bd_fsfreeze_count;/* Mutex for freeze */struct mutex        bd_fsfreeze_mutex;
};

1、块设备注册

/*** register_blkdev - register a new block device** @major: the requested major device number [1..255]. If @major=0, try to*         allocate any unused major number.* @name: the name of the new block device as a zero terminated string** The @name must be unique within the system.** The return value depends on the @major input parameter.*  - if a major device number was requested in range [1..255] then the*    function returns zero on success, or a negative error code*  - if any unused major number was requested with @major=0 parameter*    then the return value is the allocated major number in range*    [1..255] or a negative error code otherwise*/
int register_blkdev(unsigned int major, const char *name)

2、块设备注销

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

2、实际块设备

struct gendisk {/* major, first_minor and minors are input parameters only,* don't use directly.  Use disk_devt() and disk_max_parts().*/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[DISK_NAME_LEN];    /* name of major driver */char *(*devnode)(struct gendisk *gd, umode_t *mode);unsigned int events;      /* supported events */unsigned int async_events;    /* async events, subset of all *//* Array of pointers to partitions indexed by partno.* Protected with matching bdev lock but stat and other* non-critical accesses use RCU.  Always access through* helpers.*/struct disk_part_tbl __rcu *part_tbl;struct hd_struct part0;const struct block_device_operations *fops;struct request_queue *queue;void *private_data;int flags;struct device *driverfs_dev;  // FIXME: removestruct kobject *slave_dir;struct timer_rand_state *random;atomic_t sync_io;        /* RAID */struct disk_events *ev;
#ifdef  CONFIG_BLK_DEV_INTEGRITYstruct blk_integrity *integrity;
#endifint node_id;
};

1、申请 gendisk

struct gendisk *alloc_disk(int minors)

2、删除 gendisk

void del_gendisk(struct gendisk *disk)

3、将 gendisk 添加到内核

void add_disk(struct gendisk *disk)

4、设置 gendisk 容量

void set_capacity(struct gendisk *disk, sector_t size)

5、调整 gendisk 引用计数

truct kobject *get_disk(struct gendisk *disk)
void put_disk(struct gendisk *disk)

3、block_device 和 gendisk 区别


struct block_device:用来描述一个块设备或者块设备的一个分区。与文件系统关系密切。

struct gendisk:描述整个块设备的特性。块设备驱动程序的主要操作对象

对于一个包含多个分区的块设备,struct block_device 结构有多个,而 struct gendisk 结构只有一个。

4、块设备操作集

struct block_device_operations {int (*open) (struct block_device *, fmode_t);void (*release) (struct gendisk *, fmode_t);int (*rw_page)(struct block_device *, sector_t, struct page *, int rw);int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);long (*direct_access)(struct block_device *, sector_t,void **, unsigned long *pfn, long size);unsigned int (*check_events) (struct gendisk *disk,unsigned int clearing);/* ->media_changed() is DEPRECATED, use ->check_events() instead */int (*media_changed) (struct gendisk *);void (*unlock_native_capacity) (struct gendisk *);int (*revalidate_disk) (struct gendisk *);int (*getgeo)(struct block_device *, struct hd_geometry *);/* this callback is with swap_lock and sometimes page table lock held */void (*swap_slot_free_notify) (struct block_device *, unsigned long);struct module *owner;
};

5、请求队列

struct request_queue {/** Together with queue_head for cacheline sharing*/struct list_head   queue_head;struct request       *last_merge;struct elevator_queue   *elevator;int           nr_rqs[2];  /* # allocated [a]sync rqs */int            nr_rqs_elvpriv; /* # allocated rqs w/ elvpriv *//** If blkcg is not used, @q->root_rl serves all requests.  If blkcg* is used, root blkg allocates from @q->root_rl and all other* blkgs from their own blkg->rl.  Which one to use should be* determined using bio_request_list().*/struct request_list root_rl;request_fn_proc     *request_fn;make_request_fn     *make_request_fn;prep_rq_fn     *prep_rq_fn;unprep_rq_fn        *unprep_rq_fn;merge_bvec_fn     *merge_bvec_fn;softirq_done_fn      *softirq_done_fn;rq_timed_out_fn        *rq_timed_out_fn;dma_drain_needed_fn    *dma_drain_needed;lld_busy_fn       *lld_busy_fn;struct blk_mq_ops  *mq_ops;unsigned int        *mq_map;/* sw queues */struct blk_mq_ctx __percpu   *queue_ctx;unsigned int     nr_queues;/* hw dispatch queues */struct blk_mq_hw_ctx  **queue_hw_ctx;unsigned int     nr_hw_queues;/** Dispatch queue sorting*/sector_t       end_sector;struct request       *boundary_rq;/** Delayed queue handling*/struct delayed_work    delay_work;struct backing_dev_info  backing_dev_info;/** The queue owner gets to use this for whatever they like.* ll_rw_blk doesn't touch it.*/void           *queuedata;/** various queue flags, see QUEUE_* below*/unsigned long        queue_flags;/** ida allocated id for this queue.  Used to index queues from* ioctx.*/int            id;/** queue needs bounce pages for pages above this limit*/gfp_t           bounce_gfp;/** protects queue structures from reentrancy. ->__queue_lock should* _never_ be used directly, it is queue private. always use* ->queue_lock.*/spinlock_t     __queue_lock;spinlock_t     *queue_lock;/** queue kobject*/struct kobject kobj;/** mq queue kobject*/struct kobject mq_kobj;#ifdef CONFIG_PMstruct device       *dev;int            rpm_status;unsigned int     nr_pending;
#endif/** queue settings*/unsigned long     nr_requests;    /* Max # of requests */unsigned int     nr_congestion_on;unsigned int       nr_congestion_off;unsigned int      nr_batching;unsigned int        dma_drain_size;void         *dma_drain_buffer;unsigned int      dma_pad_mask;unsigned int       dma_alignment;struct blk_queue_tag  *queue_tags;struct list_head    tag_busy_list;unsigned int      nr_sorted;unsigned int      in_flight[2];/** Number of active block driver functions for which blk_drain_queue()* must wait. Must be incremented around functions that unlock the* queue_lock internally, e.g. scsi_request_fn().*/unsigned int     request_fn_active;unsigned int      rq_timeout;struct timer_list    timeout;struct list_head    timeout_list;struct list_head   icq_list;
#ifdef CONFIG_BLK_CGROUPDECLARE_BITMAP      (blkcg_pols, BLKCG_MAX_POLS);struct blkcg_gq        *root_blkg;struct list_head blkg_list;
#endifstruct queue_limits   limits;/** sg stuff*/unsigned int       sg_timeout;unsigned int     sg_reserved_size;int            node;
#ifdef CONFIG_BLK_DEV_IO_TRACEstruct blk_trace  *blk_trace;
#endif/** for flush operations*/unsigned int        flush_flags;unsigned int        flush_not_queueable:1;struct blk_flush_queue    *fq;struct list_head    requeue_list;spinlock_t     requeue_lock;struct work_struct requeue_work;struct mutex       sysfs_lock;int          bypass_depth;int            mq_freeze_depth;#if defined(CONFIG_BLK_DEV_BSG)bsg_job_fn       *bsg_job_fn;int         bsg_job_size;struct bsg_class_device bsg_dev;
#endif#ifdef CONFIG_BLK_DEV_THROTTLING/* Throttle data */struct throtl_data *td;
#endifstruct rcu_head       rcu_head;wait_queue_head_t  mq_freeze_wq;struct percpu_ref  mq_usage_counter;struct list_head   all_q_node;struct blk_mq_tag_set    *tag_set;struct list_head   tag_set_list;
};

request_queue 对象表示针对一个 gendisk 对象的所有请求的队列。

1、初始化请求队列

struct request_queue *blk_init_queue(request_fn_proc *, spinlock_t *);

2、删除请求队列

void blk_cleanup_queue(struct request_queue *);

3、制造请求函数

void blk_queue_make_request(struct request_queue *, make_request_fn *);

6、请求项

/** Try to put the fields that are referenced together in the same cacheline.** If you modify this structure, make sure to update blk_rq_init() and* especially blk_mq_rq_ctx_init() to take care of the added fields.*/
struct request {struct list_head queuelist;union {struct call_single_data csd;unsigned long fifo_time;};struct request_queue *q;struct blk_mq_ctx *mq_ctx;u64 cmd_flags;enum rq_cmd_type_bits cmd_type;unsigned long atomic_flags;int cpu;/* the following two fields are internal, NEVER access directly */unsigned int __data_len;    /* total data len */sector_t __sector;      /* sector cursor */struct bio *bio;struct bio *biotail;/** The hash is used inside the scheduler, and killed once the* request reaches the dispatch list. The ipi_list is only used* to queue the request for softirq completion, which is long* after the request has been unhashed (and even removed from* the dispatch list).*/union {struct hlist_node hash;    /* merge hash */struct list_head ipi_list;};/** The rb_node is only used inside the io scheduler, requests* are pruned when moved to the dispatch queue. So let the* completion_data share space with the rb_node.*/union {struct rb_node rb_node;  /* sort/lookup */void *completion_data;};/** Three pointers are available for the IO schedulers, if they need* more they have to dynamically allocate it.  Flush requests are* never put on the IO scheduler. So let the flush fields share* space with the elevator data.*/union {struct {struct io_cq     *icq;void           *priv[2];} elv;struct {unsigned int     seq;struct list_head    list;rq_end_io_fn       *saved_end_io;} flush;};struct gendisk *rq_disk;struct hd_struct *part;unsigned long start_time;
#ifdef CONFIG_BLK_CGROUPstruct request_list *rl;        /* rl this rq is alloced from */unsigned long long start_time_ns;unsigned long long io_start_time_ns;    /* when passed to hardware */
#endif/* Number of scatter-gather DMA addr+len pairs after* physical address coalescing is performed.*/unsigned short nr_phys_segments;
#if defined(CONFIG_BLK_DEV_INTEGRITY)unsigned short nr_integrity_segments;
#endifunsigned short ioprio;void *special;      /* opaque pointer available for LLD use */int tag;int errors;/** when request is used as a packet command carrier*/unsigned char __cmd[BLK_MAX_CDB];unsigned char *cmd;unsigned short cmd_len;unsigned int extra_len;   /* length of alignment and padding */unsigned int sense_len;unsigned int resid_len; /* residual count */void *sense;unsigned long deadline;struct list_head timeout_list;unsigned int timeout;int retries;/** completion callback.*/rq_end_io_fn *end_io;void *end_io_data;/* for bidi */struct request *next_rq;
};

1、获取请求

struct request *blk_peek_request(struct request_queue *q);

2、开启请求

void blk_start_request(struct request *req)

3、获取、开启请求

struct request *blk_fetch_request(struct request_queue *q)

7、bio

/** main unit of I/O for the block layer and lower layers (ie drivers and* stacking drivers)*/
struct bio {struct bio      *bi_next;   /* request queue link */struct block_device *bi_bdev;unsigned long      bi_flags;   /* status, command, etc */unsigned long     bi_rw;      /* bottom bits READ/WRITE,* top bits priority*/struct bvec_iter bi_iter;/* Number of segments in this BIO after* physical address coalescing is performed.*/unsigned int        bi_phys_segments;/** To keep track of the max segment size, we account for the* sizes of the first and last mergeable segments in this bio.*/unsigned int       bi_seg_front_size;unsigned int      bi_seg_back_size;atomic_t       bi_remaining;bio_end_io_t       *bi_end_io;void         *bi_private;
#ifdef CONFIG_BLK_CGROUP/** Optional ioc and css associated with this bio.  Put on bio* release.  Read comment on top of bio_associate_current().*/struct io_context    *bi_ioc;struct cgroup_subsys_state *bi_css;
#endifunion {#if defined(CONFIG_BLK_DEV_INTEGRITY)struct bio_integrity_payload *bi_integrity; /* data integrity */
#endif};unsigned short      bi_vcnt;    /* how many bio_vec's *//** Everything starting with bi_max_vecs will be preserved by bio_reset()*/unsigned short      bi_max_vecs;    /* max bvl_vecs we can hold */atomic_t      bi_cnt;     /* pin count */struct bio_vec       *bi_io_vec; /* the actual vec list */struct bio_set     *bi_pool;/** We can inline a number of vecs at the end of the bio, to avoid* double allocations for a small number of bio_vecs. This member* MUST obviously be kept at the very end of the bio.*/struct bio_vec     bi_inline_vecs[0];
};

1、遍历 bio

#define __rq_for_each_bio(_bio, rq)

2、遍历 bio 中所有段

#define bio_for_each_segment(bvl, bio, iter)

3、通知 bio 处理结束

bvoid bio_endio(struct bio *bio, int error)

8、硬件信息(bvec_iter)

struct bvec_iter {sector_t       bi_sector;  /* device address in 512 bytesectors */unsigned int     bi_size;    /* residual I/O count */unsigned int        bi_idx;     /* current index into bvl_vec */unsigned int            bi_bvec_done;   /* number of bytes completed incurrent bvec */
};

9、bio_vec

/** was unsigned short, but we might as well be ready for > 64kB I/O pages*/
struct bio_vec {struct page *bv_page;unsigned int   bv_len;unsigned int bv_offset;
};

10、bio逻辑架构

11、request_queue、request和bio关系

五、内存模拟硬盘驱动编写(使用内核请求队列)

参考:drivers\block\z2ram.c

1、编写思路

1、从 RAM 中分配内存。

2、注册逻辑块设备,为应用层提供操作对象。

3、初始化请求队列。

4、添加、初始化实际块设备,为驱动提供操作对象。

5、通过注册的请求队列函数进行数据传输(可以使用内核提供,也可以自己进行构造)。

2、驱动实现

#include "linux/init.h"
#include "linux/module.h"
#include "linux/slab.h"
#include "linux/spinlock_types.h"
#include "linux/fs.h"
#include "linux/genhd.h"
#include "linux/hdreg.h"
#include "linux/blkdev.h"#define RAMDISK_SIZE (2 * 1024 * 1024)   /* 容量大小为2MB */
#define RAMDISK_NAME    "ramdisk"         /* 名字 */
#define RADMISK_MINOR   3                   /* 表示有三个磁盘分区!不是次设备号为3! */typedef struct{unsigned char *ramdiskbuf;    /* ramdisk内存空间,用于模拟块设备 */spinlock_t lock;           /* 自旋锁 */int major;                 /* 主设备号 */struct request_queue *queue;/* 请求队列 */struct gendisk *gendisk;    /* gendisk */
}newchrdev_t;
newchrdev_t newchrdev;/** @description     : 打开块设备* @param - dev  : 块设备* @param - mode   : 打开模式* @return            : 0 成功;其他 失败*/
int ramdisk_open(struct block_device *dev, fmode_t mode)
{printk("ramdisk open\r\n");return 0;
}/** @description      : 释放块设备* @param - disk     : gendisk* @param - mode   : 模式* @return          : 0 成功;其他 失败*/
void ramdisk_release(struct gendisk *disk, fmode_t mode)
{printk("ramdisk release\r\n");
}/** @description      : 获取磁盘信息* @param - dev     : 块设备* @param - geo    : 模式* @return          : 0 成功;其他 失败*/
int ramdisk_getgeo(struct block_device *dev, struct hd_geometry *geo)
{/* 这是相对于机械硬盘的概念 */geo->heads = 2;          /* 磁头 */geo->cylinders = 32;    /* 柱面 */geo->sectors = RAMDISK_SIZE / (2 * 32 *512); /* 一个磁道上的扇区数量 */return 0;
}/* * 块设备操作函数 */
static struct block_device_operations ramdisk_fops =
{.owner  = THIS_MODULE,.open    = ramdisk_open,.release = ramdisk_release,.getgeo  = ramdisk_getgeo,
};/** @description : 处理传输过程* @param-req   : 请求* @return      : 无*/
static void ramdisk_transfer(struct request *req)
{   unsigned long start = blk_rq_pos(req) << 9;      /* blk_rq_pos获取到的是扇区地址,左移9位转换为字节地址 */unsigned long len  = blk_rq_cur_bytes(req);        /* 大小   *//* bio中的数据缓冲区* 读:从磁盘读取到的数据存放到buffer中* 写:buffer保存这要写入磁盘的数据*/void *buffer = bio_data(req->bio);       if(rq_data_dir(req) == READ)      /* 读数据 *//* 不同块设备,具体操作不同,RAM才可以使用memcpy进行处理 */memcpy(buffer, newchrdev.ramdiskbuf + start, len);else if(rq_data_dir(req) == WRITE)     /* 写数据 */memcpy(newchrdev.ramdiskbuf + start, buffer, len);}/** @description  : 请求处理函数* @param-q     : 请求队列* @return        : 无*/
void ramdisk_request_fn(struct request_queue *q)
{int err = 0;struct request *req;/* 循环处理请求队列中的每个请求 */req = blk_fetch_request(q);while(req != NULL) {/* 针对请求做具体的传输处理 */ramdisk_transfer(req);/* 判断是否为最后一个请求,如果不是的话就获取下一个请求* 循环处理完请求队列中的所有请求。*/if (!__blk_end_request_cur(req, err))req = blk_fetch_request(q);}
}/** @description  : 驱动出口函数* @param       : 无* @return       : 无*/
static int __init ramdisk_init(void)
{int ret = 0;printk("ramdisk init\r\n");/* 1、申请用于ramdisk内存 */newchrdev.ramdiskbuf = kzalloc(RAMDISK_SIZE, GFP_KERNEL);if(newchrdev.ramdiskbuf == NULL) {ret = -EINVAL;goto ram_fail;}/* 2、初始化自旋锁 */spin_lock_init(&newchrdev.lock);/* 3、注册块设备(逻辑块设备:为应用层提供一个操作对象) */newchrdev.major = register_blkdev(0, RAMDISK_NAME); /* 由系统自动分配主设备号 */if(newchrdev.major < 0) {goto register_blkdev_fail;}  printk("ramdisk major = %d\r\n", newchrdev.major);/* 4、分配并初始化gendisk */newchrdev.gendisk = alloc_disk(RADMISK_MINOR);if(!newchrdev.gendisk) {ret = -EINVAL;goto gendisk_alloc_fail;}/* 5、分配并初始化请求队列 */newchrdev.queue = blk_init_queue(ramdisk_request_fn, &newchrdev.lock);if(!newchrdev.queue) {ret = EINVAL;goto blk_init_fail;}/* 6、添加(注册)disk* (1)、关联逻辑块设备和物理块设备* (2)、为物理块设备添加操作集和请求队列* (3)、为物理块设备设置属性*/newchrdev.gendisk->major = newchrdev.major;        /* 主设备号 */newchrdev.gendisk->first_minor = 0;           /* 第一个次设备号(起始次设备号) */newchrdev.gendisk->fops = &ramdisk_fops;       /* 操作函数 */newchrdev.gendisk->private_data = &newchrdev; /* 私有数据 */newchrdev.gendisk->queue = newchrdev.queue;       /* 请求队列 */sprintf(newchrdev.gendisk->disk_name, RAMDISK_NAME); /* 名字 */set_capacity(newchrdev.gendisk, RAMDISK_SIZE/512);    /* 设备容量(单位为扇区) */add_disk(newchrdev.gendisk);return 0;
blk_init_fail:put_disk(newchrdev.gendisk);//del_gendisk(ramdisk.gendisk);
gendisk_alloc_fail:unregister_blkdev(newchrdev.major, RAMDISK_NAME);
register_blkdev_fail:kfree(newchrdev.ramdiskbuf); /* 释放内存 */
ram_fail:return ret;
}/** @description  : 驱动出口函数* @param       : 无* @return       : 无*/
static void __exit ramdisk_exit(void)
{printk("ramdisk exit\r\n");/* 释放gendisk */del_gendisk(newchrdev.gendisk);put_disk(newchrdev.gendisk);/* 清除请求队列 */blk_cleanup_queue(newchrdev.queue);/* 注销块设备 */unregister_blkdev(newchrdev.major, RAMDISK_NAME);/* 释放内存 */kfree(newchrdev.ramdiskbuf);
}module_init(ramdisk_init);
module_exit(ramdisk_exit);
MODULE_LICENSE("GPL");

3、测试

# ls
ramdisk.ko
#
# insmod ramdisk.ko
ramdisk init
ramdisk major = 254
ramdisk open
ramdisk release
#
# ls -l /dev/ramdisk
brw-rw----    1 root     root      254,   0 Jan  1 00:13 /dev/ramdisk
#
# rmmod ramdisk.ko
ramdisk exit
#
# ls -l /dev/ramdisk
ls: /dev/ramdisk: No such file or directory
#
# insmod ramdisk.ko
ramdisk init
ramdisk major = 254
ramdisk open
ramdisk release
#
# rmmod ramdisk.ko
ramdisk exit
#
# insmod ramdisk.ko
ramdisk init
ramdisk major = 254
ramdisk open
ramdisk release
# ls -l /dev/ramdisk
brw-rw----    1 root     root      254,   0 Jan  1 00:14 /dev/ramdisk
#
# fdisk -l
Disk /dev/mmcblk0: 15 GB, 15931539456 bytes, 31116288 sectors
1936 cylinders, 255 heads, 63 sectors/track
Units: sectors of 1 * 512 = 512 bytesDevice       ramdisk open
Boot StartCHS    EndCHS        Stramdisk release
artLBA     EndLBA    Sectors  Sizramdisk open
e Id Type
/dev/mmcblk0p1    0,13ramdisk release
0,3     1023,254,63       8192   31116287   31108096 14.8G  c Win95 FAT32 (LBA)
Disk /dev/mmcblk1: 7456 MB, 7818182656 bytes, 15269888 sectors
238592 cylinders, 4 heads, 16 sectors/track
Units: sectors of 1 * 512 = 512 bytesDevice       Boot StartCHS    EndCHS        StartLBA     EndLBA    Sectors  Size Id Type
/dev/mmcblk1p1 *  0,32,33     4,52,48           2048      67583      65536 32.0M  c Win95 FAT32 (LBA)
/dev/mmcblk1p2    4,52,49     950,129,11       67584   15269887   15202304 7423M 83 Linux
Disk /dev/mmcblk1boot1: 4 MB, 4194304 bytes, 8192 sectors
128 cylinders, 4 heads, 16 sectors/track
Units: sectors of 1 * 512 = 512 bytesDisk /dev/mmcblk1boot1 doesn't contain a valid partition table
Disk /dev/mmcblk1boot0: 4 MB, 4194304 bytes, 8192 sectors
128 cylinders, 4 heads, 16 sectors/track
Units: sectors of 1 * 512 = 512 bytesDisk /dev/mmcblk1boot0 doesn't contain a valid partition table
Disk /dev/ramdisk: 2 MB, 2097152 bytes, 4096 sectors
32 cylinders, 2 heads, 64 sectors/track
Units: sectors of 1 * 512 = 512 bytesDisk /dev/ramdisk doesn't contain a valid partition table
#

六、内存模拟硬盘驱动编写(自定义请求队列)

参考:drivers\block\zram\zram_drv.c

1、编写思路

1、从 RAM 中分配内存。

2、注册逻辑块设备,为应用层提供操作对象。

3、设置“制造请求”函数。

4、添加、初始化实际块设备,为驱动提供操作对象。

5、通过注册的请求队列函数进行数据传输(可以使用内核提供,也可以自己进行构造)。

2、驱动实现

#include "linux/init.h"
#include "linux/module.h"
#include "linux/slab.h"
#include "linux/spinlock_types.h"
#include "linux/fs.h"
#include "linux/genhd.h"
#include "linux/hdreg.h"
#include "linux/blkdev.h"#define RAMDISK_SIZE (2 * 1024 * 1024)   /* 容量大小为2MB */
#define RAMDISK_NAME    "ramdisk"         /* 名字 */
#define RADMISK_MINOR   3                   /* 表示有三个磁盘分区!不是次设备号为3! */typedef struct{unsigned char *ramdiskbuf;    /* ramdisk内存空间,用于模拟块设备 */spinlock_t lock;           /* 自旋锁 */int major;                 /* 主设备号 */struct request_queue *queue;/* 请求队列 */struct gendisk *gendisk;    /* gendisk */
}newchrdev_t;
newchrdev_t newchrdev;/** @description     : 打开块设备* @param - dev  : 块设备* @param - mode   : 打开模式* @return            : 0 成功;其他 失败*/
int ramdisk_open(struct block_device *dev, fmode_t mode)
{printk("ramdisk open\r\n");return 0;
}/** @description      : 释放块设备* @param - disk     : gendisk* @param - mode   : 模式* @return          : 0 成功;其他 失败*/
void ramdisk_release(struct gendisk *disk, fmode_t mode)
{printk("ramdisk release\r\n");
}/** @description      : 获取磁盘信息* @param - dev     : 块设备* @param - geo    : 模式* @return          : 0 成功;其他 失败*/
int ramdisk_getgeo(struct block_device *dev, struct hd_geometry *geo)
{/* 这是相对于机械硬盘的概念 */geo->heads = 2;          /* 磁头 */geo->cylinders = 32;    /* 柱面 */geo->sectors = RAMDISK_SIZE / (2 * 32 *512); /* 一个磁道上的扇区数量 */return 0;
}/* * 块设备操作函数 */
static struct block_device_operations ramdisk_fops =
{.owner  = THIS_MODULE,.open    = ramdisk_open,.release = ramdisk_release,.getgeo  = ramdisk_getgeo,
};#if 0
/** @description   : 处理传输过程* @param-req   : 请求* @return      : 无*/
static void ramdisk_transfer(struct request *req)
{   unsigned long start = blk_rq_pos(req) << 9;      /* blk_rq_pos获取到的是扇区地址,左移9位转换为字节地址 */unsigned long len  = blk_rq_cur_bytes(req);        /* 大小   *//* bio中的数据缓冲区* 读:从磁盘读取到的数据存放到buffer中* 写:buffer保存这要写入磁盘的数据*/void *buffer = bio_data(req->bio);       if(rq_data_dir(req) == READ)      /* 读数据 *//* 不同块设备,具体操作不同,RAM才可以使用memcpy进行处理 */memcpy(buffer, newchrdev.ramdiskbuf + start, len);else if(rq_data_dir(req) == WRITE)     /* 写数据 */memcpy(newchrdev.ramdiskbuf + start, buffer, len);}/** @description  : 请求处理函数* @param-q     : 请求队列* @return        : 无*/
void ramdisk_request_fn(struct request_queue *q)
{int err = 0;struct request *req;/* 循环处理请求队列中的每个请求 */req = blk_fetch_request(q);while(req != NULL) {/* 针对请求做具体的传输处理 */ramdisk_transfer(req);/* 判断是否为最后一个请求,如果不是的话就获取下一个请求* 循环处理完请求队列中的所有请求。*/if (!__blk_end_request_cur(req, err))req = blk_fetch_request(q);}
}
#endif/** @description : “制造请求”函数* @param-q   : 请求队列* @return        : 无*/
void ramdisk_make_request_fn(struct request_queue *q, struct bio *bio)
{int offset;struct bio_vec bvec;struct bvec_iter iter;unsigned long len = 0;offset = (bio->bi_iter.bi_sector) << 9;  /* 获取要操作的设备的偏移地址 *//* 处理bio中的每个段 */bio_for_each_segment(bvec, bio, iter){char *ptr = page_address(bvec.bv_page) + bvec.bv_offset;len = bvec.bv_len;if(bio_data_dir(bio) == READ)   /* 读数据 *//* 不同块设备,具体操作不同,RAM才可以使用memcpy进行处理 */memcpy(ptr, newchrdev.ramdiskbuf + offset, len);else if(bio_data_dir(bio) == WRITE)  /* 写数据 */memcpy(newchrdev.ramdiskbuf + offset, ptr, len);offset += len;}set_bit(BIO_UPTODATE, &bio->bi_flags);bio_endio(bio, 0);
}/** @description  : 驱动出口函数* @param       : 无* @return       : 无*/
static int __init ramdisk_init(void)
{int ret = 0;printk("ramdisk init\r\n");/* 1、申请用于ramdisk内存 */newchrdev.ramdiskbuf = kzalloc(RAMDISK_SIZE, GFP_KERNEL);if(newchrdev.ramdiskbuf == NULL) {ret = -EINVAL;goto ram_fail;}/* 2、初始化自旋锁 */spin_lock_init(&newchrdev.lock);/* 3、注册块设备(逻辑块设备:为应用层提供一个操作对象) */newchrdev.major = register_blkdev(0, RAMDISK_NAME); /* 由系统自动分配主设备号 */if(newchrdev.major < 0) {goto register_blkdev_fail;}  printk("ramdisk major = %d\r\n", newchrdev.major);/* 4、分配并初始化gendisk */newchrdev.gendisk = alloc_disk(RADMISK_MINOR);if(!newchrdev.gendisk) {ret = -EINVAL;goto gendisk_alloc_fail;}/* 5、分配并初始化请求队列 */
#if 0newchrdev.queue = blk_init_queue(ramdisk_request_fn, &newchrdev.lock);if(!newchrdev.queue) {ret = EINVAL;goto blk_init_fail;}
#endifnewchrdev.queue = blk_alloc_queue(GFP_KERNEL);if(!newchrdev.queue){ret = -EINVAL;goto blk_allo_fail;}/* 6、设置“制造请求”函数 */blk_queue_make_request(newchrdev.queue, ramdisk_make_request_fn);/* 7、添加(注册)disk* (1)、关联逻辑块设备和物理块设备* (2)、为物理块设备添加操作集和请求队列* (3)、为物理块设备设置属性*/newchrdev.gendisk->major = newchrdev.major;       /* 主设备号 */newchrdev.gendisk->first_minor = 0;           /* 第一个次设备号(起始次设备号) */newchrdev.gendisk->fops = &ramdisk_fops;       /* 操作函数 */newchrdev.gendisk->private_data = &newchrdev; /* 私有数据 */newchrdev.gendisk->queue = newchrdev.queue;       /* 请求队列 */sprintf(newchrdev.gendisk->disk_name, RAMDISK_NAME); /* 名字 */set_capacity(newchrdev.gendisk, RAMDISK_SIZE/512);    /* 设备容量(单位为扇区) */add_disk(newchrdev.gendisk);return 0;
blk_allo_fail:put_disk(newchrdev.gendisk);//del_gendisk(ramdisk.gendisk);
gendisk_alloc_fail:unregister_blkdev(newchrdev.major, RAMDISK_NAME);
register_blkdev_fail:kfree(newchrdev.ramdiskbuf); /* 释放内存 */
ram_fail:return ret;
}/** @description  : 驱动出口函数* @param       : 无* @return       : 无*/
static void __exit ramdisk_exit(void)
{printk("ramdisk exit\r\n");/* 释放gendisk */del_gendisk(newchrdev.gendisk);put_disk(newchrdev.gendisk);/* 清除请求队列 */blk_cleanup_queue(newchrdev.queue);/* 注销块设备 */unregister_blkdev(newchrdev.major, RAMDISK_NAME);/* 释放内存 */kfree(newchrdev.ramdiskbuf);
}module_init(ramdisk_init);
module_exit(ramdisk_exit);
MODULE_LICENSE("GPL");

3、测试

# ls
ramdisk.ko
# insmod ramdisk.ko
ramdisk init
ramdisk major = 254
ramdisk open
ramdisk release
# ls -l /dev/ramdisk
brw-rw----    1 root     root      254,   0 Jan  1 00:51 /dev/ramdisk
#
# rmmod ramdisk.ko
ramdisk exit
#
# ls -l /dev/ramdisk
ls: /dev/ramdisk: No such file or directory
#
# insmod ramdisk.ko
ramdisk init
ramdisk major = 254
ramdisk open
ramdisk release
#
# ls -l /dev/ramdisk
brw-rw----    1 root     root      254,   0 Jan  1 00:51 /dev/ramdisk
#
# rmmod ramdisk.ko
ramdisk exit
#
# random: nonblocking pool is initialized
# ls -l /dev/ramdisk
ls: /dev/ramdisk: No such file or directory
#
#
#
# insmod ramdisk.ko
ramdisk init
ramdisk major = 254
ramdisk open
ramdisk release
#
# fdisk -l
Disk /dev/mmcblk0: 15 GB, 15931539456 bytes, 31116288 sectors
1936 cylinders, 255 heads, 63 sectors/track
Units: sectors of 1 * 512 = 512 bytesDevice       Boot StartCHS    EndCHS        Sramdisk open
tartLBA     EndLBA    Sectors  Siramdisk release
ze Id Type
/dev/mmcblk0p1    0,1ramdisk open
30,3     1023,254,63       8192   31116287   31108096 14.8G  c Wiramdisk release
n95 FAT32 (LBA)
Disk /dev/mmcblk1: 7456 MB, 7818182656 bytes, 15269888 sectors
238592 cylinders, 4 heads, 16 sectors/track
Units: sectors of 1 * 512 = 512 bytesDevice       Boot StartCHS    EndCHS        StartLBA     EndLBA    Sectors  Size Id Type
/dev/mmcblk1p1 *  0,32,33     4,52,48           2048      67583      65536 32.0M  c Win95 FAT32 (LBA)
/dev/mmcblk1p2    4,52,49     950,129,11       67584   15269887   15202304 7423M 83 Linux
Disk /dev/mmcblk1boot1: 4 MB, 4194304 bytes, 8192 sectors
128 cylinders, 4 heads, 16 sectors/track
Units: sectors of 1 * 512 = 512 bytesDisk /dev/mmcblk1boot1 doesn't contain a valid partition table
Disk /dev/mmcblk1boot0: 4 MB, 4194304 bytes, 8192 sectors
128 cylinders, 4 heads, 16 sectors/track
Units: sectors of 1 * 512 = 512 bytesDisk /dev/mmcblk1boot0 doesn't contain a valid partition table
Disk /dev/ramdisk: 2 MB, 2097152 bytes, 4096 sectors
32 cylinders, 2 heads, 64 sectors/track
Units: sectors of 1 * 512 = 512 bytesDisk /dev/ramdisk doesn't contain a valid partition table
#

i.MX 6ULL 驱动开发 二十七:块设备相关推荐

  1. i.MX 6ULL 驱动开发 二十九:向 Linux 内核中添加自己编写驱动

    一.概述 Linux 内核编译流程如下: 1.配置 Linux 内核. 2.编译 Linux 内核. 说明:进入 Linux 内核源码,使用 make help 参看相关配置. 二.make menu ...

  2. i.MX 6ULL 驱动开发 六:beep 驱动

    一.原理分析 通过原理图可以确定 beep 连接到 SNVS_TAMPER1 引脚上.根据 beep 原理,当 SNVS_TAMPER1 输出低电平时,beep 鸣叫. 通过数据手册确定 SNVS_T ...

  3. linux 块设备驱动(二)——块设备数据结构

    linux 块设备驱动(二)--块设备数据结构 本文来源于: 1. http://www.cnblogs.com/dyllove98/archive/2013/07/01/3165567.html 块 ...

  4. i.MX 6ULL 驱动开发 十四:LED(paltform驱动框架)

    一.驱动设计思想(机制.策略.分离.分层) 驱动设计思想(机制.策略.分离.分层)_正在起飞的蜗牛的博客-CSDN博客_机制与策略分离 二.驱动开发框架 三.platform 基本概念 Linux 驱 ...

  5. Linux驱动开发--写一个块设备驱动

    原文地址:[原创] 写一个块设备驱动 http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=2017377&fromuid=288 ...

  6. Linux块设备驱动(二)————块设备的体系架构

    块设备的体系架构从上到下依次为VFS虚拟文件系统.磁盘缓冲.各种类型的磁盘系统.通用块设备层.I/O调度层(优化访问上层的请求(读写请求)).块设备驱动层.块设备硬件层. 1.虚拟文件系统(VFS) ...

  7. i.MX 6ULL 驱动开发 一:搭建开发环境

    一.搭建编译环境 1.工具 工具 版本 开发板 正点原子 imx6ull 开发板 gcc gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf ubo ...

  8. linux 块设备驱动 (三)块设备驱动开发

    linux 块设备驱动 (三)块设备驱动开发 一: 块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为: int ...

  9. 基于MTD的NAND驱动开发(二)

    基于MTD的NAND驱动开发(二) 基于MTD的NAND驱动开发(三) http://blog.csdn.net/leibniz_zsu/article/details/4977853 http:// ...

最新文章

  1. 用“脸”打卡,抬头就能签到!
  2. RNA-seq分析-数据库
  3. CoreLocation.ConnectionClient CFDictionaryApplyFunction Crash
  4. 配置IPsec Tunnel With TACACS+ User Authentication
  5. liferay和cas系统集成
  6. python subprocess Popen
  7. java定义一个getsize方法,long getSize()
  8. React组件生命周期-正确执行运行阶段的函数
  9. Linux(五):Ubuntu 16.04 更改系统语言为简体中文(Chinese simplified)
  10. 共享主机与虚拟机文件方式一
  11. string s=new string() 创建了几个对象_Java的String类5个常见面试题的解答过程和原理...
  12. 自动劫持root密码
  13. Web Developer's Handbook
  14. tsmsbs项目中用到的触发器和存储过程
  15. 如何制做计算机病毒,电脑病毒制作-怎么制作电脑病毒请教高手,怎么做病毒? – 手机爱问...
  16. uniapp 在HBuilder X中配置微信小程序开发工具
  17. Web前端必备:JavaScript基础知识梳理
  18. 摸鱼 | 远程控制实验室服务器(不在同一局域网)
  19. eventlet 协程
  20. 美团给应届生开年薪35万+ 倒挂老员工:加油、打工人

热门文章

  1. 2015年游戏行业总结:IP是把双刃剑
  2. php获取文本宽度,PHP获取一段文本显示点阵宽度和高度的方法
  3. 软件的国际化、Jstl国际化标签
  4. 毕业设计_ssm_美妆(商城)
  5. 运动耳机什么牌子的好、运动耳机排行榜10强
  6. Python pandas 中loc函数的意思及用法,及跟iloc的区别
  7. 家教日记---7.17.
  8. CSS 3.0实现炫酷发光特效
  9. python解析flv协议(AMF数据)
  10. 用Python画九宫格图(n宫格图)的脚本