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

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

相关推荐
Freestyle Coding7 小时前
使用rust自制操作系统内核
c语言·汇编·microsoft·rust·操作系统
打鱼又晒网11 小时前
Linux进程间通信——探索共享内存—— 剖析原理, 学习接口应用
linux·运维·服务器·后端·操作系统
skaiuijing2 天前
巧用二级指针
c语言·开发语言·算法·架构·操作系统
打鱼又晒网3 天前
linux进程间通信——学习与应用命名管道, 日志程序的使用与实现
linux·运维·服务器·后端·操作系统
OpenAnolis小助手4 天前
专访AMD:AMD 正式加入龙蜥社区首秀:开源协作与 AI 创新的交汇点
ai·开源·操作系统·龙蜥社区·龙蜥操作系统大会
zhangxueyi4 天前
word文档转换为PPT文档最佳方案
操作系统·文档转换
洛寒瑜4 天前
【读书笔记-《30天自制操作系统》-18】Day19
c语言·开发语言·汇编·笔记·学习·操作系统·文件读取
IronmanJay5 天前
kAFL部署、使用与原理分析
信息安全·内核·系统调用·fuzz测试·漏洞检测·内核漏洞检测工具·kafl
maosql6 天前
arm和riscv系统调用对比(笔记)
c语言·arm开发·笔记·系统调用·上下文切换
望获linux6 天前
Linux网络协议栈的实现
linux·服务器·arm开发·网络协议·操作系统·嵌入式操作系统