清华大学操作系统Lab8实验报告
课程主页:http://os.cs.tsinghua.edu.cn/oscourse/OS2018spring
实验指导书:https://chyyuu.gitbooks.io/ucore_os_docs/content/
github:https://github.com/chyyuu/ucore_os_lab
实验目的
- 了解基本的文件系统系统调用的实现方法;
- 了解一个基于索引节点组织方式的Simple FS文件系统的设计与实现;
- 了解文件系统抽象层-VFS的设计与实现;
练习1: 完成读文件操作的实现
用户程序读文件需要使用系统调用。在用户程序执行read
操作时会调用sys_read
系统调用。根据ucore的中断机制实现,系统调用将通过trap_dispatch
分发给syscall
,随后分发给读的系统调用sys_read
内核函数。
sys_read
内核函数需要进一步调用sysfile_read
内核函数,进入到文件系统抽象层处理流程完成进一步的读文件操作。sysfile_read
函数调用file_read
函数,file_read
函数调用vop_read
函数接口进入到文件系统实例的读操作接口。
vop_read
函数实际上是对sfs_read
的包装。sfs_read函数调用sfs_io函数。它有三个参数,node是对应文件的inode,iob是缓存,write表示是读还是写的布尔值( 0表示读,1表示写) ,这里是0。函数先找到inode对应sfs和sin,然后调用sfs_io_nolock函数进行读取文件操作,最后调用iobuf_skip函数调整iobuf的指针。
sfs_io_nolock
函数主要用来将磁盘中的一段数据读入到内存中或者将内存中的一段数据写入磁盘。需要补充的代码用来对一段地址对应的磁盘块读或写。
// 读取第一页,可能不对齐
if ((ret = sfs_bmap_load_nolock(sfs, sin, blkno, &ino)) != 0) {
goto out;
}
blkoff = offset % SFS_BLKSIZE;
size = (nblks != 0) ? (SFS_BLKSIZE - blkoff) : (endpos - offset);
if ((ret = sfs_buf_op(sfs, buf, size, ino, blkoff)) != 0) {
goto out;
}
alen += size;
// 如果超过一页的话,
if (nblks != 0) {
// 读取第二页到第n-1页,这些页大小均为SFS_BLKSIZE
for (int i = blkno + 1; i < blkno + nblks; ++i) {
if ((ret = sfs_bmap_load_nolock(sfs, sin, i, &ino)) != 0) {
goto out;
}
if ((ret = sfs_block_op(sfs, buf + alen, ino, 1)) != 0) {
goto out;
}
alen += SFS_BLKSIZE;
}
// 最后一页,可能不对齐
if (endpos % SFS_BLKSIZE != 0) {
if ((ret = sfs_bmap_load_nolock(sfs, sin, blkno + nblks, &ino)) != 0) {
goto out;
}
if ((ret = sfs_buf_op(sfs, buf + alen, endpos % SFS_BLKSIZE, ino, 0)) != 0) {
goto out;
}
alen += endpos % SFS_BLKSIZE;
}
}
请在实验报告中给出设计实现”UNIX的PIPE机制“的概要设方案,鼓励给出详细设计方案
PIPE在UNIX体系里也认为是一种文件,参考STDIN、STDOUT以及SFS的设计,可以将PIPE作为与这三者并列的一个子系统。
因此应当在初始化STDIN、STDOUT加入对于PIPE的初始化并创建PIPE的inode。同时为了完成PIPE中内容的缓存,在内存中开辟一块区域给PIPE使用。
为了使用户进程使用PIPE,应当添加同PIPE相关的系统调用,包括
- 将PIPE和两个进程相连
- 向PIPE中写入数据
- 从PIPE中读出数据
这些系统调用通过文件系统提供给的接口,
练习2: 完成基于文件系统的执行程序机制的实现
首先,需要在alloc_proc
函数中对进程控制块中和文件系统有关的部分进行初始化:
proc -> filesp = files_create();
在进行fork时,将父进程的文件系统信息复制到子进程中去:
int file_success = copy_files(clone_flags, proc);
if (file_success != 0) {
goto bad_fork_cleanup_fs;
}
最后还要修改load_icode
的代码。之前的代码是和操作系统一起load进来的,现在要改为在启动操作系统后,通过文件系统加载进来。
static int
load_icode(int fd, int argc, char **kargv) {
if (current->mm != NULL) {
panic("load_icode: current->mm must be empty.\n");
}
int ret = -E_NO_MEM;
struct mm_struct *mm;
//(1) create a new mm for current process
if ((mm = mm_create()) == NULL) {
goto bad_mm;
}
//(2) create a new PDT, and mm->pgdir= kernel virtual addr of PDT
if (setup_pgdir(mm) != 0) {
goto bad_pgdir_cleanup_mm;
}
//(3) copy TEXT/DATA/BSS parts in binary to memory space of process
struct Page *page;
//(3.1) read raw data content in file and resolve elfhdr
struct elfhdr __elf, *elf = &__elf;
ret = load_icode_read(fd, elf, sizeof(struct elfhdr), 0);
if (ret != 0) {
goto bad_elf_cleanup_pgdir;
}
if (elf -> e_magic != ELF_MAGIC) {
ret = -E_INVAL_ELF;
goto bad_elf_cleanup_pgdir;
}
struct proghdr __ph, *ph = &__ph;
uint32_t vm_flags, perm, phnum;
for (phnum = 0; phnum < elf->e_phnum; phnum ++) {
//(3.2) read raw data content in file and resolve proghdr based on info in elfhdr
off_t phoff = elf->e_phoff + sizeof(struct proghdr) * phnum;
if ((ret = load_icode_read(fd, ph, sizeof(struct proghdr), phoff)) != 0) {
goto bad_cleanup_mmap;
}
if (ph->p_type != ELF_PT_LOAD) {
continue ;
}
if (ph->p_filesz > ph->p_memsz) {
ret = -E_INVAL_ELF;
goto bad_cleanup_mmap;
}
if (ph->p_filesz == 0) {
continue ;
}
vm_flags = 0, perm = PTE_U;
if (ph->p_flags & ELF_PF_X) vm_flags |= VM_EXEC;
if (ph->p_flags & ELF_PF_W) vm_flags |= VM_WRITE;
if (ph->p_flags & ELF_PF_R) vm_flags |= VM_READ;
if (vm_flags & VM_WRITE) perm |= PTE_W;
//(3.3) call mm_map to build vma related to TEXT/DATA
if ((ret = mm_map(mm, ph->p_va, ph->p_memsz, vm_flags, NULL)) != 0) {
goto bad_cleanup_mmap;
}
off_t offset = ph->p_offset;
size_t off, size;
uintptr_t start = ph->p_va, end, la = ROUNDDOWN(start, PGSIZE);
ret = -E_NO_MEM;
//(3.4) callpgdir_alloc_page to allocate page for TEXT/DATA, read contents in file
end = ph->p_va + ph->p_filesz;
while (start < end) {
if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) {
ret = -E_NO_MEM;
goto bad_cleanup_mmap;
}
off = start - la, size = PGSIZE - off, la += PGSIZE;
if (end < la) {
size -= la - end;
}
if ((ret = load_icode_read(fd, page2kva(page) + off, size, offset)) != 0) {
goto bad_cleanup_mmap;
}
start += size, offset += size;
}
end = ph->p_va + ph->p_memsz;
(3.5) callpgdir_alloc_page to allocate pages for BSS, memset zero in these pages
if (start < la) {
if (start == end) {
continue ;
}
off = start + PGSIZE - la, size = PGSIZE - off;
if (end < la) {
size -= la - end;
}
memset(page2kva(page) + off, 0, size);
start += size;
assert((end < la && start == end) || (end >= la && start == la));
}
while (start < end) {
if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) {
ret = -E_NO_MEM;
goto bad_cleanup_mmap;
}
off = start - la, size = PGSIZE - off, la += PGSIZE;
if (end < la) {
size -= la - end;
}
memset(page2kva(page) + off, 0, size);
start += size;
}
}
sysfile_close(fd);
//(4) call mm_map to setup user stack, and put parameters into user stack
vm_flags = VM_READ | VM_WRITE | VM_STACK;
if ((ret = mm_map(mm, USTACKTOP - USTACKSIZE, USTACKSIZE, vm_flags, NULL)) != 0) {
goto bad_cleanup_mmap;
}
assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-PGSIZE , PTE_USER) != NULL);
assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-2*PGSIZE , PTE_USER) != NULL);
assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-3*PGSIZE , PTE_USER) != NULL);
assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-4*PGSIZE , PTE_USER) != NULL);
//(5) setup current process's mm, cr3, reset pgidr (using lcr3 MARCO)
mm_count_inc(mm);
current->mm = mm;
current->cr3 = PADDR(mm->pgdir);
lcr3(PADDR(mm->pgdir));
//(6) setup uargc and uargv in user stacks
uint32_t argv_size=0, i;
for (i = 0; i < argc; i ++) {
argv_size += strnlen(kargv[i],EXEC_MAX_ARG_LEN + 1)+1;
}
uintptr_t stacktop = USTACKTOP - (argv_size/sizeof(long)+1)*sizeof(long);
char** uargv=(char **)(stacktop - argc * sizeof(char *));
argv_size = 0;
for (i = 0; i < argc; i ++) {
uargv[i] = strcpy((char *)(stacktop + argv_size ), kargv[i]);
argv_size += strnlen(kargv[i],EXEC_MAX_ARG_LEN + 1)+1;
}
stacktop = (uintptr_t)uargv - sizeof(int);
*(int *)stacktop = argc;
//(7) setup trapframe for user environment
struct trapframe *tf = current->tf;
memset(tf, 0, sizeof(struct trapframe));
tf->tf_cs = USER_CS;
tf->tf_ds = tf->tf_es = tf->tf_ss = USER_DS;
tf->tf_esp = stacktop;
tf->tf_eip = elf->e_entry;
tf->tf_eflags = FL_IF;
ret = 0;
//(8) if up steps failed, you should cleanup the env.
out:
return ret;
bad_cleanup_mmap:
exit_mmap(mm);
bad_elf_cleanup_pgdir:
put_pgdir(mm);
bad_pgdir_cleanup_mm:
mm_destroy(mm);
bad_mm:
goto out;
}
请在实验报告中给出设计实现基于”UNIX的硬链接和软链接机制“的概要设方案,鼓励给出详细设计方案
覆盖的知识点
与参考答案的区别
- 练习1:自己完成。
- 练习2:load_icode部分参考了答案。