此为linux3.0到6.0内存块设备驱动框架介绍及使用,目前也是以vmalloc申请的内存作为一块磁盘。
由于文档的缺失,所以阅读了关于内存块设备驱动框架的代码。
块设备驱动是操作系统内核的一部分,它负责管理和控制块级别的设备,例如硬盘驱动、固态硬盘驱动和其他存储设备。这些设备以固定大小的数据块(通常以扇区为单位)进行读写。以下是有关块设备驱动的一些重要信息:
块设备和字符设备: 在操作系统中,设备可以分为字符设备和块设备。块设备以固定大小的块为单位进行读写,而字符设备以字符流为单位进行读写。块设备通常用于存储设备,如硬盘,它们需要支持随机访问和缓存。块设备驱动负责管理块设备。
硬盘和存储设备: 块设备驱动广泛应用于硬盘驱动和其他存储设备,如固态硬盘(SSD)、USB 存储设备和网络存储。
抽象接口: 块设备驱动提供了一个抽象接口,使操作系统可以与各种不同类型的块设备交互,而无需了解底层设备的细节。这种接口通常包括读写、块大小、寻道等操作。
设备文件: 操作系统为每个块设备创建一个设备文件,通常位于 /dev 目录下。这些设备文件允许用户和应用程序访问块设备。用户可以使用文件 I/O 操作打开、读取和写入这些设备文件,而块设备驱动将其转换为适当的操作。
缓存和性能优化: 块设备驱动通常包括缓存管理以提高性能。它可以缓存常用的数据块,以减少磁盘访问的延迟,从而提高读写速度。(不确定)
错误处理: 块设备驱动还需要处理错误,如硬盘坏道、写入故障和读取错误。这可能包括重新尝试操作、标记损坏的块以及通知操作系统和应用程序有关错误的信息。
设备探测和初始化: 在启动时,块设备驱动负责检测和初始化连接到系统的块设备。这可能涉及识别硬件、加载适当的驱动程序和创建设备文件。(正常来说可以不用探测,但是通用平台确实是以这些模板为框架)
文件系统: 块设备驱动与文件系统紧密相关。文件系统在块设备上创建文件和目录结构,使操作系统和用户能够有效地管理和存储数据(稍微有一些复杂,包括常说的写回,写透都在文件系统)
1.安装centos7.9系统
2.安装编译工具
3.clone内核代码方便阅读
#装完系统后,安装完环境后,用如下命令
git clone https://github.com/torvalds/linux.git --branch v3.10 --depth 1
linux 3.0kernel 的块设备驱动框架的核心数据处理框架有两种,一种是以请求队列形式的框架,一种是以bio段的框架。但是两种框架殊途同归都是单队列框架。请求队列形式框架会自带默认使用电梯算法。而bio段的框架自由度相对会高一些。
因为是内存块设备,所以不谈那些正常存储器等等的理论,nvme ssd仅仅是通过和ssd上的固件交互,但是机械硬盘就相对复杂。还有很多其他类型的,emmc,sd卡等等。
块设备驱动代码的正常逻辑如下
具体框架api如下
struct gendisk *alloc_disk(int minors);
处理队列数据的框架
struct request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock);
blk_init_queue 是 Linux 内核中用于初始化请求队列的函数,它的参数如下:
request_fn_proc *rfn:这是一个函数指针,指向用于处理块设备请求的请求处理函数。
请求处理函数通常是块设备驱动程序中定义的,它会在请求队列中的每个请求被处理时被调用。
函数的原型通常是 typedef void (request_fn_proc) (struct request_queue *q);
spinlock_t *lock:这是一个指向自旋锁(spinlock)的指针。自旋锁用于在多线程环境中保护请求队列的数据结构,
以防止竞态条件和数据不一致。自旋锁是内核中用于临界区保护的同步原语。如果为 NULL,则在请求队列的内部将分配并使用一个默认的自旋锁。
blk_init_queue 函数返回一个指向初始化后的请求队列结构体 struct request_queue 的指针。
初始化后的请求队列可以用于管理和调度 I/O 请求,
以提高块设备的性能和可靠性。这个函数通常在块设备驱动程序中的初始化过程中使用。
这是以bio元数据传过来处理的形式
struct request_queue *blk_alloc_queue(gfp_t gfp_mask);
gfp_t gfp_mask:这是一个用于内存分配的标志,指定了分配请求队列所需内存的分配策略。gfp_t 是一种表示内存分配标志的数据类型,
可以指定内核应如何处理内存分配请求,例如 GFP_KERNEL 表示从内核堆栈分配内存,GFP_ATOMIC 表示分配内存时不应休眠等。
void blk_queue_make_request(struct request_queue *q, make_request_fn *mfn);
函数用于注册自定义的请求处理函数,您需要提供这个函数的指针。
请求处理函数的原型通常是 typedef void (make_request_fn) (struct request_queue *q, struct bio *bio);
int register_blkdev(unsigned int major, const char *name);
unsigned int major:这是主设备号,它是一个非负整数,用于标识块设备驱动程序的主设备。
每个块设备驱动程序通常分配一个唯一的主设备号,以标识其支持的块设备。
const char *name:这是块设备的名称,通常是一个字符串,用于标识块设备。名称应该是唯一的,以便用户和应用程序可以识别块设备。
设置相关属性大抵是给gendisk结构体里各个变量分配需要的属性。比如major主设备号,设置数据处理的回调函数。
设置块设备驱动的操作函数集合(此功能对于伪内存块设备意义不是很大,因为使用vmalloc申请的,不需要block交互驱动api)。
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;
};