nvme开发--linux内存块设备随笔

伪内存块设备驱动随笔一

此为linux3.0到6.0内存块设备驱动框架介绍及使用,目前也是以vmalloc申请的内存作为一块磁盘。


文章目录

  • 伪内存块设备驱动随笔一
  • 前言
  • 一、块设备驱动介绍
  • 二、内存块设备驱动开发
    • 1.linux3.0块设备驱动开发
      • 环境搭建
      • 块设备驱动框架
        • 内存块设备通用框架
          • 1.分配跟gendisk结构体变量
          • 2.初始化请求队列
            • 1.以处理队列数据的框架
            • 2.以处理bio段数据的框架
          • 3. 注册块设备号
          • 4.设置相关属性
  • 总结


前言

由于文档的缺失,所以阅读了关于内存块设备驱动框架的代码。

一、块设备驱动介绍

块设备驱动是操作系统内核的一部分,它负责管理和控制块级别的设备,例如硬盘驱动、固态硬盘驱动和其他存储设备。这些设备以固定大小的数据块(通常以扇区为单位)进行读写。以下是有关块设备驱动的一些重要信息:

块设备和字符设备: 在操作系统中,设备可以分为字符设备和块设备。块设备以固定大小的块为单位进行读写,而字符设备以字符流为单位进行读写。块设备通常用于存储设备,如硬盘,它们需要支持随机访问和缓存。块设备驱动负责管理块设备。

硬盘和存储设备: 块设备驱动广泛应用于硬盘驱动和其他存储设备,如固态硬盘(SSD)、USB 存储设备和网络存储。

抽象接口: 块设备驱动提供了一个抽象接口,使操作系统可以与各种不同类型的块设备交互,而无需了解底层设备的细节。这种接口通常包括读写、块大小、寻道等操作。

设备文件: 操作系统为每个块设备创建一个设备文件,通常位于 /dev 目录下。这些设备文件允许用户和应用程序访问块设备。用户可以使用文件 I/O 操作打开、读取和写入这些设备文件,而块设备驱动将其转换为适当的操作。

缓存和性能优化: 块设备驱动通常包括缓存管理以提高性能。它可以缓存常用的数据块,以减少磁盘访问的延迟,从而提高读写速度。(不确定)

错误处理: 块设备驱动还需要处理错误,如硬盘坏道、写入故障和读取错误。这可能包括重新尝试操作、标记损坏的块以及通知操作系统和应用程序有关错误的信息。

设备探测和初始化: 在启动时,块设备驱动负责检测和初始化连接到系统的块设备。这可能涉及识别硬件、加载适当的驱动程序和创建设备文件。(正常来说可以不用探测,但是通用平台确实是以这些模板为框架)

文件系统: 块设备驱动与文件系统紧密相关。文件系统在块设备上创建文件和目录结构,使操作系统和用户能够有效地管理和存储数据(稍微有一些复杂,包括常说的写回,写透都在文件系统)

二、内存块设备驱动开发

1.linux3.0块设备驱动开发

环境搭建

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卡等等。
块设备驱动代码的正常逻辑如下

  1. 分配gendisk结构体变量:首先,块设备驱动程序需要分配并初始化一个 gendisk 结构体,这个结构体代表了块设备。其中包括设备的名称、主次设备号、大小等信息。
  2. 初始化请求队列:使用 blk_init_queue() 等函数来初始化请求队列,配置请求队列的参数,包括请求队列的调度策略、队列深度、超时时间等。(也就是块设备子系统下面会有一个IO调度层)
  3. 注册块设备号:通过函数如 register_blkdev() 来注册块设备号,以便内核和用户空间应用程序能够识别和访问该块设备。
  4. 设置其他相关属性(比如大小,buffer)
  5. 注册gendisk结构体:最后,块设备驱动程序需要将初始化好的 gendisk 结构体注册到内核中,以便内核能够识别并管理该块设备。
    总的来说具体框架是这种模式,但是第二部初始化其实分成两种,一种是以请求队列形式的数据包的回调函数,第二种是以bio段数据的回调函数为核心,每一个段是4096字节,但是第二种实际上自由度更高,也就是他不会给你默认的调度算法,正常来说第一种是以电梯算法为核心。

具体框架api如下

1.分配跟gendisk结构体变量
struct gendisk *alloc_disk(int minors);
2.初始化请求队列
1.以处理队列数据的框架

处理队列数据的框架

 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 请求,
以提高块设备的性能和可靠性。这个函数通常在块设备驱动程序中的初始化过程中使用。
2.以处理bio段数据的框架

这是以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);

3. 注册块设备号
int register_blkdev(unsigned int major, const char *name);
unsigned int major:这是主设备号,它是一个非负整数,用于标识块设备驱动程序的主设备。
每个块设备驱动程序通常分配一个唯一的主设备号,以标识其支持的块设备。

const char *name:这是块设备的名称,通常是一个字符串,用于标识块设备。名称应该是唯一的,以便用户和应用程序可以识别块设备。
4.设置相关属性

设置相关属性大抵是给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;
};


总结

你可能感兴趣的:(nvme驱动开发,1024程序员节,linux)