参考:【块设备】通用块层 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_SYSFS
struct list_head bd_holder_disks;
#endif
struct 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: remove
struct kobject *slave_dir;
struct timer_rand_state *random;
atomic_t sync_io; /* RAID */
struct disk_events *ev;
#ifdef CONFIG_BLK_DEV_INTEGRITY
struct blk_integrity *integrity;
#endif
int 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_PM
struct 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_CGROUP
DECLARE_BITMAP (blkcg_pols, BLKCG_MAX_POLS);
struct blkcg_gq *root_blkg;
struct list_head blkg_list;
#endif
struct queue_limits limits;
/*
* sg stuff
*/
unsigned int sg_timeout;
unsigned int sg_reserved_size;
int node;
#ifdef CONFIG_BLK_DEV_IO_TRACE
struct 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;
#endif
struct 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_CGROUP
struct 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;
#endif
unsigned 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;
#endif
union {
#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 byte
sectors */
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 in
current 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 bytes
Device 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 bytes
Device 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 bytes
Disk /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 bytes
Disk /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 bytes
Disk /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 0
newchrdev.queue = blk_init_queue(ramdisk_request_fn, &newchrdev.lock);
if(!newchrdev.queue) {
ret = EINVAL;
goto blk_init_fail;
}
#endif
newchrdev.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 bytes
Device 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 bytes
Device 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 bytes
Disk /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 bytes
Disk /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 bytes
Disk /dev/ramdisk doesn't contain a valid partition table
#