操作系统真象还原:更改任务的工作目录

14.14任务的工作目录

​ 在 Linux 中咱们经常会使用命令 pwd 来显示当前工作目录,还要用 cd 命令来改变工作目录,咱们要实现类似的功能。

14.14.1 显示当前工作目录的原理及基础代码

Linux中使用pwd来显示当前工作路径。任何目录中都有目录项“…”,它表示父目录。也就是说,有了“…”,无论我们身处任何一级的子目录,都可以“顺藤摸瓜”找到根目录。因此咱们具体的做法是先通过“…”获取当前目录的父目录,在父目录中搜索当前目录的目录项,从目录项中获取当前目录名称,然后再向上找父目录的父目录,再从中获得父目录的名称……沿着目录树层层而上,就能构建出当前目录的绝对路径。

/*获得父目录的inode编号*/
static uint32_t get_parent_dir_inode_nr(uint32_t child_inode_nr, void* io_buf){
    struct inode* child_dir_inode = inode_open(cur_part,child_inode_nr);
    /*目录中的目录项"..",中包含了父目录inode编号,".."位于目录的第0块*/
    uint32_t block_lba = child_dir_inode->i_sectors[0];
    ASSERT(block_lba>=cur_part->sb->data_start_lba);
    inode_close(child_dir_inode);
    ide_read(cur_part->my_disk,block_lba,io_buf,1);
    struct dir_entry* dir_e = (struct dir_entry*)io_buf;
    /*第0个目录项是".",第1个目录项是".."*/
    ASSERT(dir_e[1].i_no < 4096 && dir_e[1].f_type == FT_DIRECTORY);
    return dir_e[1].i_no;   //返回..即父目录的inode编号
}

/*在inode编号为p_inode_nr的目录中查找inode编号为c_inode_nr的子目录的名字,将名字存入缓冲区path,成功返回0,失败返回-1*/
static int get_child_dir_name(uint32_t p_inode_nr, uint32_t c_inode_nr, char* path, void* io_buf){
    struct inode* parent_dir_inode = inode_open(cur_part,p_inode_nr);
    /*填充all_blocks,将该目录的所占扇区地址全部写入all_blocks*/
    uint8_t block_idx = 0;
    uint32_t all_blocks[140] = {0}, block_cnt = 12;
    while(block_idx < 12){
        all_blocks[block_idx] = parent_dir_inode->i_sectors[block_idx];
        block_idx++:
    }
    if(parent_dir_inode->i_sectors[12]){    //若包含了一级间接块表,将其读入 all_blocks
        ide_read(cur_part->my_disk,parent_dir_inode->i_sectors[12],all_blocks+12,1);
        block_cnt = 140;
    }
    inode_close(parent_dir_inode);
    
    struct dir_entry* dir_e = (struct dir_entry*)io_buf;
    uint32_t dir_entry_size = cur_part->sb->dir_entry_size;
    uint32_t dir_entrys_per_sec = (512 / dir_entry_size);
    block_idx=0;

    /*遍历所有块*/
    while(block_idx<block_cnt){
        if(all_blocks[block_idx]){  //如果相应块不为空,则读入相应块
            ide_read(cur_part->my_disk,all_blocks[block_idx],io_buf,1);
            uint8_t dir_e_idx = 0;
            /*遍历每个目录项*/
            while(dir_e_idx < dir_entrys_per_sec){
                if((dir_e+dir_e_idx)->i_no == c_inode_nr){
                    strcat(path,"/");
                    strcat(path,(dir_e+dir_e_idx)->filename);
                    return 0;
                }
                dir_e_idx++;
            }
        }
        block_idx++;
    }
    return -1;
}

get_parent_dir_inode_nr:接受 2 个参数,子目录 inode 编号 child_inode_nr、缓冲区 io_buf,功能是获得父目录的 inode 编号。

get_child_dir_name:接受 4 个参数,父目录 inode 编号 p_inode_nr、子目录 inode 编号 c_inode_nr、存储路径的缓冲区 path、硬盘读写缓冲区 io_buf,功能是在 inode 编号p_inode_nr的目录中查找 inode编号为 c_inode_nr 的子目录,将子目录的名字存入缓冲区 path,成功返回 0,失败返-1。

14.14.2 实现sys_getcwd

Linux 中用 getcwd 函数来获取当前工作路径,其原型是:

char *getcwd(char *buf, size_t size)

buf 是容纳当前目录绝对路径的缓冲区, getcwd 会将当前工作目录的绝对路径写入 buf 中, sizebuf 的大小。 buf 可以由用户提供,也可以由操作系统提供,如果用户不提供 buf,即传给 buf 的参数为 NULL,系统会通过 malloc 单独分配内存给 buf之后 getcwd 再将 buf返回,用户进程记得要用free 释放。本节咱们要实现其内核部分-sys_getcwd.

/*把当前工作目录绝对路径写入buf,size是buf的大小,当buf为NULL时,由操作系统分配存储工作路径的空间并返回地址,失败返回NULL*/
char* sys_getcwd(char* buf, uint32_t size){
    /*确保buf不为空,若用户进程提供的buf为NULL,系统调用getcwd中要为用户进程通过malloc分配内存*/
    ASSERT(buf!=NULL);
    void* io_buf = sys_malloc(SECTOR_SIZE);
    if(io_buf==NULL){
        return NULL;
    }

    struct task_struct* cur_thread = running_thread();
    int32_t parent_inode_nr = 0;
    int32_t child_inode_nr = cur_thread.cwd_inode_nr;	//这个cwd_inode_nr是用来记录工作目录的inode编号。
    ASSERT(child_inode_nr >= 0 && child_inode_nr < 4096);   //最大支持4096个inode
    /*若当前目录是根目录,直接返回'\'*/
    if(child_inode_nr == 0){
        buf[0]='/';
        buf[1]= 0;
        return buf;
    }
    memset(buf,0,size);
    char full_path_reverse[MAX_PATH_LEN] = {0}; //用来做全路径缓冲区

    /**
     * 从下往上逐层找父目录,直到找到根目录为止。
     * 当child_inode_nr为根目录的inode编号(0)时停止
     * 即已经查看完根目录中的目录项
    */
    while((child_inode_nr)){
        parent_inode_nr = get_parent_dir_inode_nr(child_inode_nr,io_buf);
        if(get_child_dir_name(parent_inode_nr,child_inode_nr,full_path_reverse,io_buf)==-1){    //或未找到名字,失败退出
            sys_free(io_buf);
            return NULL;
        }
        child_inode_nr = parent_inode_nr;
    }
    ASSERT(strlen(full_path_reverse)<=size);
    /**
     * 至此full_path_reverse中的路径是反着的
     * 即子目录在前,父目录在后,
     * 现在将full_path_reverse中的路径反置
    */
    char* last_slash;   //用于记录字符串中最后一个斜杠地址
    while((last_slash == strrchr(full_path_reverse,'/'))){
        uint16_t len = strlen(buf);
        strcpy(buf+len, last_slash);
        /*full_path_reverse中添加结束字符,作为下一次执行strcpy中last_slash的边界*/
        *last_slash = 0;
    }
    sys_free(io_buf);
    return buf;
}

sys_getcwd:把当前工作目录绝对路径写入buf,size是buf的大小,当buf为NULL时,由操作系统分配存储工作路径的空间并返回地址,失败返回NULL。

原理:我们之前已经为PCB中添加了cwd_inode_nr用于表示任务工作目录inode索引,假设现在这个inode索引是正确的。首先调用get_parent_dir_inode_nr得到父目录inode索引,此时原来的cwd_inode_nr就变成了子目录inode索引,然后就可以调用get_child_dir_name得到子目录名称,然后将父目录inode索引转换成新的子目录索引,又调用get_parent_dir_inode_nr得到新的父目录索引…如此循环,缓冲区中就会存储反转的绝对路径。比如一个进程工作在/home/kanshan/test下,缓冲区就会存入/test/kanshan/home,所以我们最后把这个路径反转过来即可。

14.14.3 实现sys_chdir改变工作目录

Linux中用函数chdir来改变当前工作目录,其原型是:int chdir(const char *path);

/*更改当前工作目录为绝对路径path,成功则返回0,失败返回-1*/
int32_t sys_chdir(const char* path){
    int32_t ret=-1;
    struct path_search_record searched_record;
    memset(&searched_record,0,sizeof(struct path_search_record));
    int inode_no = search_file(path,&searched_record);
    if(inode_no!=-1){
        if(searched_record.file_type == FT_DIRECTORY){
            running_thread()->cwd_inode_nr = inode_no;
            ret = 0;
        }else{
            printk("sys_chdir:%s is regular file or other!\n", path);
        }
    }
    dir_close(searched_record.parent_dir);
    return ret;
}

sys_chdir:更改当前工作目录为绝对路径path,成功则返回0,失败返回-1。任务的工作目录记录在 pcb 中的 cwd_inode_nr,因此更改工作目录的核心原理就是修改 cwd_inode_nr

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值