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

已经测试了直接操作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位置信息进行关联,以及通过上述结构管理整个磁盘。

  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值