Bootstrap

操作系统真象还原:文件的打开与关闭代码实现

14.6 文件的打开与关闭

14.6.1 文件的打开
//fs.c
/*打开编号为inode_no的inode对应的文件,若成功则返回文件描述符,否则返回-1*/
int32_t file_open(uint32_t inode_no, uint8_t flag){
    int fd_idx = get_free_slot_in_global();
    if(fd_idx == -1){
        printk("exceed max open files\n");
        return -1;
    }
    file_table[fd_idx].fd_inode = inode_open(cur_part,inode_no);
    file_table[fd_idx].fd_pos = 0;  //每次打开文件,要将fd_pos还原为0,即让文件内的指针指向开头
    file_table[fd_idx].fd_flag = flag;
    bool* write_deny = &file_table[fd_idx].fd_inode->write_deny;
    
    if(flag & O_WRONLY || flag & O_RDWR){
        //只要是关于写文件,判断是否有其他进程正在写此文件,要保持互斥
        //若是读文件,不需要考虑write_deny;
        /*以下进入临界区前先关闭中断*/
        enum intr_status old_status = intr_disable();
        if(!(*write_deny)){ //若当前没有其他进程写该文件,将其占用
            *write_deny = true; //置位true,避免多个进程同时写这个文件
            intr_set_status(old_status);
        }else{  //直接返回失败
            intr_set_status(old_status);
            printk("file can`t be write now, try again later\n");
            return -1;
        }
    }   //若是读文件或创建文件,不用例会write_deny,保持默认
    return pcb_fd_install(fd_idx);
}

file_open:函数功能是打开编号为inode_no的inode对应的文件,若成功则返回文件描述符,否则返回-1。流程:1.调用get_free_slot_in_global先是获取一个空的fd描述符;2.再打开一个inode_no并把它填充到file_table;3.关于写文件,判断是否有其他进程正在写此文件,我们要保持互斥,读文件则不需要考虑;4.在创建文件进程或线程的pcb中安装文件描述符。

同时修改上面之前是sys_open这样我们的sys_open函数就可以处理打开文件的命令了。

    switch (flags & O_CREAT)
    {
    case O_CREAT:
        printk("creating file\n");
        fd = file_create(searched_record.parent_dir, (strrchr(pathname, '/') + 1), flags);
        dir_close(searched_record.parent_dir);
        break;
    default:
        /* 其余情况均为打开已存在文件:
         * O_RDONLY,O_WRONLY,O_RDWR */
        fd = file_open(inode_no, flags);
    }

    /* 此fd是指任务pcb->fd_table数组中的元素下标,
     * 并不是指全局file_table中的下标 */
    return fd;

14.6.2 文件的关闭
//fs.c
/*关闭文件*/
int32_t file_close(struct file* file){
    if(file==NULL){
        return -1;
    }

    file->fd_inode->write_deny=false;
    inode_close(file->fd_inode);
    file->fd_inode=NULL;    //使得文件可用
    return 0;
}

......

/*将文件描述符转化为文件表的下标*/
static uint32_t fd_local2global(uint32_t local_fd){
    struct task_struct* cur = running_thread();
    int32_t global_fd = cur->fd_table[local_fd];
    ASSERT(global_fd>=0&&global_fd<MAX_FILE_OPEN);
    return (uint32_t)global_fd;
}

/*关闭文件描述符fd指向的文件,成功返回0,失败返回-1*/
int32_t sys_close(int32_t fd){
    int32_t ret = -1;   //返回值默认为-1,即失败
    if(fd>2){
        uint32_t _fd = fd_local2global(fd);
        ret = file_close(&file_table[_fd]);
        running_thread()->fd_table[fd] = -1;//使得文件描述符位可用
    }
    return ret;
}

file_close:将文件正在写标志关闭,调用inode_close移除内存中的inode,并解除文件结构与inode的关系。

fd_local2global:功能是将文件描述符转化为文件表的下标。这里说明一下:file_table[i]中存储的是file结构体里面存储着inode,而进程的fd_table[j]中存储的就是file_table[]中文件标记的i,这样就可以顺利的摸到inode

;