Preview
格式化磁盘其实就是将文件系统的元数据固化到磁盘中去,这样在mount的时候可以从磁盘中读取相应的数据
比如ext4文件系统的格式化就是用mkfs.ext4这个命令
前置知识
- 你至少要知道文件系统(指整个文件系统体系包括vfs这些)的结构,读写流程、原理要熟悉才行
- 需要对vfs中的数据格式有所了解,可以参考:https://juejin.cn/post/7033574024198406174 写挺好
组织格式
Dummy block | Super block | bmap | imap |inode table | data block0 | data block1 | ... ...
本文件系统主要以上面的格式组织数据
说明一下Dummy block这个位置其实是引导块的位置,但是我们这个不需要引导块数据,所以就随便用数据填充就行。
上面其他名词的含义自己查。。。
源码解析
源码在mkfs.c里面,main函数先通过open()系统调用打开磁盘,获得fd,然后调用init_disk()函数计算相关数据
static int init_disk(int fd, const char* path)
{
// 获取磁盘的大小
disk_size = get_file_size(path);
if (disk_size == -1) {
perror("Error: can not get disk size!\n");
return -1;
}
printf("Disk size id %lu\n", disk_size);
super_block.version = 1;
super_block.block_size = HUST_BLOCKSIZE;
// 这个magic是用来做数据校验的,magic错了那就证明这个磁盘上的文件系统坏了
super_block.magic = MAGIC_NUM;
// 磁盘上的所有数据都是以块的方式组织的(可能有歧义,理解我的意思就行),块的数量等于磁盘大小除以块大小向下取整
super_block.blocks_count = disk_size/HUST_BLOCKSIZE;
printf("blocks count is %llu\n", super_block.blocks_count);
// 这里iNode的数量设置成和block一样
super_block.inodes_count = super_block.blocks_count;
super_block.free_blocks = 0;
//计算bmap(块位图), 注:用一个bit表示一个block,一个block有8*HUST_BLOCKSIZE这么多bit
bmap_size = super_block.blocks_count/(8*HUST_BLOCKSIZE);
// 第2个block开始用作bmap
super_block.bmap_block = RESERVE_BLOCKS;
// 小处理,应该能理解,向上取整的意思
if (super_block.blocks_count%(8*HUST_BLOCKSIZE) != 0) {
bmap_size += 1;
}
// 分配内存
bmap = (uint8_t *)malloc(bmap_size*HUST_BLOCKSIZE);
// 全部置零,防止脏数据
memset(bmap,0,bmap_size*HUST_BLOCKSIZE);
//计算imap
imap_size = super_block.inodes_count/(8*HUST_BLOCKSIZE);
// 从bmap结束后的第一块开始做imap
super_block.imap_block = super_block.bmap_block + bmap_size;
if(super_block.inodes_count%(8*HUST_BLOCKSIZE) != 0) {
imap_size += 1;
}
imap = (uint8_t *)malloc(imap_size*HUST_BLOCKSIZE);
memset(imap,0,imap_size*HUST_BLOCKSIZE);
//计算inode_table
inode_table_size = super_block.inodes_count/(HUST_BLOCKSIZE/HUST_INODE_SIZE);
// 从imap结束的第一个block开始做inode_table
super_block.inode_table_block = super_block.imap_block + imap_size;
// 从inode_table结束后的第一个block开始做data_block,下面这种算法更直观易理解
super_block.data_block_number = RESERVE_BLOCKS + bmap_size + imap_size + inode_table_size;
// 计算空闲的data_block数,这里为啥要减一如果理解不了,可以画个图捋捋
super_block.free_blocks = super_block.blocks_count - super_block.data_block_number - 1;
//设置bmap以及imap
int idx;
// plus one becase of the root dir
for (idx = 0; idx < super_block.data_block_number + 1; ++idx) {
// 填充bmap
if (set_bmap(idx, 1)) {
return -1;
}
}
return 0;
}
计算好数据之后就是固化数据到磁盘了, 这里唯一要说的就是在固化inode table的时候做了一些操作,主要是创建了root目录
static int write_itable(int fd)
{
// 获取用户和用户组ID
uint32_t _uid = getuid();
uint32_t _gid = getgid();
ssize_t ret;
struct HUST_inode root_dir_inode;
root_dir_inode.mode = S_IFDIR; // S_IFDIR代表该文件为目录类型
root_dir_inode.inode_no = HUST_ROOT_INODE_NUM; // HUST_ROOT_INODE_NUM值为0,也就是说存在第一个inode中
root_dir_inode.blocks = 1; // 该文件占用一个block
root_dir_inode.block[0] = super_block.data_block_number; // 该文件放在最后一个block中
root_dir_inode.dir_children_count = 3; // 有三个子文件
root_dir_inode.i_gid = _gid;
root_dir_inode.i_uid = _uid;
root_dir_inode.i_nlink = 2;
root_dir_inode.i_atime = root_dir_inode.i_mtime = root_dir_inode.i_ctime = ((int64_t)time(NULL));
ret = write(fd, &root_dir_inode, sizeof(root_dir_inode));
if (ret != sizeof(root_dir_inode)) {
perror("write_itable error!\n");
return -1;
}
struct HUST_inode onefile_inode;
onefile_inode.mode = S_IFREG; // S_IFREG代表该文件为普通文件类型,regular file
onefile_inode.inode_no = 1;
onefile_inode.blocks = 0; // 没有数据的文件
onefile_inode.block[0] = 0;
onefile_inode.file_size = 0;
onefile_inode.i_gid = _gid;
onefile_inode.i_uid = _uid;
onefile_inode.i_nlink = 1;
onefile_inode.i_atime = onefile_inode.i_mtime = onefile_inode.i_ctime = ((int64_t)time(NULL));
ret = write(fd, &onefile_inode, sizeof(onefile_inode));
if (ret != sizeof(onefile_inode)) {
perror("write_itable error!\n");
return -1;
}
// 接下来创建了三个文件记录,分别表示root目录下的当前目录(.),父目录(..),和一个名为file的文件
// 也就是说目录的data_block中是存放的子文件的信息
// HUST_dir_record定义在下面
struct HUST_dir_record root_dir_c;
const char* cur_dir = ".";
const char* parent_dir = "..";
memcpy(root_dir_c.filename, cur_dir, strlen(cur_dir) + 1);
root_dir_c.inode_no = HUST_ROOT_INODE_NUM;
struct HUST_dir_record root_dir_p;
memcpy(root_dir_p.filename, parent_dir, strlen(parent_dir) + 1);
root_dir_p.inode_no = HUST_ROOT_INODE_NUM;
struct HUST_dir_record file_record;
const char* onefile = "file";
memcpy(file_record.filename, onefile, strlen(onefile) + 1);
file_record.inode_no = 1;
// 下面是把文件指针seek到root目录对应的block位置
off_t current_off = lseek(fd, 0L, SEEK_CUR);
printf("Current seek is %lu and rootdir at %lu\n", current_off
, super_block.data_block_number*HUST_BLOCKSIZE);
if(-1 == lseek(fd, super_block.data_block_number*HUST_BLOCKSIZE, SEEK_SET)) {
perror("lseek error\n");
return -1;
}
// 将这三个文件记录写入block中
ret = write(fd, &root_dir_c, sizeof(root_dir_c));
ret = write(fd, &root_dir_p, sizeof(root_dir_p));
ret = write(fd, &file_record, sizeof(file_record));
if (ret != sizeof(root_dir_c)) {
perror("Write error!\n");
return -1;
}
printf("Create root dir successfully!\n");
return 0;
}
HUST_dir_record的定义
struct HUST_dir_record
{
char filename[HUST_FILENAME_MAX_LEN];
uint64_t inode_no;
};
好!格式化完成