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

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

相关推荐
獭.獭.3 分钟前
Linux -- 文件【下】
linux·服务器·文件系统·软硬链接·inode·ext2
OpenAnolis小助手20 小时前
朗空量子与 Anolis OS 完成适配,龙蜥获得抗量子安全能力
安全·开源·操作系统·龙蜥社区·龙蜥生态
墨夏2 天前
跨平台开发下的策略模式
设计模式·操作系统
fakerth2 天前
OpenHarmony介绍
操作系统·openharmony
程序员老刘4 天前
操作系统“卡脖子”到底是个啥?
android·开源·操作系统
有信仰4 天前
操作系统——虚拟内存和物理内存
操作系统
望获linux9 天前
【实时Linux实战系列】实时数据流处理框架分析
linux·运维·前端·数据库·chrome·操作系统·wpf
unfetteredman9 天前
Mac查看端口使用信息
操作系统·mac
闪电麦坤9510 天前
操作系统:RPC 中可能遇到的问题(Issues in RPC)
rpc·操作系统
闪电麦坤9510 天前
操作系统:远程过程调用( Remote Procedure Call,RPC)
rpc·操作系统