操作系统真象还原:遍历目录

14.12 遍历目录

这是一个网站有所有小节的代码实现,同时也包含了Bochs等文件

14.12.1 打开目录和关闭目录

遍历目录就是读取目录中所有的目录项,在遍历之前必须要先把目录打开,之后还需要把目录关闭。

Linux 中分别用函数 opendir 和 closedir 完成目录打开和关闭,原型分别是:

DIR *opendir(const char *name)int closedir(DIR *dirp)

所以我们先实现sys_opendirsys_closedir

c 复制代码
/*目录打开成功后返回目录指针,失败则返回NULL*/
struct dir* sys_opendir(const char* name){
    ASSERT(strlen(name) < MAX_PATH_LEN);
    /*如果是根目录'/',则直接返回&root_dir*/
    if(name[0]=='/'&&(name[1]==0 || name[0]=='.'))  return &root_dir;

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

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

sys_opendir:接受一个参数 name,功能是打开目录 name,成功后返回目录指针,失败返回 NULL 。 原理:调用search_file将路径转换为inode索引,然后调用dir_open创建目录结构并将对应的inode载入内存。

sys_closedir:接受一个参数dir,功能是关闭dir目录。

14.12.2 读取1个目录项

读取目录实际上就是读取目录中的目录项。

c 复制代码
/*读取目录,成功返回1个目录项,失败返回NULL*/
struct dir_entry* dir_read(struct dir* dir){
    struct dir_entry* dir_e = (struct dir_entry*)dir->dirr_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_entrs_per_sec = SECTOR_SIZE / dir_entry_size;  //1扇区可容纳的目录项个数

    /*在目录大小内遍历*/
    while(dir->dir_pos < dir_inode->i_size){
        if(dir->dir_pos >= dir_inode->i_size)
            return NULL;
        
        if(all_blocks[block_idx]==0){   //如果此块地址为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_entrs_per_sec){
            if((dir_e+dir_entry_idx)->f_type){  //如果f_type不等于0,即不等于FT_UNKNOWN
                /*判断是不是最新的目录项,避免返回曾经已经返回过的目录项*/
                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;
}

dir_read:接受 1 个参数,目录指针曲,功能是读取目录dir,成功返回 1 个目录项,失败返回 NULL。 比如该目录文件下目录项分布:a,空,b,空,c,第一次调用返回a,第二次调用返回b,第三次调用返回c...原理:目录内有个指向自己inode的指针,该inode内有i_sectors[ ],所以能在磁盘中找到该目录文件,将其读出到缓冲区然后按需设定遍历规则即可。

代码中dir_pos实际含义是已经返回过的目录项总大小,cur_dir_entry_pos的含义是此次调用遍历过程中,扫描过的非空目录项总大小。当扫描到的非空目录项时,此时若cur_dir_entry_posdir_pos相等,自然就可以判断出这个非空目录项应该返回。dir_pos 在执行 sys_opendir 时就被置为0了。

14.12.3 实现sys_readdir及sys_rewinddir

Linux中读取目录的函数是readdir,其原型是:"struct dirent *readdir(DIR *dirp)"

遍历目录的操作中,经常会用到目录回绕的功能,也就是使目录的"游标" dir_pos 回到 0,它与 lseek 功能类似,只不过是针对目录的,避免了将目录先关闭再重新打开的繁琐。在 Linux 中目录回绕是用函数 rewinddir实现的,其原型是:"void rewinddir(DIR *dirp)"我们也按照此形式实现。

c 复制代码
/*读取目录dir的第一个目录项,成功后返回其目录项地址,到目录尾时或出错时返回NULL*/
struct dir_entry* sys_readdir(struct dir* dir){
    ASSERT(dir!=NULL);
    return dir_read(dir);
}

/*把目录dir的指针dir_pos置0*/
void sys_rewinddir(struct dir* dir){
    dir->dir_pos = 0;
}

sys_readdir,就是dir_read的封装

sys_rewinddir将目录的dir_pos置为0

相关推荐
肖老师+12 小时前
“从零到一:揭秘操作系统的奇妙世界”【操作系统中断和异常】
操作系统
GoGeekBaird2 天前
69天探索操作系统-第20天:页替换算法 - 深度剖析
后端·操作系统
IT 青年3 天前
操作系统(12)内存分配
操作系统
IT 青年4 天前
操作系统(15)I/O硬件
操作系统
killsime5 天前
操作系统笔记
笔记·操作系统
IT 青年6 天前
操作系统(11)程序处理
操作系统
千千寰宇6 天前
[操作系统] 计算机资源虚拟化技术
操作系统·docker/k8s/虚拟化/容器化
极客代码6 天前
深入C语言文件操作:从库函数到系统调用
linux·c语言·文件操作·系统调用·库函数
終不似少年遊*6 天前
华为云(openstack)常用命令行
linux·服务器·网络·华为云·云计算·操作系统·openstack
helloWorldZMY7 天前
操作系统内存管理
操作系统·内存管理·计算机基础