这节的内容也不多,先梳理一下14.14这节整体的思路我们再写代码。这里所说的任务的工作目录以及改变任务的工作目录的主体是任务,我们也是从这里作为起点开始捋一下思路。
(1)任务就是指线程,每个线程都默认有个初始的工作目录,我们这里设置为根目录。因为线程相关的内容都存放到pcb中,所以我们给pcb增加了字段uint32_t cwd_inode_nr,来记录当前工作目录的inode号
(2)pcb中的cwd_inode_nr需要被赋予初始值0(即根目录的inode编号),这一步是在init_thread函数中实现的,我自己把这个函数改名了叫init_thread_pcb
(3)现在有了工作目录的inode号如何以字符串的形式展示当前目录的绝对路径呢?我们知道文件名或目录名都是存在于目录项中的,所以我们需要先获取到父目录的inode号,然后再在父目录的目录项中寻找对应当前目录inode号的目录名,然后逐层向上获取最终得到完整的绝对路径。这里就需要我们实现两个函数:1、根据子目录的inode号获取父目录的inode号 2、根据父目录和子目录的inode号,在父目录的硬盘数据块中查询子目录目录项中对应的名称。
沿着上述的思路,我们就先进行pcb的修改(therad.h以及thread.c),然后再实现两个基础函数,最后实现sys_getcwd
/* 进程或线程的pcb,程序控制块 */
struct task_struct {
uint32_t* self_kstack;
pid_t pid;
enum task_status status;
uint32_t priority;
char name[16];
uint32_t ticks;
uint32_t elapsed_ticks;
struct list_elem thread_ready_list_tag;
struct list_elem thread_all_list_tag;
/* 用户进程 */
uint32_t* pgdir;
struct virtual_addr userprog_vaddr;
struct mem_block_desc u_block_descs[DESC_CNT];
/* 文件系统 */
int32_t fd_table[MAX_FILES_OPEN_PER_PROC];
uint32_t cwd_inode_nr;
uint32_t stack_magic;
};
/* 初始化线程基本信息 */
void init_thread_pcb(struct task_struct* pthread, char* thread_name, uint8_t priority) {
pthread->self_kstack = (uint32_t*) ((uint32_t) pthread + PG_SIZE);
pthread->pid = allocate_pid();
pthread->status = TASK_READY;
pthread->priority = priority;
strcpy(pthread->name, thread_name);
pthread->stack_magic = (uint32_t) 0x19970814;
pthread->ticks = priority;
pthread->elapsed_ticks = 0;
pthread->pgdir = NULL;
/* 预留标准输入输出 */
pthread->fd_table[0] = 0;
pthread->fd_table[1] = 1;
pthread->fd_table[2] = 2;
uint8_t fd_idx = 3;
while(fd_idx < MAX_FILES_OPEN_PER_PROC) {
pthread->fd_table[fd_idx] = -1;
fd_idx++;
}
pthread->cwd_inode_nr = 0;
}
/* 获取父目录的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);
uint32_t block_lba = child_dir_inode->i_sectors[0];
ASSERT(block_lba != 0);
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;
ASSERT(dir_e[1].i_no < 4096 && dir_e[1].f_type == FT_DIRECTORY);
return dir_e[1].i_no;
}
/* 在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) {
/* 打开p_inode,取出所有数据块存入all_blocks中 */
struct inode* parent_dir_inode = inode_open(cur_part, p_inode_nr);
uint32_t* all_blocks = (uint32_t*) sys_malloc(48 + SECTOR_SIZE);
uint32_t block_idx = 0;
while(block_idx < 12) {
all_blocks[block_idx] = parent_dir_inode->i_sectors[block_idx];
block_idx++;
}
if(parent_dir_inode->i_sectors[12] != 0) {
ide_read(cur_part->my_disk, parent_dir_inode->i_sectors[12], all_blocks + 12, 1);
}
inode_close(parent_dir_inode);
/* 遍历每个数据块,查找编号为c_inode_nr的目录项 */
block_idx = 0;
uint32_t dir_entry_size = cur_part->sb->dir_entry_size;
uint32_t max_dir_entries_per_sec = SECTOR_SIZE / dir_entry_size;
struct dir_entry* dir_e = (struct dir_entry*) io_buf;
while (block_idx < 140) {
if(all_blocks[block_idx] == 0) {
block_idx++;
continue;
}
ide_read(cur_part->my_disk, all_blocks[block_idx], io_buf, 1);
uint32_t dir_entry_idx = 0;
while (dir_entry_idx < max_dir_entries_per_sec) {
if(((dir_e + dir_entry_idx)->f_type == FT_DIRECTORY) && ((dir_e + dir_entry_idx)->i_no == c_inode_nr)) {
strcat(path, "/");
strcat(path, (dir_e + dir_entry_idx)->filename);
sys_free(all_blocks);
return 0;
}
dir_entry_idx++;
}
block_idx++;
}
sys_free(all_blocks);
return -1;
}
上面两个函数的实现都没太多难度,就不梳理流程了。后面的sys_getcwd函数也不太难就大概说一下,其实就分两步:
(1)拿到当前线程的cwd_inode_nr后通过get_parent_dir_inode_nr函数以及get_child_dir_name函数拼接绝对路径,不过这一步拼接出来是反的
(2)将路径反转并返回即可
char* sys_getcwd(char* buf, uint32_t size) {
ASSERT(buf != NULL);
void* io_buf = sys_malloc(SECTOR_SIZE);
if(io_buf == NULL) {
return NULL;
}
/* 拼接目录 */
struct task_struct* cur_thread = running_thread();
uint32_t parent_dir_inode_nr = 0;
uint32_t child_dir_inode_nr = cur_thread->cwd_inode_nr;
ASSERT(child_dir_inode_nr >= 0 && child_dir_inode_nr < 4096);
if(child_dir_inode_nr == 0) {
buf[0] = '/';
buf[1] = 0;
sys_free(io_buf);
return buf;
}
memset(buf, 0, size);
char full_path_reverse[MAX_PATH_LEN] = {0};
while (child_dir_inode_nr) {
parent_dir_inode_nr = get_parent_dir_inode_nr(child_dir_inode_nr, io_buf);
if(get_child_dir_name(parent_dir_inode_nr, child_dir_inode_nr, full_path_reverse, io_buf) == -1) {
sys_free(io_buf);
return NULL;
}
child_dir_inode_nr = parent_dir_inode_nr;
}
ASSERT(strlen(full_path_reverse) <= size);
/* 目录反转 */
char* last_slash;
while ((last_slash = strrchr(full_path_reverse, '/'))) {
strcpy(buf + strlen(buf), last_slash);
*last_slash = 0;
}
sys_free(io_buf);
return buf;
}
接下来说sys_chdir改变工作目录的函数,该函数的形式如下:
int32_t sys_chdir(const char* path)
起始这个函数也很简单,就是检查一下path路径存不存在,如果存在path路径对应的是不是目录是目录就把搜索到的目录inode号赋值给当前线程的pcb的cwd_inode_nr字段,下次再调用sys_getcwd时就会以cwd_inode_nr字段的新值作为工作目录的inode号了,上代码(fs.c中)
int32_t sys_chdir(const char* path) {
ASSERT(path != NULL);
struct path_search_record searched_record;
memset(&searched_record, 0, sizeof(struct path_search_record));
int32_t inode_no = search_file(path, &searched_record);
int32_t ret = -1;
if(inode_no == -1) {
printk("sys_chdir: %s is not exist!\n", searched_record.searched_path);
} else {
uint32_t searched_path_depth = path_depth_cnt(searched_record.searched_path);
uint32_t path_depth = path_depth_cnt(path);
if(path_depth == searched_path_depth) {
running_thread()->cwd_inode_nr = inode_no;
ret = 0;
} else {
printk("sys_chdir: %s is regular file!\n", searched_record.searched_path);
}
}
return ret;
}
实验结果
#include "print.h"
#include "init.h"
#include "debug.h"
#include "memory.h"
#include "thread.h"
#include "interrupt.h"
#include "console.h"
#include "process.h"
#include "syscall.h"
#include "syscall-init.h"
#include "stdio.h"
#include "fs.h"
#include "dir.h"
int main(void)
{
put_str("I am kernel\n");
init_all();
intr_enable();
char cwd_buf[32] = {0};
sys_getcwd(cwd_buf, 32);
printf("cwd:%s\n", cwd_buf);
sys_chdir("/dir1");
printf("change cwd now \n");
sys_getcwd(cwd_buf, 32);
printf("cwd:%s\n", cwd_buf);
while(1);
return 0;
}
本篇结束,还最后一篇获得文件属性,文件系统这一章终于要结束了!