清華大學操作系統(tǒng)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
實驗目的
- 了解基本的文件系統(tǒng)系統(tǒng)調(diào)用的實現(xiàn)方法廓俭;
- 了解一個基于索引節(jié)點組織方式的Simple FS文件系統(tǒng)的設計與實現(xiàn)云石;
- 了解文件系統(tǒng)抽象層-VFS的設計與實現(xiàn);
練習1: 完成讀文件操作的實現(xiàn)
用戶程序讀文件需要使用系統(tǒng)調(diào)用研乒。在用戶程序執(zhí)行read
操作時會調(diào)用sys_read
系統(tǒng)調(diào)用留晚。根據(jù)ucore的中斷機制實現(xiàn),系統(tǒng)調(diào)用將通過trap_dispatch
分發(fā)給syscall
告嘲,隨后分發(fā)給讀的系統(tǒng)調(diào)用sys_read
內(nèi)核函數(shù)。
sys_read
內(nèi)核函數(shù)需要進一步調(diào)用sysfile_read
內(nèi)核函數(shù)奖地,進入到文件系統(tǒng)抽象層處理流程完成進一步的讀文件操作橄唬。sysfile_read
函數(shù)調(diào)用file_read
函數(shù),file_read
函數(shù)調(diào)用vop_read
函數(shù)接口進入到文件系統(tǒng)實例的讀操作接口参歹。
vop_read
函數(shù)實際上是對sfs_read
的包裝仰楚。sfs_read函數(shù)調(diào)用sfs_io函數(shù)。它有三個參數(shù)犬庇,node是對應文件的inode僧界,iob是緩存,write表示是讀還是寫的布爾值( 0表示讀臭挽,1表示寫) 捂襟,這里是0。函數(shù)先找到inode對應sfs和sin欢峰,然后調(diào)用sfs_io_nolock函數(shù)進行讀取文件操作葬荷,最后調(diào)用iobuf_skip函數(shù)調(diào)整iobuf的指針。
sfs_io_nolock
函數(shù)主要用來將磁盤中的一段數(shù)據(jù)讀入到內(nèi)存中或者將內(nèi)存中的一段數(shù)據(jù)寫入磁盤纽帖。需要補充的代碼用來對一段地址對應的磁盤塊讀或?qū)憽?/p>
// 讀取第一頁宠漩,可能不對齊
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;
}
}
請在實驗報告中給出設計實現(xiàn)”UNIX的PIPE機制“的概要設方案,鼓勵給出詳細設計方案
PIPE在UNIX體系里也認為是一種文件室囊,參考STDIN雕崩、STDOUT以及SFS的設計魁索,可以將PIPE作為與這三者并列的一個子系統(tǒng)刺彩。
因此應當在初始化STDIN蟹略、STDOUT加入對于PIPE的初始化并創(chuàng)建PIPE的inode。同時為了完成PIPE中內(nèi)容的緩存瑰排,在內(nèi)存中開辟一塊區(qū)域給PIPE使用捉貌。
為了使用戶進程使用PIPE支鸡,應當添加同PIPE相關(guān)的系統(tǒng)調(diào)用,包括
- 將PIPE和兩個進程相連
- 向PIPE中寫入數(shù)據(jù)
- 從PIPE中讀出數(shù)據(jù)
這些系統(tǒng)調(diào)用通過文件系統(tǒng)提供給的接口趁窃,
練習2: 完成基于文件系統(tǒng)的執(zhí)行程序機制的實現(xiàn)
首先牧挣,需要在alloc_proc
函數(shù)中對進程控制塊中和文件系統(tǒng)有關(guān)的部分進行初始化:
proc -> filesp = files_create();
在進行fork時,將父進程的文件系統(tǒng)信息復制到子進程中去:
int file_success = copy_files(clone_flags, proc);
if (file_success != 0) {
goto bad_fork_cleanup_fs;
}
最后還要修改load_icode
的代碼醒陆。之前的代碼是和操作系統(tǒng)一起load進來的瀑构,現(xiàn)在要改為在啟動操作系統(tǒng)后,通過文件系統(tǒng)加載進來刨摩。
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;
}
請在實驗報告中給出設計實現(xiàn)基于”UNIX的硬鏈接和軟鏈接機制“的概要設方案寺晌,鼓勵給出詳細設計方案
覆蓋的知識點
與參考答案的區(qū)別
- 練習1:自己完成。
- 練習2:load_icode部分參考了答案澡刹。