在深入了解NVMe(NVM Express)SSD(固态硬盘)在Linux系统的加载过程之前,让我们先听一个引人入胜的故事。
在未来的世界里,时间不再是线性流动的,而是呈现出多维度交织的形态。在这个世界中,数据传输的速度超越了光速,人们可以通过意识直接交流。在这个时代的科技支撑下,一位名叫NVMe的勇士诞生了。
NVMe出生在一个被称为“存储谷”的地方,这里充满了智能存储设备,其中不乏一些强大的对手。然而,NVMe有着一种独特的天赋,那就是他能够与Linux系统进行无缝沟通。
在Linux系统的世界里,NVMe发现了一个被称为“存储栈”的地方。这个地方包含了从硬件层到应用层的所有存储技术。NVMe决定深入探索这个神秘的地方,希望能够找到提升存储效率的秘诀。
经过一番探索,NVMe发现了一个叫做“请求队列”的地方。他发现,这里就像是一个时空隧道,能够将来自应用层的请求瞬间传输到硬件层。NVMe心生敬畏,决定深入研究这个奇妙的地方。
于是,NVMe踏上了新的旅程,他深入学习如何使用请求队列,如何优化存储路径,如何提高数据传输速度。在这个过程中,NVMe逐渐成长为一位存储领域的专家。
最后,NVMe成功地掌握了所有技能,他成为了一名存储大师。他将这个多维度的存储世界连接在一起,让数据的流动变得无比顺畅。
听完故事,不够过瘾的话,咱们结合代码进一步挖掘nvme ssd加载过程:
- 设备发现和初始化:
-
- 在Linux内核中,NVMe设备的发现和初始化主要由PCI子系统和NVMe子系统共同完成。
- PCI子系统通过扫描PCI总线,发现NVMe设备,并为其创建相应的pci_dev结构体。
- NVMe子系统通过注册的probe函数(通常为nvme_probe)来初始化NVMe设备,创建相应的nvme_dev结构体,并设置设备的读写策略等属性。
static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{ struct nvme_dev *dev; int err; dev = kzalloc(sizeof(struct nvme_dev), GFP_KERNEL); if (!dev) return -ENOMEM; err = nvme_init_device(pdev, dev); if (err) goto free_dev; pci_set_drvdata(pdev, dev); return 0; free_dev: kfree(dev); return err;
}
上述代码是NVMe子系统的probe函数,用于初始化NVMe设备。它首先分配一个nvme_dev结构体,然后调用nvme_init_device函数进行设备初始化。最后,将设备实例作为pci_drvdata关联到PCI设备上。
- 注册硬盘设备:
-
- 在NVMe子系统中,会创建一个名为nvme_queue的结构体,用于管理设备的I/O队列。
- 通过调用blk_add_disk函数,将NVMe设备注册为一个新的硬盘设备,并创建相应的device_t结构体。
- 这个结构体包含了设备的属性和管理接口,可以被块层用来管理硬盘的读写操作。
int nvme_register_disk(struct nvme_dev *dev, struct gendisk *disk)
{ int ret; // 初始化请求队列,并设置请求处理函数为nvme_ops ret = blk_init_queue(request_queue_callee(disk), &nvme_ops); if (ret) return ret; // 将NVMe设备注册为一个新的硬盘设备 ret = add_disk(disk); if (ret) blk_cleanup_queue(request_queue_callee(disk)); return ret;
}
上述代码是NVMe子系统注册硬盘设备的函数。它首先调用blk_init_queue函数初始化请求队列,并设置请求处理函数为nvme_ops。然后调用add_disk函数将NVMe设备注册为一个新的硬盘设备。如果注册失败,则清理请求队列。
- 加载文件系统:
-
- 这部分代码通常不在NVMe子系统中,而是在文件系统相关的模块中。
- 文件系统的加载过程取决于具体的文件系统类型和配置。例如,对于UBI文件系统,会通过ubi_scan函数扫描并解析UBI引导扇区或超级块。
- 创建文件系统实例的过程可能会涉及到分配内存、初始化文件系统结构等操作。
int ubi_scan(const char *name, int verbose)
{ // 扫描并解析UBI引导扇区或超级块 // ... // 创建UBI文件系统实例 ubi = ubi_alloc(&ubi->vols[0].eba_tbl, vols, vcnt, UBI_EMULATION_FLAGS); if (!ubi) { ubi_err("failed to allocate memory for UBI volume structure"); return -ENOMEM; } // ...
}
- 挂载文件系统:
-
- 在VFS(Virtual File System)模块中,通过调用mount函数来挂载文件系统。
- 这个函数会创建VFS节点和目录项,并将其关联到文件系统实例上。
- 挂载成功后,操作系统就可以通过标准的文件读写接口来访问硬盘了。
- 访问硬盘:
-
- 在块层中,所有的硬盘读写操作都会被抽象为请求(request)结构体。
- 当应用程序调用read、write等系统调用时,块层会将这些请求封装为一个请求结构体,并将其加入到NVMe设备的I/O队列中。
- NVMe子系统通过处理这个队列,将请求发送给硬件进行实际的读写操作。
// 请求结构体
typedef struct request { // 请求相关字段
} request; // 系统调用处理函数
void system_call_handler(void) { // 封装请求结构体 request req; // ... // 将请求加入到NVMe设备的I/O队列中 enqueue(nvme_io_queue, &req);
} // NVMe子系统处理函数
void nvme_subsystem_handler(void) { // 处理I/O队列 while (!empty(nvme_io_queue)) { request *req = dequeue(nvme_io_queue); // 发送请求给硬件进行实际的读写操作 hardware_io(req); }
}