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

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

相关推荐
atomicmaker10 小时前
经典同步问题
操作系统·pv·同步问题
iCxhust11 小时前
Proteus例程导入方法
操作系统·proteus·课程设计·微机原理·8086最小系统·8088单板机
空荡forevere12 小时前
Linux文件系统(三)
linux·运维·系统架构·操作系统
atomicmaker20 小时前
处理器管理
操作系统·进程·同步与互斥·cpu调度·处理器管理
kunge20132 天前
1. Tmux 使用指南(入门篇)
后端·架构·操作系统
iCxhust2 天前
AD0808调试笔记
笔记·单片机·嵌入式硬件·操作系统·微机原理·8088单板机
fakerth5 天前
【OpenHarmony】startup_init 模块
操作系统·openharmony
大熊猫侯佩6 天前
升级到 macOS26.5 后看视频会自动息屏的解决
macos·操作系统
j7~6 天前
【Linux操作系统】基础IO文件系统(理解硬件,理解文件系统,Inode,软硬链接)
linux·运维·服务器·磁盘·文件系统·inode·软硬件链接
sulikey7 天前
个人Linux操作系统学习笔记2 - gcc与库的理解
linux·笔记·学习·操作系统·gcc·