格式化磁盘

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;
};

好!格式化完成

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容