操作系统真象还原[14章/十一]-打开/关闭目录以及遍历目录

         一、打开/关闭目录

        打开目录其实就是通过dir_open函数打开指定目录的inode,打开inode之前已经说过了就是把硬盘中的inode数组中的inode结构从硬盘中读入到内存中即可。

        关闭目录其实就是通过dir_close函数关闭指定目录的inode,关闭inode也提到过就是把内存中的inode结构占用的内存通过sys_free函数释放掉。

        这里再把inode_open、inode_close、dir_open、dir_close以及dir、inode的结构再贴一下强化记忆

struct dir {
    struct inode* inode;
    uint32_t dir_pos;
    uint8_t dir_buf[512];
};

struct inode {
    uint32_t i_no;                  //inode编号
    uint32_t i_size;                //当inode是文件时,i_size指文件大小;当inode是目录时,i_size指该目录下所有目录项大小之和
    uint32_t i_open_cnts;           //记录次文件被打开的次数
    bool write_deny;                //写文件不能并行,进程写文件前检查次标识

    uint32_t i_sectors[13];         //0-11是直接块,12用来存储一级间接块指针
    struct list_elem inode_tag;     //用于加入已打开的inode队列
};

struct inode* inode_open(struct partition* part, uint32_t inode_no) {

    /* 在已打开的inode链表中找是否存在编号为inode_no的inode */
    struct list_elem* elem = part->open_inodes.head.next;
    struct inode* inode_found;
    while(elem != &part->open_inodes.tail) {
        inode_found = elem2entry(struct inode, inode_tag, elem);
        if(inode_found->i_no == inode_no) {
            inode_found->i_open_cnts++;
            return inode_found;
        }
        elem = elem->next;
    }

    /* 在内核空间创建inode,因为该inode需要被所有任务共享 */
    struct task_struct* cur = running_thread();
    uint32_t* cur_pagedir_bak = cur->pgdir;
    cur->pgdir = NULL;
    inode_found = (struct inode*) sys_malloc(sizeof(struct inode));
    cur->pgdir = cur_pagedir_bak;

    /* 定位inode */
    struct inode_position inode_pos;
    inode_locate(part, inode_no, &inode_pos);

    /* 从硬盘中读出inode并拷贝到内核空间的inode中 */
    char* inode_buf;
    if(inode_pos.two_sec) {
        inode_buf = (char*) sys_malloc(SECTOR_SIZE * 2);
        ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 2);
    } else {
        inode_buf = (char*) sys_malloc(SECTOR_SIZE);
        ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 1);
    }
    memcpy(inode_found, inode_buf + inode_pos.off_size, sizeof(struct inode));

    sys_free(inode_buf);

    /* 将内核空间的inode加入到已打开的inode队列中 */
    list_push(&part->open_inodes, &inode_found->inode_tag);
    inode_found->i_open_cnts = 1;

    return inode_found;
}

void inode_close(struct inode* inode) {
    enum intr_status old_status = intr_disable();
    /* 将inode的i_open_cnts属性-1判断是否无任务引用该inode */
    if(--inode->i_open_cnts == 0) {
        /* 将inode移除出分区的已打开inode节点队列 */
        list_remove(&inode->inode_tag);
        /* 在内核内存池中释放inode */
        struct task_struct* cur = running_thread();
        uint32_t* cur_pagedir_bak = cur->pgdir;
        cur->pgdir = NULL;
        sys_free(inode);
        cur->pgdir = cur_pagedir_bak;
    }
    intr_set_status(old_status);
}

/* 打开目录同打开根目录一致 */
struct dir* dir_open(struct partition* part, uint32_t inode_no) {
    struct dir* pdir = (struct dir*) sys_malloc(sizeof(struct dir));
    pdir->inode = inode_open(part, inode_no);
    pdir->dir_pos = 0;
    return pdir;
}

/* 关闭目录,实际上就是关闭目录代表的inode并释放目录结构在内存中所占的空间 */
void dir_close(struct dir* dir) {
    if(dir == &root_dir) {
        return;
    }
    inode_close(dir->inode);
    sys_free(dir);
}

        下面给出sys_opendir以及sys_closedir的代码:

/* 目录打开成功返回目录指针,失败返回NULL */
struct dir* sys_opendir(char* name) {
    /* 判断是否是根目录 */
    if(name[0] == '/' && (name[1] == 0 || name[1] == '.')) {
        return &root_dir;
    }

    /* 检查待打开的目录是否存在 */
    struct path_search_record searched_record;
    memset(&searched_record, 0, sizeof(struct path_search_record));
    int inode_no = search_file(name, &searched_record);
    struct dir* ret = NULL;
    if(inode_no == -1) {
        printk("In %s, sub path %s not exist\n", name, searched_record.searched_path);
    } else {
        if(searched_record.file_type == FT_REGULAR) {
            printk("%s is regular file!\n", name);
        } else {
            ret = dir_open(cur_part, inode_no);
        }
    }
    dir_close(searched_record.parent_dir);
    return ret;
}

/* 成功关闭目录dir返回0,失败返回-1 */
int32_t sys_closedir(struct dir* dir) {
    int32_t ret = -1;
    if(dir != NULL) {
        dir_close(dir);
        ret = 0;
    }
    return ret;
}

        实验结果

#include "print.h"
#include "init.h"
#include "debug.h"
#include "memory.h"
#include "thread.h"
#include "interrupt.h"
#include "console.h"
#include "process.h"
#include "syscall.h"
#include "syscall-init.h"
#include "stdio.h"
#include "fs.h"

int main(void)
{
    put_str("I am kernel\n");
    init_all();
    intr_enable();
    struct dir* p_dir = sys_opendir("/dir1/subdir1");
    if(p_dir) {
        printf("/dir1/subdir1 open done!\n");
        if(sys_closedir(p_dir) == 0) {
            printf("/dir1/subdir1 close done!\n");
        } else {
            printf("/dir1/subdir1 close fail!\n");
        }
    } else {
        printf("/dir1/subdir1 open fail!\n");
    }

    while(1);
    return 0;
}

         二、遍历目录

        遍历目录dir_read函数主要流程梳理:

        (1)、将目录dir的所有磁盘数据块lba地址读入all_blocks中

        (2)、逐个数据块读入到内存进行遍历

                (2.1)、数据块中的逐个目录项进行遍历直到找到下一个要返回的目录项(dir->dir_pos代表已读入的目录项字节大小,下一个要返回的目录项位于距离数据块开始后的dir->dir_pos字节处)

        这里再贴一下dir_entry的数据结构强化记忆,上代码(遍历目录的代码位于fs/dir.c中,包装函数sys_readdir位于fs/fs.c中)

/* 目录项结构 */
struct dir_entry {
    char filename[MAX_FILE_NAME_LEN];       //普通文件或目录文件名称
    uint32_t i_no;                          //普通文件或目录对应的inode编号
    enum file_types f_type;                 //文件类型
};
struct dir_entry* dir_read(struct dir* dir) {
    struct dir_entry* dir_e = (struct dir_entry*) dir->dir_buf;
    struct inode* dir_inode = dir->inode;
    uint32_t all_blocks[140] = {0}, block_cnt = 12;
    uint32_t block_idx = 0, dir_entry_idx = 0;
    while (block_idx < 12) {
        all_blocks[block_idx] = dir_inode->i_sectors[block_idx];
        block_idx++;

    }

    if(dir_inode->i_sectors[12] != 0) {
        ide_read(cur_part->my_disk, dir_inode->i_sectors[12], all_blocks + 12, 1);
        block_cnt = 140;
    }
    block_idx = 0;

    uint32_t cur_dir_entry_pos = 0;
    uint32_t dir_entry_size = cur_part->sb->dir_entry_size;
    uint32_t dir_entrys_per_sec = SECTOR_SIZE / dir_entry_size;
    while (dir->dir_pos < dir_inode->i_size) {
        if(dir->dir_pos >= dir_inode->i_size) {
            return NULL;
        }
        if(all_blocks[block_idx] == 0) {
            block_idx++;
            continue;
        }
        memset(dir_e, 0, SECTOR_SIZE);
        ide_read(cur_part->my_disk, all_blocks[block_idx], dir_e, 1);
        dir_entry_idx = 0;
        while(dir_entry_idx < dir_entrys_per_sec) {
            if((dir_e + dir_entry_idx)->f_type) {
                if(cur_dir_entry_pos < dir->dir_pos) {
                    cur_dir_entry_pos += dir_entry_size;
                    dir_entry_idx++;
                    continue;
                }
                ASSERT(cur_dir_entry_pos == dir->dir_pos);
		   dir->dir_pos += dir_entry_size;
                return dir_e + dir_entry_idx;
            }
            dir_entry_idx++;
        }
        block_idx++;
    }

    return NULL;
}
void sys_rewinddir(struct dir* dir) {
    ASSERT(dir != NULL);
    dir->dir_pos = 0;
}

struct dir_entry* sys_readdir(struct dir* dir) {
    ASSERT(dir != NULL);
    return dir_read(dir);
}

        sys_rewinddir就是把目录已读字节数置0,太简单了就不单梳理流程了

        实验结果

#include "print.h"
#include "init.h"
#include "debug.h"
#include "memory.h"
#include "thread.h"
#include "interrupt.h"
#include "console.h"
#include "process.h"
#include "syscall.h"
#include "syscall-init.h"
#include "stdio.h"
#include "fs.h"
#include "dir.h"
#include "inode.h"

int main(void)
{
    put_str("I am kernel\n");
    init_all();
    intr_enable();

struct dir* p_dir = sys_opendir("/dir1/subdir1");
    if(p_dir) {
        printf("/dir1/subdir1 open done\ncontent:\n");
        char* type = NULL;
        struct dir_entry* dir_e = NULL;
        while ((dir_e = sys_readdir(p_dir))) {
            if(dir_e->f_type == FT_REGULAR) {
                type = "regular";
            } else {
                type = "directory";
            }
            printf("    %s  %s\n", type, dir_e->filename);
        }
        if(sys_closedir(p_dir) == 0) {
            printf("/dir1/subdir1 close done!\n");
        } else {
            printf("/dir1/subdir1 close fail!\n");
        }
    } else {
        printf("/dir1/subdir1 open fail!\n");
    }

    while(1);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值