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

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

相关推荐
Serene_Dream11 小时前
OS 内存小结
操作系统·内存
程序员一点16 小时前
第3章:首次启动与基础配置
操作系统·openeuler
冰冷的希望21 小时前
【系统】VMware17虚拟机安装黑苹果macOS 15.0详细步骤(保姆级)
macos·操作系统·系统·vmware·虚拟机·黑苹果
请输入蚊子2 天前
«操作系统真像还原» 第二章 编写MBR主引导记录
linux·汇编·操作系统·bochs·操作系统真像还原
添砖java‘’3 天前
线程的互斥与同步
linux·c++·操作系统·线程·信息与通信
未来可期LJ3 天前
【Linux 操作系统篇】文件系统 innode的理解
linux·文件系统
燃于AC之乐3 天前
【Linux系统编程】进程控制完全指南:从fork创建、优雅终止到进程等待的全面解析
linux·操作系统·进程控制·进程创建·进程等待·进程终止·fork函数
Trouvaille ~4 天前
【Linux】Linux线程概念与控制(四):glibc源码剖析与实现原理
linux·运维·服务器·c++·操作系统·glibc·线程控制
_OP_CHEN4 天前
【Linux系统编程】(二十四)深入 Ext2 块组内部:inode、数据块与目录的底层工作机制
linux·操作系统·文件系统·c/c++·inode·块组·数据块映射
番茄灭世神4 天前
Linux从入门到进阶第一章
linux·计算机·操作系统