前文回顾
《Linux驱动开发(一)—环境搭建与hello world》 《Linux驱动开发(二)—驱动与设备的分离设计》 《Linux驱动开发(三)—设备树》 《Linux驱动开发(四)—树莓派内核编译》 《Linux驱动开发(五)—树莓派设备树配合驱动开发》 《Linux驱动开发(六)—树莓派配合硬件进行字符驱动开发》 《Linux驱动开发(七)—树莓派按键驱动开发》 《Linux驱动开发(八)—树莓派SR04驱动开发》 《Linux驱动开发(九)—树莓派I2C设备驱动开发(BME280)》 《Linux驱动开发(十)—树莓派输入子系统学习(红外接收)》 《Linux驱动开发(十一)—树莓派SPI驱动学习(OLED)》 《Linux驱动开发(十二)—树莓派framebuffer学习(改造OLED)》 《Linux驱动开发(十三)—USB驱动HID开发学习(鼠标)》 《Linux驱动开发(十四)—USB驱动开发学习(键盘+鼠标)》 《Linux驱动开发(十五)—如何使用内核现有驱动(显示屏)》
本章介绍及注意事项
今天来学习一下linux驱动中的另一种关键驱动,块设备驱动。 不过在我的树莓派系统下,5.15的内核中,这块的函数已经大幅度进行了修改,所以前半部分内容来自于网络的博客学习,后面有专门介绍高版本内核的ramdisk开发。
块设备(blockdevice)
是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性读到缓冲区。
块设备中有如下几个大小概念的定义
扇区(Sectors) 任何块设备硬件对数据处理的基本单位。通常,1个扇区的大小为512byte。(对设备而言)
块 (Blocks) 由Linux制定对内核或文件系统等数据处理的基本单位。通常,1个块由1个或多个扇区组成。(对Linux操作系统而言)
段(Segments) 由若干个相邻的块组成。是Linux内存管理机制中一个内存页或者内存页的一部分。
页、段、块、扇区之间的关系图如下:
块设备分类
这里我们按照驱动的不同实现,将块设备分成了两类:
非机械设备 比如EMMC、SD卡、NAND Flash这类没有任何机械设备的存储设备,可以任意读写任何的扇区(块设备物理存储单元)。
机械设备 机械硬盘这样带有磁头的设备,读取不同的盘面或者磁道里面的数据,磁头都需要进行移动。
分类的原因就在于非机械设备,可以任意读取,不会有性能的差异。但是机械设备就需要内核实现专门的算法,将杂乱的访问按照一定的顺序进行排列,可以有效提高磁盘性能。
驱动过程
这里的驱动过程,也是低版本内核块设备驱动的写法。
Created with Raphaël 2.2.0
入口
注册块设备 register_blkdev
分配磁盘 alloc_disk
分配请求队列
设置并注册磁盘 add_disk
结束
那么在分配请求队列处理上,根据前面的设备分类不同,有两种处理方法,后面再讲。 先讲一下:请求队列结构request_queue
每个块设备都有一个请求队列,每个请求队列单独执行I/O调度。 每个请求又由多个bio组成。 每个bio对应1个I/O请求,
大概如下图所示
IO调度算法可将连续的bio合并成1个请求。
关键数据结构
块设备对象结构 block_device
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 */
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;
u8 bd_partno;
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 backing_dev_info * bd_bdi;
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;
} __randomize_layout;
磁盘结构体
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 short events; /* supported events */
unsigned short event_flags; /* flags related to event processing */
/* 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 rw_semaphore lookup_sem;
struct kobject * slave_dir;
struct timer_rand_state * random;
atomic_t sync_io; /* RAID */
struct disk_events * ev;
#ifdef CONFIG_BLK_DEV_INTEGRITY
struct kobject integrity_kobj;
#endif /* CONFIG_BLK_DEV_INTEGRITY */
int node_id;
struct badblocks * bb;
struct lockdep_map lockdep_map;
} ;
磁盘结构体中的操作描述符,类似于file_operations
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 * , unsigned int ) ;
int ( * ioctl) ( struct block_device * , fmode_t, unsigned , unsigned long ) ;
int ( * compat_ioctl) ( struct block_device * , fmode_t, unsigned , unsigned long ) ;
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 ) ;
int ( * report_zones) ( struct gendisk * , sector_t sector,
struct blk_zone * zones, unsigned int * nr_zones) ;
struct module * owner;
const struct pr_ops * pr_ops;
} ;
请求request结构
struct request {
//用于挂在请求队列链表的节点,使用函数blkdev_dequeue_request访问它,而不能直接访
问
struct list_head queuelist;
struct list_head donelist; /*用于挂在已完成请求链表的节点*/
struct request_queue * q; /*指向请求队列*/
unsigned int cmd_flags; /*命令标识*/
enum rq_cmd_type_bits cmd_type; /*命令类型*/
/*各种各样的扇区计数*/
/*为提交i/o维护bio横断面的状态信息,hard_*成员是块层内部使用的,驱动程序不应该改变
它们*/
sector_t sector; /*将提交的下一个扇区*/
sector_t hard_sector; /* 将完成的下一个扇区*/
unsigned long nr_sectors; /* 整个请求还需要传送的扇区数*/
unsigned long hard_nr_sectors; /* 将完成的扇区数*/
/*在当前bio中还需要传送的扇区数 */
unsigned int current_nr_sectors;
/*在当前段中将完成的扇区数*/
unsigned int hard_cur_sectors;
struct bio * bio; /*请求中第一个未完成操作的bio*、
struct bio *biotail; /*请求链表中末尾的bio*、
struct hlist_node hash; /*融合 hash */
/* rb_node仅用在I/O调度器中,当请求被移到分发队列中时,
请求将被删除。因此,让completion_data与rb_node分享空间*/
union {
struct rb_node rb_node; /* 排序/查找*/
void * completion_data;
} ; 作者:补给站Linux内核 https: / / www. bilibili. com/ read/ cv17063262/ 出处:bilibili
bio结构
struct bio {
sector_t bi_sector; //该bio结构所要传输的第一个(512字节)扇区:磁盘的位置
struct bio * bi_next; //请求链表
struct block_device * bi_bdev; //相关的块设备
unsigned long bi_flags//状态和命令标志
unsigned long bi_rw; //读写
unsigned short bi_vcnt; //bio_vesc偏移的个数
unsigned short bi_idx; //bi_io_vec的当前索引
unsigned short bi_phys_segments; //结合后的片段数目
unsigned short bi_hw_segments; //重映射后的片段数目
unsigned int bi_size; //I/O计数
unsigned int bi_hw_front_size; //第一个可合并的段大小;
unsigned int bi_hw_back_size; //最后一个可合并的段大小
unsigned int bi_max_vecs; //bio_vecs数目上限
struct bio_vec * bi_io_vec; //bio_vec链表:内存的位置
bio_end_io_t * bi_end_io; //I/O完成方法
atomic_t bi_cnt; //使用计数
void * bi_private; //拥有者的私有方法
bio_destructor_t * bi_destructor; //销毁方法
} ;
写出这种框架的人,也是厉害,光看就头疼了
核心操作函数
注意,这里都是低版本内核的关键操作,高版本好多函数都找不到了。 注册块设备
int register_blkdev ( unsigned int major, const char * name)
申请磁盘
#define alloc_disk(minors) alloc_disk_node(minors, NUMA_NO_NODE)
设置磁盘容量
void set_capacity ( struct gendisk * disk, sector_t size)
添加磁盘
void add_disk ( struct gendisk * disk)
释放磁盘
void del_gendisk ( struct gendisk * gd)
根据不同类型的块设备,请求队列的使用不相同
机械类块设备 需要内核帮忙处理算法,所以接口采用 blk_init_queue(ramdisk_request_fn, &ramdisk.lock);
非机械类的 首先申请队列 struct request_queue *blk_alloc_queue(gfp_t gfp_mask)
然后制造请求函数 void blk_queue_make_request(struct request_queue *q, make_request_fn *mfn)
两类设备驱动例子
代码参考自 栋哥修炼日记的 《linux块设备驱动简述(Linux驱动开发篇)》 这里都是低版本内核之前的写法。
机械类
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define RAMDISK_SIZE (2 * 1024 * 1024) /* 容量大小为2MB */
#define RAMDISK_NAME "ramdisk" /* 名字 */
#define RADMISK_MINOR 3 /* 表示有三个磁盘分区!不是次设备号为3! */
/* ramdisk设备结构体 */
struct ramdisk_dev{
int major; /* 主设备号 */
unsigned char * ramdiskbuf; /* ramdisk内存空间,用于模拟块设备 */
spinlock_t lock; /* 自旋锁 */
struct gendisk * gendisk_des; /* gendisk */
struct request_queue * queue; /* 请求队列 */
} ;
struct ramdisk_dev ramdisk; /* ramdisk设备 */
/*
* @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) /* 读数据 */
memcpy ( buffer, ramdisk. ramdiskbuf + start, len) ;
else if ( rq_data_dir ( req) == WRITE) /* 写数据 */
memcpy ( ramdisk. 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内存 */
ramdisk. ramdiskbuf = kzalloc ( RAMDISK_SIZE, GFP_KERNEL) ;
if ( ramdisk. ramdiskbuf == NULL ) {
ret = - EINVAL;
goto ram_fail;
}
/* 2、初始化自旋锁 */
spin_lock_init ( & ramdisk. lock) ;
/* 3、注册块设备 */
ramdisk. major = register_blkdev ( 0 , RAMDISK_NAME) ; /* 由系统自动分配主设备号 */
if ( ramdisk. major < 0 ) {
goto register_blkdev_fail;
}
printk ( "ramdisk major = %d\r\n" , ramdisk. major) ;
/* 4、分配并初始化gendisk */
ramdisk. gendisk_des = alloc_disk ( RADMISK_MINOR) ;
if ( ! ramdisk. gendisk_des) {
ret = - EINVAL;
goto gendisk_alloc_fail;
}
/* 5、分配并初始化请求队列 */
ramdisk. queue = blk_init_queue ( ramdisk_request_fn, & ramdisk. lock) ;
if ( ! ramdisk. queue) {
ret = EINVAL;
goto blk_init_fail;
}
/* 6、添加(注册)disk */
ramdisk. gendisk_des-> major = ramdisk. major; /* 主设备号 */
ramdisk. gendisk_des-> first_minor = 0 ; /* 第一个次设备号(起始次设备号) */
ramdisk. gendisk_des-> fops = & ramdisk_fops; /* 操作函数 */
ramdisk. gendisk_des-> private_data = & ramdisk; /* 私有数据 */
ramdisk. gendisk_des-> queue = ramdisk. queue; /* 请求队列 */
sprintf ( ramdisk. gendisk_des-> disk_name, RAMDISK_NAME) ; /* 名字 */
set_capacity ( ramdisk. gendisk_des, RAMDISK_SIZE/ 512 ) ; /* 设备容量(单位为扇区) */
add_disk ( ramdisk. gendisk_des) ;
return 0 ;
blk_init_fail:
put_disk ( ramdisk. gendisk_des) ;
//del_gendisk(ramdisk.gendisk_des);
gendisk_alloc_fail:
unregister_blkdev ( ramdisk. major, RAMDISK_NAME) ;
register_blkdev_fail:
kfree ( ramdisk. ramdiskbuf) ; /* 释放内存 */
ram_fail:
return ret;
}
/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static void __exit ramdisk_exit ( void )
{
printk ( "ramdisk exit\r\n" ) ;
/* 释放gendisk */
del_gendisk ( ramdisk. gendisk_des) ;
put_disk ( ramdisk. gendisk_des) ;
/* 清除请求队列 */
blk_cleanup_queue ( ramdisk. queue) ;
/* 注销块设备 */
unregister_blkdev ( ramdisk. major, RAMDISK_NAME) ;
/* 释放内存 */
kfree ( ramdisk. ramdiskbuf) ;
}
module_init ( ramdisk_init) ;
module_exit ( ramdisk_exit) ;
MODULE_LICENSE ( "GPL" ) ;
MODULE_AUTHOR ( "zuozhongkai" ) ;
非机械类
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define RAMDISK_SIZE (2 * 1024 * 1024) /* 容量大小为2MB */
#define RAMDISK_NAME "ramdisk" /* 名字 */
#define RADMISK_MINOR 3 /* 表示有三个磁盘分区!不是次设备号为3! */
/* ramdisk设备结构体 */
struct ramdisk_dev{
int major; /* 主设备号 */
unsigned char * ramdiskbuf; /* ramdisk内存空间,用于模拟块设备 */
spinlock_t lock; /* 自旋锁 */
struct gendisk * gendisk_des; /* gendisk */
struct request_queue * queue; /* 请求队列 */
} ;
struct ramdisk_dev ramdisk; /* ramdisk设备 */
/*
* @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-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) /* 读数据 */
memcpy ( ptr, ramdisk. ramdiskbuf + offset, len) ;
else if ( bio_data_dir ( bio) == WRITE) /* 写数据 */
memcpy ( ramdisk. 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内存 */
ramdisk. ramdiskbuf = kzalloc ( RAMDISK_SIZE, GFP_KERNEL) ;
if ( ramdisk. ramdiskbuf == NULL ) {
ret = - EINVAL;
goto ram_fail;
}
/* 2、初始化自旋锁 */
spin_lock_init ( & ramdisk. lock) ;
/* 3、注册块设备 */
ramdisk. major = register_blkdev ( 0 , RAMDISK_NAME) ; /* 由系统自动分配主设备号 */
if ( ramdisk. major < 0 ) {
goto register_blkdev_fail;
}
printk ( "ramdisk major = %d\r\n" , ramdisk. major) ;
/* 4、分配并初始化gendisk */
ramdisk. gendisk_des = alloc_disk ( RADMISK_MINOR) ;
if ( ! ramdisk. gendisk_des) {
ret = - EINVAL;
goto gendisk_alloc_fail;
}
/* 5、分配请求队列 */
ramdisk. queue = blk_alloc_queue ( GFP_KERNEL) ;
if ( ! ramdisk. queue) {
ret = - EINVAL;
goto blk_allo_fail;
}
/* 6、设置“制造请求”函数 */
blk_queue_make_request ( ramdisk. queue, ramdisk_make_request_fn) ;
/* 7、添加(注册)disk */
ramdisk. gendisk_des-> major = ramdisk. major; /* 主设备号 */
ramdisk. gendisk_des-> first_minor = 0 ; /* 第一个次设备号(起始次设备号) */
ramdisk. gendisk_des-> fops = & ramdisk_fops; /* 操作函数 */
ramdisk. gendisk_des-> private_data = & ramdisk; /* 私有数据 */
ramdisk. gendisk_des-> queue = ramdisk. queue; /* 请求队列 */
sprintf ( ramdisk. gendisk_des-> disk_name, RAMDISK_NAME) ; /* 名字 */
set_capacity ( ramdisk. gendisk_des, RAMDISK_SIZE/ 512 ) ; /* 设备容量(单位为扇区) */
add_disk ( ramdisk. gendisk_des) ;
return 0 ;
blk_allo_fail:
put_disk ( ramdisk. gendisk_des) ;
//del_gendisk(ramdisk.gendisk_des);
gendisk_alloc_fail:
unregister_blkdev ( ramdisk. major, RAMDISK_NAME) ;
register_blkdev_fail:
kfree ( ramdisk. ramdiskbuf) ; /* 释放内存 */
ram_fail:
return ret;
}
/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static void __exit ramdisk_exit ( void )
{
printk ( "ramdisk exit\r\n" ) ;
/* 释放gendisk */
del_gendisk ( ramdisk. gendisk_des) ;
put_disk ( ramdisk. gendisk_des) ;
/* 清除请求队列 */
blk_cleanup_queue ( ramdisk. queue) ;
/* 注销块设备 */
unregister_blkdev ( ramdisk. major, RAMDISK_NAME) ;
/* 释放内存 */
kfree ( ramdisk. ramdiskbuf) ;
}
module_init ( ramdisk_init) ;
module_exit ( ramdisk_exit) ;
MODULE_LICENSE ( "GPL" ) ;
MODULE_AUTHOR ( "zuozhongkai" ) ;
这个代码没有运行过,要是出错了也别来找我啊。
Ramdisk使用
格式化ramdisk 需要将ramdisk用一种文件系统格式化,由于没有必要采用日志文件系统,因此仅用EXT2格式即可,以仅需要使用ram0为例: [root]# mke2fs -m 0 /dev/ramdisk
创建挂载点,挂载ramdisk 在已经格式化了ramdisk之后,必须为其创建一个挂载点,然后将ramdisk挂载到该挂载点后使用。 [root]# mkdir /mnt/rd; mount /dev/ramdisk /mnt/rd
查看验证挂载是否成功及文件系统信息 [root]# mount | grep ramdisk
[root]# df -h | grep ramdisk
进一步查看ram0的详细信息 [root]# tune2fs -l /dev/ramdisk
修改挂载点的使用权限 [root]# chown van:root /mnt/rd
[root]# chmod 0770 /mnt/rd
验证并查看挂载点的权限是否修改 [root]# ls -ald /mnt/rd
使用ramdisk 完成以上工作后,就可以像在磁盘分区上一样在ramdisk上进创建、复制、移动、删除、编辑文件了。如果需要移除ramdisk,采用以下命令解除挂载即可: [root]# umount -v /mnt/rd
高版本
我用的树莓派版本是5.1.55。在内核的源码中,已经没有了那几个核心函数 思路已经改变了。 现在的ramdisk已经是通过块设备操作来进行读写操作了。代码可以参考brd.c。
/*
* 块设备操作函数
*/
static struct block_device_operations ramdisk_fops =
{
. owner = THIS_MODULE,
. open = myrd_open,
. release = myrd_release,
. submit_bio = myrd_submit_bio,
. rw_page = myrd_rw_page,
. getgeo = myrd_getgeo,
} ;
我也写了一个类似前面的自定义方式产生myramdisk。
《代码下载地址》 厉不厉害,全网独一份的代码呢
结束语
昨天也是情人节,我和姐姐两家人去看古仔的新电影《明日战记》,场面还可以,不过情节单调,人物也单调,为什么还要找两个五六十岁的人来扮演两个战士,普通市民刘先生都快六十岁的人了。香港电影能拿得出来的,还是那些老面孔,真的是没落了好多。 不过最后没有人牺牲,也算是一个好的结尾,适合带着小孩子观看。