• Linux驱动开发(十六)---块设备驱动


    前文回顾

    《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)

    是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性读到缓冲区。

    块设备中有如下几个大小概念的定义

    1. 扇区(Sectors)
      任何块设备硬件对数据处理的基本单位。通常,1个扇区的大小为512byte。(对设备而言)
    2. 块 (Blocks)
      由Linux制定对内核或文件系统等数据处理的基本单位。通常,1个块由1个或多个扇区组成。(对Linux操作系统而言)
    3. 段(Segments)
      由若干个相邻的块组成。是Linux内存管理机制中一个内存页或者内存页的一部分。

    页、段、块、扇区之间的关系图如下:
    在这里插入图片描述

    块设备分类

    这里我们按照驱动的不同实现,将块设备分成了两类:

    • 非机械设备
      比如EMMC、SD卡、NAND Flash这类没有任何机械设备的存储设备,可以任意读写任何的扇区(块设备物理存储单元)。
    • 机械设备
      机械硬盘这样带有磁头的设备,读取不同的盘面或者磁道里面的数据,磁头都需要进行移动。

    分类的原因就在于非机械设备,可以任意读取,不会有性能的差异。但是机械设备就需要内核实现专门的算法,将杂乱的访问按照一定的顺序进行排列,可以有效提高磁盘性能。
    在这里插入图片描述

    驱动过程

    这里的驱动过程,也是低版本内核块设备驱动的写法。

    Created with Raphaël 2.3.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;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    磁盘结构体

    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;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    磁盘结构体中的操作描述符,类似于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;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    请求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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    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;    //销毁方法
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    写出这种框架的人,也是厉害,光看就头疼了
    在这里插入图片描述

    核心操作函数

    注意,这里都是低版本内核的关键操作,高版本好多函数都找不到了。
    在这里插入图片描述
    注册块设备

    int register_blkdev(unsigned int major, const char *name)
    
    • 1

    申请磁盘

    #define alloc_disk(minors) alloc_disk_node(minors, NUMA_NO_NODE)
    
    • 1

    设置磁盘容量

    void set_capacity(struct gendisk *disk, sector_t size)
    
    • 1

    添加磁盘

    void add_disk(struct gendisk *disk)
    
    • 1

    释放磁盘

    void del_gendisk(struct gendisk *gd)
    
    • 1

    根据不同类型的块设备,请求队列的使用不相同

    • 机械类块设备
      需要内核帮忙处理算法,所以接口采用
      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 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #include 
    #include 
    #include 
    
    
    #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");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227

    非机械类

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #include 
    #include 
    #include 
    
    #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");
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211

    这个代码没有运行过,要是出错了也别来找我啊。
    在这里插入图片描述

    Ramdisk使用

    1. 格式化ramdisk
      需要将ramdisk用一种文件系统格式化,由于没有必要采用日志文件系统,因此仅用EXT2格式即可,以仅需要使用ram0为例:
      [root]# mke2fs -m 0 /dev/ramdisk

    2. 创建挂载点,挂载ramdisk
      在已经格式化了ramdisk之后,必须为其创建一个挂载点,然后将ramdisk挂载到该挂载点后使用。
      [root]# mkdir /mnt/rd; mount /dev/ramdisk /mnt/rd

    3. 查看验证挂载是否成功及文件系统信息
      [root]# mount | grep ramdisk
      [root]# df -h | grep ramdisk

    4. 进一步查看ram0的详细信息
      [root]# tune2fs -l /dev/ramdisk

    5. 修改挂载点的使用权限
      [root]# chown van:root /mnt/rd
      [root]# chmod 0770 /mnt/rd

    6. 验证并查看挂载点的权限是否修改
      [root]# ls -ald /mnt/rd

    7. 使用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,
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    我也写了一个类似前面的自定义方式产生myramdisk。
    在这里插入图片描述

    《代码下载地址》
    厉不厉害,全网独一份的代码呢
    在这里插入图片描述

    结束语

    昨天也是情人节,我和姐姐两家人去看古仔的新电影《明日战记》,场面还可以,不过情节单调,人物也单调,为什么还要找两个五六十岁的人来扮演两个战士,普通市民刘先生都快六十岁的人了。香港电影能拿得出来的,还是那些老面孔,真的是没落了好多。
    在这里插入图片描述
    不过最后没有人牺牲,也算是一个好的结尾,适合带着小孩子观看。

  • 相关阅读:
    通用/参考电路/ 连接备份
    如何避免小程序被封
    低照度图像数据集
    C\C++中数组指针和二维数组最强最简单粗暴深刻理解!!!一遍包过!
    小米汽车超级工厂智能物流
    零售超市商家怎么做微信小程序
    深入理解多线程(第二篇)
    element-table 行的拖拽更改顺序(无需下载sortableJs
    ESP8266-Arduino编程实例-TM1637-驱动4位7段数码管
    Jsoup爬虫入门实战
  • 原文地址:https://blog.csdn.net/baidu_19348579/article/details/126157778