简单Linux文件系统核心函数


简介

此文承接上文简单Linux文件系统数据结构设计,内容是文件系统中核心函数的实现。

申请iNode节点

//申请iNode节点,size单位为Byte
int FileSystem::alloc_inode(unsigned long size, iNode &node,bool is_dentry)
{
    unsigned int blocks_needed = ceil((double)size / s_block.blockSize);//需要存储内容的块数
    //最大大小超过限制,iNode节点不足时
    if (size>s_block.maxBytes || s_block.inode_remain == 0){
        return -1;
    }
    if (size == 0){
        blocks_needed = 10;
    }
    fileDisk.seekg(s_block.inodemap_pos, ios::beg);//挪动到位图位置
    bool is_end = false;
    int inode_no = 1;//使用的iNode号码
    unsigned char* bytes = new unsigned char[s_block.blockSize];//读取的数组

    int block_node_num = s_block.blockSize / sizeof(unsigned int);//每块可存的iNode的id的数目
    if (blocks_needed <= 10){
        //可以在10个直接块中放下
    }
    else if (blocks_needed <= (10 + block_node_num)){
        //加上一个一次间接块
        blocks_needed += 1;
    }
    else if (blocks_needed <= (10 + block_node_num + block_node_num*block_node_num)){
        //加上一个二次间接块
        blocks_needed += 2 + block_node_num;
    }
    else if (blocks_needed <= (10 + block_node_num + block_node_num*block_node_num + block_node_num*block_node_num*block_node_num)){
        //加上一个三次间接块
        blocks_needed += 3 + 2 * block_node_num + block_node_num*block_node_num;
    }
    if (blocks_needed > s_block.block_remain){
        //超过剩余量
        return -1;
    }
    //寻找空闲的iNode
    for (int i = 0; i < ceil(s_block.inode_num / (8 * s_block.blockSize)); i++){
        if (is_end)
            break;//遍历完所有位图后退出
        fileDisk.read((char *)bytes, s_block.blockSize);//一次读取一块
        unsigned char mask = 1;
        mask = mask << 7;
        for (int index = 0; index<s_block.blockSize; index++){
            if (is_end)
                break;//遍历完所有位图后退出
            unsigned char byte = bytes[index];
            for (int j = 0; j < 8; j++){
                if (!((byte << j)&mask)){
                    //若有空位
                    bytes[index] = byte | (1 << (7-j));
                    fileDisk.seekg(s_block.inodemap_pos + i*s_block.blockSize, ios::beg);
                    fileDisk.write((char *)bytes, s_block.blockSize);//位图改变整块写回
                    is_end = true;//结束
                    break;
                }
                inode_no += 1;
                if (inode_no>s_block.inode_num){
                    is_end = true;
                    break;
                }
            }
        }
    }
    vector<unsigned int> block_list;
    alloc_blocks(blocks_needed, block_list);//申请磁盘块
    node = iNode(inode_no,size,blocks_needed,block_list);
    node.i_mode = (7<<8)+(7<<4)+7;//777
    if (is_dentry){
        unsigned short mode = 1;
        mode = mode << 14;
        node.i_mode = mode + (7 << 8) + (7 << 4) + 7;//设定为文件夹
    }
    node.i_uid = currUser.u_id;
    node.i_gid = currUser.u_id;
    write_inode(node);
    //更新相应的超级块信息
    s_block.inode_remain--;
    s_block.block_remain -= blocks_needed;
    seekAndSave<superBlock>(0, s_block);
    return 1;
}

申请块

//申请块
int FileSystem::alloc_blocks(int num, vector<unsigned int> &list){
    fileDisk.seekg(s_block.bitmap_pos, ios::beg);//挪动到位图位置
    bool is_end = false;
    unsigned int block_no = 1;//使用的block号码
    unsigned char* bytes = new unsigned char[s_block.blockSize];//读取的数组
    //寻找空闲的block
    for (int i = 0; i < ceil(s_block.block_num / (8 * s_block.blockSize)); i++){
        if (is_end)
            break;//遍历完所有位图后退出
        fileDisk.read((char *)bytes, s_block.blockSize);//一次读取一块
        bool modify = false;//是否有更改需要写回磁盘
        unsigned char mask = 1;
        mask = mask << 7;
        for (int index = 0; index<s_block.blockSize; index++){
            if (is_end)
                break;//遍历完所有位图后退出
            unsigned char byte = bytes[index];
            for (int j = 0; j < 8; j++){
                if (block_no < s_block.first_data_block_no){
                    block_no++;
                    continue;
                }
                if (!((byte << j)&mask)){
                    //若有空位
                    bytes[index] = byte | (1 << (7 - j));
                    byte = bytes[index];//更新byte
                    list.push_back(block_no);
                    modify = true;
                    if (num == list.size()){
                        is_end = true;//结束
                        break;
                    }
                }
                block_no += 1;
                if (block_no>s_block.block_num){
                    is_end = true;
                    break;
                }
            }
        }
        if (modify){
            fileDisk.seekg(s_block.bitmap_pos + i*s_block.blockSize, ios::beg);
            fileDisk.write((char *)bytes, s_block.blockSize);//位图改变整块写回
        }
    }
    clearBlockContent(list);//清除分配的块中的内容
    //排放block
    int block_per_num = s_block.blockSize / sizeof(unsigned int);//每块可存的block的id的数目
    if (list.size() <= 10){
        //可以在10个直接块中放下
    }
    else if (list.size() <= (11 + block_per_num)){
        //加上一个一次间接块
        int cnt = 0, i;//写入次数和写入下标
        unsigned int once_no = list[10];
        fileDisk.seekg((once_no-1)*s_block.blockSize);//去到正确的偏移位置
        for (i = 11; cnt< block_per_num&&i<list.size(); i++, cnt++){
            fileDisk.write((char*)&list[i], sizeof(unsigned int));
        }
        list.resize(11);
    }
    else if (list.size() <= (12 + 2*block_per_num + block_per_num*block_per_num)){
        //加上一个一次间接块
        int cnt=0,i;//写入次数和写入下标
        unsigned int once_no = list[10];
        unsigned int twice_no = list[11];
        fileDisk.seekg((once_no - 1)*s_block.blockSize);//去到正确的偏移位置
        for (i = 12; cnt <block_per_num ; i++,cnt++){
            fileDisk.write((char*)&list[i], sizeof(unsigned int));
        }
        //加上一个二次间接块
        vector<unsigned int> once_in_twice;//二次间接块中的一次间接块
        fileDisk.seekg((twice_no - 1)*s_block.blockSize);//去到正确的偏移位置
        cnt = 0;
        for (; cnt<block_per_num; i++,cnt++){
            once_in_twice.push_back(list[i]);
            fileDisk.write((char*)&list[i], sizeof(unsigned int));
        }
        //二次间接块的一次间接块
        for (auto item : once_in_twice){
            fileDisk.seekg((item - 1)*s_block.blockSize);//去到正确的偏移位置
            cnt = 0;
            for (; cnt<block_per_num&&i<list.size(); i++, cnt++){
                once_in_twice.push_back(list[i]);
                fileDisk.write((char*)&list[i], sizeof(unsigned int));
            }
        }
        list.resize(12);
    }
    else if (list.size() <= (13 + 3*block_per_num + 2*block_per_num*block_per_num + block_per_num*block_per_num*block_per_num)){
        //加上一个一次间接块
        int cnt = 0, i;//写入次数和写入下标
        unsigned int once_no = list[10];
        unsigned int twice_no = list[11];
        unsigned int third_no = list[12];
        fileDisk.seekg((once_no - 1)*s_block.blockSize);//去到正确的偏移位置
        for (i = 13; cnt <block_per_num; i++, cnt++){
            fileDisk.write((char*)&list[i], sizeof(unsigned int));
        }
        //加上一个二次间接块
        vector<unsigned int> once_in_twice;//二次间接块中的一次间接块
        fileDisk.seekg((twice_no - 1)*s_block.blockSize);//去到正确的偏移位置
        cnt = 0;
        for (; cnt<block_per_num; i++, cnt++){
            once_in_twice.push_back(list[i]);
            fileDisk.write((char*)&list[i], sizeof(unsigned int));
        }
        //二次间接块的一次间接块
        for (auto item : once_in_twice){
            fileDisk.seekg((item - 1)*s_block.blockSize);//去到正确的偏移位置
            cnt = 0;
            for (; cnt<block_per_num; i++, cnt++){
                once_in_twice.push_back(list[i]);
                fileDisk.write((char*)&list[i], sizeof(unsigned int));
            }
        }
        //加上一个三次间接块
        vector<unsigned int> twice_in_third;//三次间接块中的一次间接块
        fileDisk.seekg((third_no - 1)*s_block.blockSize);//去到正确的偏移位置
        cnt = 0;
        for (; cnt<block_per_num; i++, cnt++){
            twice_in_third.push_back(list[i]);
            fileDisk.write((char*)&list[i], sizeof(unsigned int));
        }
        once_in_twice.clear();
        for (auto item : twice_in_third){
            fileDisk.seekg((item - 1)*s_block.blockSize);//去到正确的偏移位置
            cnt = 0;
            for (; cnt<block_per_num; i++, cnt++){
                once_in_twice.push_back(list[i]);
                fileDisk.write((char*)&list[i], sizeof(unsigned int));
            }
        }
        for (auto item : once_in_twice){
            fileDisk.seekg((item - 1)*s_block.blockSize);//去到正确的偏移位置
            cnt = 0;
            for (; cnt<block_per_num&&i<list.size(); i++, cnt++){
                once_in_twice.push_back(list[i]);
                fileDisk.write((char*)&list[i], sizeof(unsigned int));
            }
        }
        list.resize(13);
    }
    return 1;
}

目录搜索

//通过路径vector寻找目录项,返回1表示找到文件夹,返回2表示找到文件,0表示未找到,type为1找文件夹,type为2找文件
int FileSystem::findDentry(vector<string> list,dentry *&p_dentry,char firstChar,int type)
{
    int ret = 0;
    p_dentry = curr_dentry;
    if (firstChar == '/'){
        p_dentry = &root_dentry;//从根目录开始
        list.erase(list.begin());//若第一个字符是/则split后第一位为空
    }
    if (list.size() == 0){
        return 1;//直接当前目录下创建
    }
    for (auto item : list){
        ret = 0;
        if (item == ".."){//父层目录
            p_dentry = p_dentry->parent;
            ret = 1;
        }
        else if (item == "."){//当前目录
            ret = 1;
        }
        else{//遍历寻找目录
            for (auto child_dentry : p_dentry->child_list){
                if (child_dentry->fileName == item){
                    //如果名字对上了,还要判断文件类型
                    if (child_dentry->is_dir()){
                        p_dentry = child_dentry;
                        if (p_dentry->child_list.size() == 0){
                            InitDentry(*p_dentry);//初始化自身及子目录
                        }
                        for (auto item : p_dentry->child_list){
                            InitDentry(*item);
                        }
                        //若不是路径上的最后一个,则必须是文件类型
                        if (type == FOLDER_TYPE || item != list[list.size() - 1]){
                            ret = FOLDER_TYPE;//找的是文件夹
                            break;
                        }
                    }
                    else{
                        p_dentry = child_dentry;
                        //若是路径上的最后一个,则看是不是寻找的文件
                        if (type == FILE_TYPE && item == list[list.size() - 1]){
                            ret = FILE_TYPE;//找的是文件
                            break;
                        }
                    }
                }
            }
        }
        if (ret == 0){
            return ret;
        }
    }
    return ret;
}

小结

此处只说明了比较难缕清思路的几个函数,其他函数没有作过多的说明,项目地址在GitHub,代码中都有注释,在多进程间同步的方法采用的是:
单SimDisk,多Shell的方式,Shell与SimDisk之间通讯首先要加锁,进程间通讯用的是共享内存的方式。
流程大致如下:

至于如何鉴定Shell对应的用户,采用的是登录后保存token,每次执行名利带上token的方式来校验,校验流程如下: