存储课程学习笔记4_设计数据结构管理nvme磁盘(基于已经通过struct nvme_user_io和ioctl实现了对nvme设备的读写)

news2024/11/16 19:45:15

已经测试了直接操作nvme磁盘的方式,那么基于可以读写nvme磁盘的功能,如何扩展呢。

通过struct nvme_user_io结构体+ioctl实现对nvme磁盘的读写访问,可以定义结构,对整个磁盘进行管理,以配合业务进行衍生功能。

0:总结

已经能对nvme设备进行基本的读和写了,如何对nvme磁盘进行管理。

根据不同的业务设计,自己定义数据结构对磁盘进行管理,这里定义结构进行管理测试。

可以看看ioctl中参数NVME_IOCTL_SUBMIT_IO 跟踪代码对细节进行了解。

思考:基于当前磁盘的读写,如何实现一个文件系统对该磁盘进行控制。

1:梳理管理方案。(与需求有关,不同需求设计不同,预留磁盘大小不一样,方案不同)

对磁盘的管理,实际上就是对磁盘上的文件进行管理,目录也是一种特殊的文件吧。

对文件进行管理,涉及两个点:

===》存储文件的相关属性,一般用inode节点。

===》文件实际上有自身的内容的,存储在特定的block中。

设计数据结构,实现对整个磁盘进行管理,也就是对文件的inode节点和实际内容存储 管理。

**数据结构的设计如图:**每个扇区,块的大小是512,bitmap每8bit为1byte,占用的块个数为total/(512*8)

在这里插入图片描述

在磁盘中细分需要结构的预留空间:

根据业务,总的super结构可以存储在第0个逻辑块处,

然后管理inode的bitmap可以存储在第一个逻辑处,然后预留inode节点内容空间。

最后是实际存储文件内容的逻辑块位置,(存储block的bitmap以及真正block的管理)

在这里插入图片描述

inode节点只是存储文件的属性,占用空间相对于文件真正内容就很少了(这里设计的都是存储的超大文件方案),则上面的设计取了super结构块+inode管理块共占用总内存的0.1%,剩下的都分配给存储文件内容的block块。

2:设计对应的数据结构。

#define SECTOR_SIZE		512    //块大小是512   至少分配一个块,以块为单位,不够得空着 计算时用到

//第0个块存储superblock   第一个块存储inode的bitmap 紧跟着存储inode信息 ===》共占用1/1000的磁盘大小
//    接下来1/1000的位置存储block的bitmap以及 对应的起始位置  block中真正存储文件信息 
struct zvvfs_superblock {

	uint32_t magic;   //总的内存大小   21,474,836,480 byte = 20G

	uint32_t sb_nr_inodes_total;   //总的inode文件节点的个数 
	uint32_t sb_nr_blocks_total;   //总的blocks的个数
	
	uint32_t sb_nr_inode_free;     //剩余的inode节点的个数
	uint32_t sb_nr_block_free;	   //剩余block的个数

	uint32_t sb_inode_table;       //inode节点开始位置  怎么管理inode待定
	uint32_t sb_inode_bitmap;      //inode对应bitmap块对应的位置

	uint32_t sb_block_table;		//真正block位置的管理
	uint32_t sb_block_bitmap;		//block块对应的bitmap位置
};


//文件 或者目录的必要属性信息 因为要预留inode节点空间,要根据结构设计。
struct zvvfs_inode {
	uint32_t i_mode;
	uint32_t i_uid;
	uint32_t i_gid;
	uint32_t i_size;
	uint32_t i_ctime;
	uint32_t i_atime;
	uint32_t i_mtime;
	uint32_t i_blocks;
};

//基于上面的基础结构,还需要管理文件/目录的额外结构  以及上层方案。
//文件名和对应的inode对应关系 inode中有实际数据存储位置 管理这个结构即所有文件了
struct zvvfs_file {
	uint32_t inode;
	char filename[128];
};

//目录管理文件    4k  可以自己设计大小 管理目录架构,也可以其他方案。
#define ZVVFS_FILES_PER_BLOCK (4096 / sizeof(struct zvvfs_file))
struct zvvfs_dir_block {
	struct zvvfs_file files[ZVVFS_FILES_PER_BLOCK]; 
};

3:构造super块,提前分配管理磁盘

注意逻辑块的基本大小为512,(和磁盘扇区大小的关系?)

这里已经设计了磁盘中不同位置存储不同的信息,已经设计好了。 比如inode节点的个数限制(1/10000的磁盘空间存储inode节点),比如block块的位置(按业务涉及,这里设计block块单位为2M,设计基于存储大文件)。

//设计super块的内容
struct superblock* zvvfs_write_superblock(int fd, long size)
{
	printf(" ====> %ld \n", size);
	struct superblock* sb = malloc(sizeof(struct superblock)); 
	if (!sb) {
		return NULL;
	}
	memset(sb, 0, sizeof(struct superblock));

	long total_size = size;
	uint32_t sector_cnt = total_size/512;  //扇区的个数 每个扇区的大小一般512

	sb->info.magic = 0xFEEDBABE;  //需要类型转换 total_size;  // 20*1024*1024*1024; //实际真个磁盘的大小

	//inode节点的总个数
	sb->info.sb_nr_inodes_total =  sector_cnt /10000;  //这里总共sector_cnt个扇区 用1/10000用来存储inode节点,可以根据需要自己设计
	sb->info.sb_nr_inodes_total *= (512/sizeof(struct zvvfs_inode)); //真正inode节点的个数

	//blocks的总个数  因为设计基于大文件的存储,每个块以2M为准
	sb->info.sb_nr_blocks_total =  sector_cnt - sb->info.sb_nr_inodes_total;  //剩下的节点存储blocks块,这里设计block每个块大小为2M 2*1024*1024
	sb->info.sb_nr_blocks_total = sb->info.sb_nr_blocks_total/(2*1024*1024/512); //2M一个页

	//inode bitmap的开始位置  table的位置从bitmap开始到预留节点个数计算
	sb->info.sb_inode_bitmap = 1;  //节点的第一个块存储inode的bitmap
	//bitmap的块的位置   是inode节点的个数除以512(块的大小) 再除以8 (1个byte有8bit)     先除以8算出bitmap占用的字节个数 再除以512块求得块的个数 
	sb->info.sb_inode_table = sb->info.sb_inode_bitmap + (sb->info.sb_nr_inodes_total/(512*8))+1;

	sb->info.sb_block_bitmap = sector_cnt / 10000 + 1; //前面1/10000的位置+1
	sb->info.sb_block_table = sb->info.sb_block_bitmap + (sb->info.sb_nr_blocks_total/(512*8) + 1); 
//空闲的个数  
	sb->info.sb_nr_inode_free = sb->info.sb_nr_inodes_total;
	sb->info.sb_nr_block_free = sb->info.sb_nr_blocks_total;

	printf("magic = %u  \n"
		"sb_nr_inodes_total=%d\n"
		"sb_nr_blocks_total=%d\n"
		"sb_inode_bitmap=%d\n"
		"sb_inode_table=%d\n"
		"sb_block_bitmap=%d\n"
		"sb_block_table=%d\n"
		"sb_nr_inode_free=%d\n"
		"sb_nr_block_free=%d\n\n\n",
		sb->info.magic ,
		sb->info.sb_nr_inodes_total, sb->info.sb_nr_blocks_total, 
		sb->info.sb_inode_bitmap, sb->info.sb_inode_table,
		sb->info.sb_block_bitmap, sb->info.sb_block_table, 
		sb->info.sb_nr_inode_free, sb->info.sb_nr_block_free);
	
	if (0 != nvme_write(fd, 0, sb, sizeof(struct superblock))) {
		printf("zvvfs_write_superblock failed\n");
		free(sb);
		return NULL;
	}
	return sb;
}

4:测试代码。

//实际上就是构造整个数据结构  按方式对磁盘进行管理。
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <linux/fs.h>

#include <linux/nvme_ioctl.h>

#define SECTOR_SIZE		512    //块大小是512   这里可以思考块和页的关系 

//第0个块存储superblock   第一个块存储inode的bitmap 紧跟着存储inode信息 ===》共占用1/1000的磁盘大小
//    接下来1/1000的位置存储block的bitmap以及 对应的起始位置  block中真正存储文件信息 
struct zvvfs_superblock {

	uint32_t magic;   //总的内存大小   21,474,836,480 byte = 20G

	uint32_t sb_nr_inodes_total;   //总的inode文件节点的个数 
	uint32_t sb_nr_blocks_total;   //总的blocks的个数
	
	uint32_t sb_nr_inode_free;     //剩余的inode节点的个数
	uint32_t sb_nr_block_free;	   //剩余block的个数

	uint32_t sb_inode_table;       //inode节点开始位置  怎么管理inode待定
	uint32_t sb_inode_bitmap;      //inode对应bitmap块对应的位置

	uint32_t sb_block_table;		//真正block位置的管理
	uint32_t sb_block_bitmap;		//block块对应的bitmap位置
};

//文件 或者目录的必要属性信息
struct zvvfs_inode {
	uint32_t i_mode;
	uint32_t i_uid;
	uint32_t i_gid;
	uint32_t i_size;
	uint32_t i_ctime;
	uint32_t i_atime;
	uint32_t i_mtime;
	uint32_t i_blocks;
};

//文件名和对应的inode对应关系 inode中有实际数据存储位置 管理这个结构即所有文件了
struct zvvfs_file {
	uint32_t inode;
	char filename[128];
};

//目录管理文件    4k  可以自己设计大小 管理目录架构,也可以其他方案。
#define ZVVFS_FILES_PER_BLOCK (4096 / sizeof(struct zvvfs_file))
struct zvvfs_dir_block {
	struct zvvfs_file files[ZVVFS_FILES_PER_BLOCK]; 
};

//第一个块存储整个磁盘的管理信息 总的块个数  支持总的块个数 总的inode个数 
struct superblock {  
	struct zvvfs_superblock info;
	char padding[SECTOR_SIZE - sizeof(struct zvvfs_superblock)]; //补齐一个块用
};

int nvme_write(int fd, __u64 lba, void *blocks, int length);
struct superblock* zvvfs_write_superblock(int fd, long size);
int main(int argc, char *argv[]) {

	if (argc < 2) {
		return -1;
	}

	const char *filename = argv[1];
	
	int fd = open(filename, O_RDWR);
	if (fd < 0) {
		perror("open\n");
		return -1;
	}

	struct stat stat_buf;
	int ret = fstat(fd, &stat_buf);
	if (ret) {
		perror("fstat\n");
		return -1;
	}

	/******************************
	S_IFSOCK:套接字(socket)
	S_IFLNK:符号链接(symbolic link)
	S_IFREG:普通文件(regular file)
	S_IFBLK:块设备文件(block device)
	S_IFDIR:目录(directory)
	S_IFCHR:字符设备文件(character device)
	S_IFIFO:命名管道(FIFO)
	******************************/
	if ((stat_buf.st_mode & S_IFMT) != S_IFBLK) { //是块设备文件
		return -1;
	}
	//获取关联块设备的大小   字节为单位
	long blk_size = 0;
	if (0 != ioctl(fd, BLKGETSIZE64, &blk_size)) {
		perror("ioctl\n");
		return -1;
	}
	stat_buf.st_size = blk_size;

	printf("size: %ld\n", stat_buf.st_size);
	printf("sector: %ld\n", stat_buf.st_size / SECTOR_SIZE);

	//根据相关参数设置super块
	struct superblock* sb = zvvfs_write_superblock(fd, blk_size);

//接下来就是inode节点的bitmap和对应内容的写  以及文件实际内容写入block中	
	//写一个文件时  先构造inode节点 写入信息和bitmap更新  实际内容时block写入,也是更新bitmap对应位置。
	//另外就是管理目录下文件 目录架构和inode之间的关系  目录层级关系
	free(sb);
	close(fd);
	return 0;
}

//设计super块的内容
struct superblock* zvvfs_write_superblock(int fd, long size)
{
	printf(" ====> %ld \n", size);
	struct superblock* sb = malloc(sizeof(struct superblock)); 
	if (!sb) {
		return NULL;
	}
	memset(sb, 0, sizeof(struct superblock));

	long total_size = size;
	uint32_t sector_cnt = total_size/512;  //扇区的个数 每个扇区的大小一般512

	sb->info.magic = 0xFEEDBABE;  //需要类型转换 total_size;  // 20*1024*1024*1024; //实际真个磁盘的大小

	//inode节点的总个数
	sb->info.sb_nr_inodes_total =  sector_cnt /10000;  //这里总共sector_cnt个扇区 用1/10000用来存储inode节点,可以根据需要自己设计
	sb->info.sb_nr_inodes_total *= (512/sizeof(struct zvvfs_inode)); //真正inode节点的个数

	//blocks的总个数  因为设计基于大文件的存储,每个块以2M为准
	sb->info.sb_nr_blocks_total =  sector_cnt - sb->info.sb_nr_inodes_total;  //剩下的节点存储blocks块,这里设计block每个块大小为2M 2*1024*1024
	sb->info.sb_nr_blocks_total = sb->info.sb_nr_blocks_total/(2*1024*1024/512); //2M一个页

	//inode bitmap的开始位置  table的位置从bitmap开始到预留节点个数计算
	sb->info.sb_inode_bitmap = 1;  //节点的第一个块存储inode的bitmap
	//bitmap的块的位置   是inode节点的个数除以512(块的大小) 再除以8 (1个byte有8bit)     先除以8算出bitmap占用的字节个数 再除以512块求得块的个数 
	sb->info.sb_inode_table = sb->info.sb_inode_bitmap + (sb->info.sb_nr_inodes_total/(512*8))+1;

	sb->info.sb_block_bitmap = sector_cnt / 10000 + 1; //前面1/10000的位置+1
	sb->info.sb_block_table = sb->info.sb_block_bitmap + (sb->info.sb_nr_blocks_total/(512*8) + 1); 
//空闲的个数  
	sb->info.sb_nr_inode_free = sb->info.sb_nr_inodes_total;
	sb->info.sb_nr_block_free = sb->info.sb_nr_blocks_total;

	printf("magic = %u  \n"
		"sb_nr_inodes_total=%d\n"
		"sb_nr_blocks_total=%d\n"
		"sb_inode_bitmap=%d\n"
		"sb_inode_table=%d\n"
		"sb_block_bitmap=%d\n"
		"sb_block_table=%d\n"
		"sb_nr_inode_free=%d\n"
		"sb_nr_block_free=%d\n\n\n",
		sb->info.magic ,
		sb->info.sb_nr_inodes_total, sb->info.sb_nr_blocks_total, 
		sb->info.sb_inode_bitmap, sb->info.sb_inode_table,
		sb->info.sb_block_bitmap, sb->info.sb_block_table, 
		sb->info.sb_nr_inode_free, sb->info.sb_nr_block_free);
	
	if (0 != nvme_write(fd, 0, sb, sizeof(struct superblock))) {
		printf("zvvfs_write_superblock failed\n");
		free(sb);
		return NULL;
	}
	return sb;
}

//在对应块的位置 写入信息
int nvme_write(int fd, __u64 lba, void *blocks, int length) {

	struct nvme_user_io io;
	memset(&io, 0, sizeof(io));
	
	io.addr = (__u64)blocks;  
	io.slba = lba;
	io.nblocks = (length-1) / SECTOR_SIZE + 1;  //(15 + 1) * 256;
	io.opcode = 1;

	if (-1 == ioctl(fd, NVME_IOCTL_SUBMIT_IO, &io)) { //write
		perror("ioctl write");
		return -1;
	}

	//printf("write lba: %lld, nblocks: %d\n", lba, length / SECTOR_SIZE + 1);
	return 0;
}

5:测试结果

root@ubuntu:/home/ubuntu/storage/test_nvme# ./nvme_user_superblock /dev/nvme0n1 
size: 21474836480
sector: 41943040
 ====> 21474836480 
 ====> 21474836480 
magic = 4276992702  
sb_nr_inodes_total=67104
sb_nr_blocks_total=10223
sb_inode_bitmap=1
sb_inode_table=18
sb_block_bitmap=4195
sb_block_table=4198

sb_nr_inode_free=67104
sb_nr_block_free=10223

基于结构设计,创建一个文件,实际上就是给文件名和inode节点以及实际存储blobk位置信息进行关联,以及通过上述结构管理整个磁盘。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2120073.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用Idea新建一个Demo项目基于WebApp目录下的服务器访问测试-作业篇

文章目录 前言一、Maven环境搭建二、创项目总结 前言 例如&#xff1a;第一堂JAVAweb 轻量级的项目搭建运行作业。 一、Maven环境搭建 环境变量配置 新建系统变量 MAVEN_HOMEC:\apache-maven-3.6.3path后边加上 %MAVEN_HOME%\bin然后控制台cmd 检查变量配置是否完全 mvn -v…

ABC 370 E - Avoid K Partition

原题链接&#xff1a;E - Avoid K Partition 题意&#xff1a;给长度为n的数组&#xff0c;将数组划分成任意份&#xff0c;但是每一份的总和都不能是k&#xff0c;问有多少种分割方法。 思路&#xff1a;dp&#xff0c;f[i]&#xff0c;代表前i个元素满足题意的划分的总和&a…

申请Shopify PayPal账号的时间和所需资料如下

申请流程 注册账户&#xff1a; 访问 PayPal官网。点击右上角的“注册”按钮&#xff0c;选择“企业账户”。输入电子邮箱地址和密码&#xff0c;点击“继续”。填写公司信息&#xff1a; 提供公司名称、营业执照地址、联系方式等信息。确保公司名称与营业执照上的名称一致。填…

1.2 半导体二极管笔记

文章目录 一、符号二、 伏安特性1、正向特性2、反向特性3、反向击穿特性 二、二极管的主要参数1、最大整流电流 I F I_F IF​2、反向击穿电压 U B R U_{BR} UBR​3、反向饱和电流 I S I_S IS​4、最高工作频率 f m f_m fm​5、极间电容 C d C_d Cd​ 四、二极管的测试1、二极管…

突破最强算法模型,Transformer !!

这几天&#xff0c;大家对于Transformer的问题&#xff0c;还是不少。 今儿再和大家聊聊~ 简单来说&#xff0c;Transformer 是一种神经网络模型&#xff0c;在机器翻译、语言理解等任务中表现特别好。它的核心思想是自注意力机制&#xff08;Self-Attention&#xff09;&…

el-image(vue 总)

一 加载静态资源 在第一次使用vue3开发项目时&#xff0c;使用require&#xff08;‘图片路径’&#xff09;&#xff0c;结果浏览器报错&#xff1a; Uncaught (in promise) ReferenceError: require is not defined 因为require是webpack提供的一种加载能力&#xff0c;但…

经典文献阅读之--Multi S-Graphs(一种高效的实时分布式语义关系协同SLAM)

0. 简介 协作同时定位与建图&#xff08;CSLAM&#xff09;对于使多个机器人能够在复杂环境中操作至关重要。大多数CSLAM技术依赖于原始传感器测量或低级特征&#xff0c;如关键帧描述符&#xff0c;这可能由于缺乏对环境的深入理解而导致错误的闭环。此外&#xff0c;这些测量…

[SDK]-菜单 和 树控件

前言 各位师傅大家好&#xff0c;我是qmx_07&#xff0c;今天给大家讲解菜单和树控件的相关知识 菜单 认识菜单 及 创建自定义菜单栏 资源文件 -> 项目名.rc ->Menu 这是系统提供的默认菜单&#xff0c;也可以往里面添加修改内容 以下是 自定义菜单栏&#xff1a; 创…

如何通过Autoscaler实现Kubernetes的伸缩?

本文将介绍如何在流量高峰之前使用KEDA和Cron scaler主动调整工作负载规模。 在设计Kubernetes集群时&#xff0c;我们可能经常需要回答以下问题&#xff1a; 集群伸缩需要多长时间&#xff1f;在新Pod创建之前需要等待多长时间&#xff1f; 有四个主要因素会影响集群的伸缩…

Windows环境下 VS2022 编译 OGG 源码

OGG OGG音频编码格式&#xff0c;全称为Ogg Vorbis&#xff0c;是一种开源且无专利限制的音频压缩格式。它被设计用来提供高质量的音频存储和传输&#xff0c;同时保持较小的文件大小。OGG Vorbis支持多声道音频&#xff0c;并且可以处理可变比特率&#xff0c;这意味着它可以根…

分享从零开始学习网络设备配置--任务6.3 使用基本ACL限制网络访问

任务描述 某公司构建了互联互通的办公网&#xff0c;为保护公司内网用户数据的安全&#xff0c;该公司实施内网安全防范措施。公司分为经理部、财务部和销售部&#xff0c;分属3个不同的网段&#xff0c;3个部门之间用路由器进行信息传递。为了安全起见&#xff0c;公司领导要求…

npm 清除缓存

npm cache clean --forcenpm cache verify# 安装依赖 npm install# 建议不要直接使用 cnpm 安装依赖&#xff0c;会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题 npm install --registryhttps://registry.npmmirror.com npm彻底清理缓存_npm cache verify-CSD…

OpenCV仿射变换和透视变换函数(C++)

文章目录 引言图像仿射变换 warpAffine()图像的旋转仿射变换 透视变换 warpPerspective()透视变换例子参考文献 **仿射变换相关函数** cv::transform()&#xff1a;对一组点进行仿射变换 cv::warpAffine()&#xff1a;对整幅图像进行仿射变换 cv::getAffineTransform()&#xf…

[基于 Vue CLI 5 + Vue 3 + Ant Design Vue 4 搭建项目] 01 安装 nodejs 环境

文章目录 下载安装测试 这里让我们去看看如何安装一下 nodejs 的环境 下载 通过官网进行下载安装包 官网 https://nodejs.org/zh-cn点击 下载 Node.js (LTS) 开始下载 安装 下载完成之后&#xff0c;双击进行安装 开始进行安装了 这样&#xff0c;node.js 就安装好了 测试 …

Ubuntu下使用Cron定时任务

Ubuntu下使用Cron定时任务 文章目录 Ubuntu下使用Cron定时任务概述Cron 工作原理crontab的基本指令使用Cron 定时任务语法用户的crontab 文件系统的crontab 文件cron 任务设置环境变量1. 直接在 crontab 中声明变量2. 将变量声明为命令的一部分3. 从文件加载变量使用环境变量控…

网络基础入门指南(二)

一、什么是交换机 交换机&#xff0c;Switch 用于将多台计算机/交换机连接到一起&#xff0c;组建网络 交换机负责为其中任意两台计算机提供独享线路进行通信类型&#xff1a; 非网管&#xff08;即插即用&#xff09;&#xff0c;便宜&#xff0c;不可管理 网管&#xff0…

CCF推荐C类会议和期刊总结:(计算机体系结构/并行与分布计算/存储系统领域)

中国计算机学会&#xff08;CCF&#xff09;在计算机体系结构、并行与分布计算、存储系统领域推荐了一系列C类会议和期刊。此汇总涵盖了各期刊和会议的全称、出版社、dblp文献网址及研究领域&#xff0c;为学者和研究人员提供了重要的学术交流资源。列表包括《ACM Journal on E…

Javase复习day21算法、arrays、Lamdba表达式

常见算法 查找算法 基本查找 package search;public class BasicSearchDemo1 {public static void main(String[] args) {//基本算法&#xff08;顺序查找&#xff09;int[] arr {131,23,57,37,95,48,57,43};System.out.println(basicSearch(arr, 43));}public static boo…

基于PINN 进行混合流体中的热量与质量扩散预测

近年来&#xff0c;物理信息神经网络&#xff08;PINN&#xff0c;Physics-Informed Neural Networks&#xff09;成为解决复杂物理问题的一种强大工具。PINN 的核心在于结合物理定律和机器学习的能力&#xff0c;直接从偏微分方程&#xff08;PDEs&#xff09;出发&#xff0c…

LDtk to Unity 大致流程和一些注意点

因为自己也还在探索中&#xff0c;所以有点杂乱&#xff0c;后续有其他的东西还会继续更。 制作 先套用这个模板&#xff0c;确定基础的循环。再去丰富。 LDtk一小时完全入门教程_哔哩哔哩_bilibili To Unity 安装包 LDtk To Unity 输出 图集 在Run after saving运行 ../../Lib…