操作系統(tǒng) Lab8 文件系統(tǒng) 實驗報告
課程信息所在網(wǎng)址:https://github.com/chyyuu/os_course_info
實驗目的
- 了解基本的文件系統(tǒng)系統(tǒng)調(diào)用的實現(xiàn)方法向抢;
- 了解一個基于索引節(jié)點組織方式的Simple FS文件系統(tǒng)的設(shè)計與實現(xiàn)抽诉;
- 了解文件系統(tǒng)抽象層-VFS的設(shè)計與實現(xiàn)霍殴;
實驗內(nèi)容
- 通過分析了解ucore文件系統(tǒng)的總體架構(gòu)設(shè)計舌菜,完善讀寫文件操作;
- 實現(xiàn)基于文件系統(tǒng)的執(zhí)行程序機制(即改寫do_execve),從而可以完成執(zhí)行存儲在磁盤上的文件和實現(xiàn)文件讀寫等功能;
基本練習
練習0:填寫已有實驗
在本練習中將LAB1/2/3/4/5/6/7的實驗內(nèi)容移植到了LAB8的實驗框架內(nèi)先嬉,由于手動進行內(nèi)容移植比較煩雜泵肄,因此考慮使用diff和patch工具進行自動化的移植,具體使用的命令如下所示:(對于patch工具進行合并的時候產(chǎn)生沖突的少部分內(nèi)容焕蹄,則使用*.rej, *.orig文件來手動解決沖突問題)
diff -r -u -P lab7_origin lab7 > lab7.patch
cd lab8
patch -p1 -u < ../lab7.patch
練習1:完成讀文件操作的實現(xiàn)(需要編碼)
首先了解打開文件的處理流程逾雄,然后參考本實驗后續(xù)的文件讀寫操作的過程分析,編寫在sfs_inode.c中sfs_io_nolock讀文件中數(shù)據(jù)的實現(xiàn)代碼腻脏。
設(shè)計實現(xiàn)
- 在完成練習1之前鸦泳,首先需要對先前LAB中填寫的代碼進行更新,包括對進程控制塊中新增變量的初始化永品、以及在do_fork等函數(shù)中對這些變量進行相應的設(shè)置等做鹰,由于這些內(nèi)容均比較瑣碎,因此在本報告中將不對其進行贅述腐碱;
- 在本練習中需要進行具體編碼實現(xiàn)的函數(shù)是sfs_node.c文件中的sfs_io_nolock函數(shù)誊垢,因此不妨對該函數(shù)進行分析:
- 根據(jù)對該函數(shù)的觀察可以得知,該函數(shù)的功能為針對指定的文件(文件對應的內(nèi)存中的inode信息已經(jīng)給出)症见,從指定偏移量進行指定長度的讀或者寫操作喂走,因此不妨分析系統(tǒng)調(diào)用的讀操作究竟是符合調(diào)用到這個函數(shù)的來了解這個函數(shù)在整個系統(tǒng)中的功能:
- 發(fā)起read系統(tǒng)調(diào)用后,通過正常的系統(tǒng)調(diào)用處理流程谋作,進入sys_read函數(shù)芋肠,該函數(shù)進一步調(diào)用了sysfile_read函數(shù),在這個函數(shù)中遵蚜,創(chuàng)建了大小有限的緩沖區(qū)帖池,用于從文件讀取數(shù)據(jù)之后,進一步復制到用戶空間的指定位置去吭净;具體用于從文件讀取數(shù)據(jù)的函數(shù)是file_read睡汹,在file_read函數(shù)中,通過文件描述符查找到了相應文件對應的內(nèi)存中的inode信息寂殉,然后轉(zhuǎn)交給vop_read進行讀取處理囚巴,事實上就是轉(zhuǎn)交到了sfs_read函數(shù)進行處理(通過函數(shù)指針),然后調(diào)用了sfs_io函數(shù),再進一步調(diào)用了sfs_io_nolock函數(shù)彤叉,這就是我們在本次練習中需要完善的函數(shù)庶柿;
- 在sfs_io_nolock函數(shù)中,首先會進行一系列邊界檢查秽浇,檢查是否訪問合法浮庐,然后將具體的讀/寫操作使用函數(shù)指針統(tǒng)一起來,統(tǒng)一成針對整塊的操作柬焕,以及不需要針對整塊的操作兩個處理函數(shù)审残,接下來的部分就是在本次實驗中需要完成的部分了,這部分的主要功能為完成不落在整塊數(shù)據(jù)塊上的讀/寫操作斑举,以及落在整塊數(shù)據(jù)塊上的讀寫维苔,接下來將結(jié)合具體的代碼來說明實際的實現(xiàn)過程:
if (offset % SFS_BLKSIZE != 0 || endpos / SFS_BLKSIZE == offset / SFS_BLKSIZE) { // 判斷被需要讀/寫的區(qū)域所覆蓋的數(shù)據(jù)塊中的第一塊是否是完全被覆蓋的,如果不是懂昂,則需要調(diào)用非整塊數(shù)據(jù)塊進行讀或?qū)懙暮瘮?shù)來完成相應操作 blkoff = offset % SFS_BLKSIZE; // 計算出在第一塊數(shù)據(jù)塊中進行讀或?qū)懖僮鞯钠屏? size = (nblks != 0) ? (SFS_BLKSIZE - blkoff) : (endpos - offset); // 計算出在第一塊數(shù)據(jù)塊中進行讀或?qū)懖僮餍枰臄?shù)據(jù)長度 if ((ret = sfs_bmap_load_nolock(sfs, sin, blkno, &ino)) != 0) goto out; // 獲取當前這個數(shù)據(jù)塊對應到的磁盤上的數(shù)據(jù)塊的編號 if ((ret = sfs_buf_op(sfs, buf, size, ino, blkoff)) != 0) goto out; // 將數(shù)據(jù)寫入到磁盤中 alen += size; // 維護已經(jīng)讀寫成功的數(shù)據(jù)長度信息 buf += size; } uint32_t my_nblks = nblks; if (offset % SFS_BLKSIZE != 0 && my_nblks > 0) my_nblks --; if (my_nblks > 0) { // 判斷是否存在被需要讀寫的區(qū)域完全覆蓋的數(shù)據(jù)塊 if ((ret = sfs_bmap_load_nolock(sfs, sin, (offset % SFS_BLKSIZE == 0) ? blkno: blkno + 1, &ino)) != 0) goto out; // 如果存在,首先獲取這些數(shù)據(jù)塊對應到磁盤上的數(shù)據(jù)塊的編號 if ((ret = sfs_block_op(sfs, buf, ino, my_nblks)) != 0) goto out; // 將這些磁盤上的這些數(shù)據(jù)塊進行讀或?qū)懖僮? size = SFS_BLKSIZE * my_nblks; alen += size; // 維護已經(jīng)成功讀寫的數(shù)據(jù)長度 buf += size; // 維護緩沖區(qū)的偏移量 } if (endpos % SFS_BLKSIZE != 0 && endpos / SFS_BLKSIZE != offset / SFS_BLKSIZE) { // 判斷需要讀寫的最后一個數(shù)據(jù)塊是否被完全覆蓋(這里還需要確保這個數(shù)據(jù)塊不是第一塊數(shù)據(jù)塊没宾,因為第一塊數(shù)據(jù)塊已經(jīng)操作過了) size = endpos % SFS_BLKSIZE; // 確定在這數(shù)據(jù)塊中需要讀寫的長度 if ((ret = sfs_bmap_load_nolock(sfs, sin, endpos / SFS_BLKSIZE, &ino) == 0) != 0) goto out; // 獲取該數(shù)據(jù)塊對應到磁盤上的數(shù)據(jù)塊的編號 if ((ret = sfs_buf_op(sfs, buf, size, ino, 0)) != 0) goto out; // 進行非整塊的讀或者寫操作 alen += size; buf += size; }
- 至此凌彬,練習1中的所有編碼工作完成,實現(xiàn)了讀文件操作循衰;
- 根據(jù)對該函數(shù)的觀察可以得知,該函數(shù)的功能為針對指定的文件(文件對應的內(nèi)存中的inode信息已經(jīng)給出)症见,從指定偏移量進行指定長度的讀或者寫操作喂走,因此不妨分析系統(tǒng)調(diào)用的讀操作究竟是符合調(diào)用到這個函數(shù)的來了解這個函數(shù)在整個系統(tǒng)中的功能:
問題回答
- 請在實驗報告中給出設(shè)計實現(xiàn)”UNIX的PIPE機制“的概要設(shè)方案铲敛,鼓勵給出詳細設(shè)計方案。
- 為了實現(xiàn)UNIX的PIPE機制会钝,可以考慮在磁盤上保留一部分空間或者是一個特定的文件來作為pipe機制的緩沖區(qū)伐蒋,接下來將說明如何完成對pipe機制的支持:
- 當某兩個進程之間要求建立管道,假定將進程A的標準輸出作為進程B的標準輸入迁酸,那么可以在這兩個進程的進程控制塊上新增變量來記錄進程的這種屬性先鱼;并且同時生成一個臨時的文件,并將其在進程A, B中打開;
- 當進程A使用標準輸出進行write系統(tǒng)調(diào)用的時候奸鬓,通過PCB中的變量可以知道焙畔,需要將這些標準輸出的數(shù)據(jù)輸出到先前提高的臨時文件中去;
- 當進程B使用標準輸入的時候進行read系統(tǒng)調(diào)用的時候串远,根據(jù)其PCB中的信息可以知道宏多,需要從上述的臨時文件中讀取數(shù)據(jù);
- 至此完成了對pipe機制的設(shè)計澡罚;
- 事實上伸但,由于在真實的文件系統(tǒng)和用戶之間還由一層虛擬文件系統(tǒng),因此我們也可以不把數(shù)據(jù)緩沖在磁盤上留搔,而是直接保存在內(nèi)存中更胖,然后完成一個根據(jù)虛擬文件系統(tǒng)的規(guī)范完成一個虛擬的pipe文件,然后進行輸入輸出的時候只要對這個文件進行操作即可;
- 為了實現(xiàn)UNIX的PIPE機制会钝,可以考慮在磁盤上保留一部分空間或者是一個特定的文件來作為pipe機制的緩沖區(qū)伐蒋,接下來將說明如何完成對pipe機制的支持:
練習2: 完成基于文件系統(tǒng)的執(zhí)行程序機制的實現(xiàn)(需要編碼)
改寫proc.c中的load_icode函數(shù)和其他相關(guān)函數(shù)函喉,實現(xiàn)基于文件系統(tǒng)的執(zhí)行程序機制避归。執(zhí)行: make qemu。如果能看看到sh用戶程序的執(zhí)行界面管呵,則基本成功了梳毙。如果在sh用戶界面上可 以執(zhí)行”ls”,”hello”等其他放置在sfs文件系統(tǒng)中的其他執(zhí)行程序,則可以認為本實驗基本成功捐下。
設(shè)計實現(xiàn)
- 通過對實驗代碼的分析可以得知最終用于從磁盤上讀取可執(zhí)行文件账锹,并且加載到內(nèi)存中,完成內(nèi)存空間的初始化的函數(shù)是load_icode函數(shù)坷襟,該函數(shù)在本LAB中的具體實現(xiàn)與先前的LAB區(qū)別在于奸柬,先前的LAB僅僅將原先就加載到了內(nèi)核內(nèi)存空間中的ELF可執(zhí)行文件加載到用戶內(nèi)存空間中,而沒有涉及從磁盤讀取數(shù)據(jù)的操作婴程,而且先前的時候也沒有考慮到給需要執(zhí)行的應用程度傳遞操作的可能性廓奕;
- 仿照先前l(fā)ab中的load_icode實現(xiàn),可以大致將該函數(shù)的實現(xiàn)流程分為以下幾個步驟:
- 給要執(zhí)行的用戶進程創(chuàng)建一個新的內(nèi)存管理結(jié)構(gòu)mm档叔,原先該進程的mm已經(jīng)在do_execve中被釋放掉了桌粉;
- 創(chuàng)建用戶內(nèi)存空間的新的頁目錄表;
- 將磁盤上的ELF文件的TEXT/DATA/BSS段正確地加載到用戶空間中衙四;
- 從磁盤中讀取elf文件的header铃肯;
- 根據(jù)elfheader中的信息,獲取到磁盤上的program header传蹈;
- 對于每一個program header:
- 為TEXT/DATA段在用戶內(nèi)存空間上的保存分配物理內(nèi)存頁押逼,同時建立物理頁和虛擬頁的映射關(guān)系;
- 從磁盤上讀取TEXT/DATA段惦界,并且復制到用戶內(nèi)存空間上去挑格;
- 根據(jù)program header得知是否需要創(chuàng)建BBS段,如果是表锻,則分配相應的內(nèi)存空間恕齐,并且全部初始化成0,并且建立物理頁和虛擬頁的映射關(guān)系瞬逊;
- 將用戶棧的虛擬空間設(shè)置為合法显歧,并且為棧頂部分先分配4個物理頁,建立好映射關(guān)系确镊;
- 切換到用戶地址空間士骤;
- 設(shè)置好用戶棧上的信息,即需要傳遞給執(zhí)行程序的參數(shù)蕾域;
- 設(shè)置好中斷幀拷肌;
- 接下來結(jié)合具體的代碼實現(xiàn)來說明本實驗中的實現(xiàn):
static int
load_icode(int fd, int argc, char **kargv) {
if (current->mm != NULL) { // 判斷當前進程的mm是否已經(jīng)被釋放掉了
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) { // 為進程創(chuàng)建一個新的mm
goto bad_mm;
}
// (2) create a new PDT
if ((ret = setup_pgdir(mm)) != 0) { // 進行頁表項的設(shè)置
goto bad_pgdir_cleanup_mm;
}
// (3) copy TEXT/DATA/BSS section
// (3.1) resolve elf header
struct elfhdr elf, *elfp = &elf;
off_t offset = 0;
load_icode_read(fd, (void *) elfp, sizeof(struct elfhdr), offset); // 從磁盤上讀取出ELF可執(zhí)行文件的elf-header
offset += sizeof(struct elfhdr);
if (elfp->e_magic != ELF_MAGIC) { // 判斷該ELF文件是否合法
ret = -E_INVAL_ELF;
goto bad_elf_cleanup_pgdir;
}
struct proghdr ph, *php = &ph;
uint32_t vm_flags, perm;
struct Page *page;
for (int i = 0; i < elfp->e_phnum; ++ i) { // 根據(jù)elf-header中的信息到旦,找到每一個program header
// (3.2) resolve prog header
load_icode_read(fd, (void *) php, sizeof(struct proghdr), elfp->e_phoff + i * sizeof(struct proghdr)); // 讀取program header
if (php->p_type != ELF_PT_LOAD) {
continue;
}
if (php->p_filesz > php->p_memsz) {
ret = -E_INVAL_ELF;
goto bad_cleanup_mmap;
}
if (php->p_filesz == 0) {
continue;
}
// (3.3) build vma
vm_flags = 0, perm = PTE_U;
if (php->p_flags & ELF_PF_X) vm_flags |= VM_EXEC; // 根據(jù)ELF文件中的信息,對各個段的權(quán)限進行設(shè)置
if (php->p_flags & ELF_PF_W) vm_flags |= VM_WRITE;
if (php->p_flags & ELF_PF_R) vm_flags |= VM_READ;
if (vm_flags & VM_WRITE) perm |= PTE_W;
if ((ret = mm_map(mm, php->p_va, php->p_memsz, vm_flags, NULL)) != 0) { // 將這些段的虛擬內(nèi)存地址設(shè)置為合法的
goto bad_cleanup_mmap;
}
// (3.4) allocate pages for TEXT/DATA sections
offset = php->p_offset;
size_t off, size;
uintptr_t start = php->p_va, end = php->p_va + php->p_filesz, la = ROUNDDOWN(start, PGSIZE);
ret = -E_NO_MEM;
while (start < end) {
if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) { // 為TEXT/DATA段逐頁分配物理內(nèi)存空間
goto bad_cleanup_mmap;
}
off = start - la, size = PGSIZE - off, la += PGSIZE;
if (end < la) {
size -= la - end;
}
load_icode_read(fd, page2kva(page) + off, size, offset); // 將磁盤上的TEXT/DATA段讀入到分配好的內(nèi)存空間中去
//memcpy(page2kva(page) + off, page2kva(buff_page), size);
start += size, offset += size;
}
// (3.5) allocate pages for BSS
end = php->p_va + php->p_memsz;
if (start < la) { // 如果存在BSS段巨缘,并且先前的TEXT/DATA段分配的最后一頁沒有被完全占用添忘,則剩余的部分被BSS段占用,因此進行清零初始化
if (start == end) {
continue;
}
off = start + PGSIZE - la, size = PGSIZE - off;
if (end < la) {
size -= la - end;
}
memset(page2kva(page) + off, 0, size); // init all BSS data with 0
start += size;
assert((end < la && start == end) || (end >= la && start == la));
}
while (start < end) { // 如果BSS段還需要更多的內(nèi)存空間的話若锁,進一步進行分配
if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) { // 為BSS段分配新的物理內(nèi)存頁
goto bad_cleanup_mmap;
}
off = start - la, size = PGSIZE - off, la += PGSIZE;
if (end < la) {
size -= la - end;
}
memset(page2kva(page), 0, size); // 將分配到的空間清零初始化
start += size;
}
}
sysfile_close(fd); // 關(guān)閉傳入的文件搁骑,因為在之后的操作中已經(jīng)不需要讀文件了
// (4) setup user stack
vm_flags = VM_READ | VM_WRITE | VM_STACK; // 設(shè)置用戶棧的權(quán)限
if ((ret = mm_map(mm, USTACKTOP - USTACKSIZE, USTACKSIZE, vm_flags, NULL)) != 0) { // 將用戶棧所在的虛擬內(nèi)存區(qū)域設(shè)置為合法的
goto bad_cleanup_mmap;
}
// setup args
uint32_t stacktop = USTACKTOP;
uint32_t argsize = 0;
for (int j = 0; j < argc; ++ j) { // 確定傳入給應用程序的參數(shù)具體應當占用多少空間
argsize += (1 + strlen(kargv[j])); // includinng the ending '\0'
}
argsize = (argsize / sizeof(long) + 1) * sizeof(long); //alignment
argsize += (2 + argc) * sizeof(long);
stacktop = USTACKTOP - argsize; // 根據(jù)參數(shù)需要在棧上占用的空間來推算出,傳遞了參數(shù)之后棧頂?shù)奈恢? uint32_t pagen = argsize / PGSIZE + 4;
for (int j = 1; j <= 4; ++ j) { // 首先給棧頂分配四個物理頁
assert(pgdir_alloc_page(mm->pgdir, USTACKTOP - PGSIZE * j, PTE_USER) != NULL);
}
// for convinience, setup mm (5)
mm_count_inc(mm); // 切換到用戶的內(nèi)存空間又固,這樣的話后文中在棧上設(shè)置參數(shù)部分的操作將大大簡化仲器,因為具體因為空間不足而導致的分配物理頁的操作已經(jīng)交由page fault處理了,是完全透明的
current->mm = mm;
current->cr3 = PADDR(mm->pgdir);
lcr3(PADDR(mm->pgdir));
// (6) setup args in user stack
uint32_t now_pos = stacktop, argvp;
*((uint32_t *) now_pos) = argc; // 設(shè)置好argc參數(shù)(壓入棧)
now_pos += 4;
*((uint32_t *) now_pos) = argvp = now_pos + 4; // 設(shè)置argv數(shù)組的位置
now_pos += 4;
now_pos += argc * 4;
for (int j = 0; j < argc; ++ j) {
argsize = strlen(kargv[j]) + 1; // 將argv[j]指向的數(shù)據(jù)拷貝到用戶棧中
memcpy((void *) now_pos, kargv[j], argsize);
*((uint32_t *) (argvp + j * 4)) = now_pos; // 設(shè)置好用戶棧中argv[j]的數(shù)值
now_pos += argsize;
}
// (7) setup tf
struct trapframe *tf = current->tf; // 設(shè)置中斷幀
memset(tf, 0, sizeof(struct trapframe));
tf->tf_cs = USER_CS; // 需要返回到用戶態(tài)仰冠,因此使用用戶態(tài)的數(shù)據(jù)段和代碼段的選擇子
tf->tf_ds = tf->tf_es = tf->tf_ss = USER_DS;
tf->tf_esp = stacktop; // 棧頂位置為先前計算過的棧頂位置乏冀,注意在C語言的函數(shù)調(diào)用規(guī)范中,棧頂指針指向的位置應該是返回地址而不是第一個參數(shù)洋只,這里讓棧頂指針指向了第一個參數(shù)的原因在于辆沦,在中斷返回之后,會跳轉(zhuǎn)到ELF可執(zhí)行程序的入口處识虚,在該入口處會進一步使用call命令調(diào)用主函數(shù)众辨,這時候也就完成了將Return address入棧的功能,因此這里無需畫蛇添足壓入返回地址
tf->tf_eip = elfp->e_entry; // 將返回地址設(shè)置為用戶程序的入口
tf->tf_eflags = 0x2 | FL_IF; // 允許中斷舷礼,根據(jù)IA32的規(guī)范,eflags的第1位需要恒為1
ret = 0;
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;
}
- 至此郊闯,完成了本練習中的所有編碼任務妻献;
問題回答
- 請在實驗報告中給出設(shè)計實現(xiàn)基于”UNIX的硬鏈接和軟鏈接機制“的概要設(shè)方案,鼓勵給出詳細設(shè)計方案;
- 觀察到保存在磁盤上的inode信息均存在一個nlinks變量用于表示當前文件的被鏈接的計數(shù)团赁,因而支持實現(xiàn)硬鏈接和軟鏈接機制育拨;
- 如果在磁盤上創(chuàng)建一個文件A的軟鏈接B,那么將B當成正常的文件創(chuàng)建inode欢摄,然后將TYPE域設(shè)置為鏈接熬丧,然后使用剩余的域中的一個,指向A的inode位置怀挠,然后再額外使用一個位來標記當前的鏈接是軟鏈接還是硬鏈接析蝴;
- 當訪問到文件B(read,write等系統(tǒng)調(diào)用)绿淋,判斷如果B是一個鏈接闷畸,則實際是將對B指向的文件A(已經(jīng)知道了A的inode位置)進行操作;
- 當刪除一個軟鏈接B的時候吞滞,直接將其在磁盤上的inode刪掉即可佑菩;
- 如果在磁盤上的文件A創(chuàng)建一個硬鏈接B盾沫,那么在按照軟鏈接的方法創(chuàng)建完B之后,還需要將A中的被鏈接的計數(shù)加1殿漠;
- 訪問硬鏈接的方式與訪問軟鏈接是一致的赴精;
- 當刪除一個硬鏈接B的時候,除了需要刪除掉B的inode之外绞幌,還需要將B指向的文件A的被鏈接計數(shù)減1蕾哟,如果減到了0,則需要將A刪除掉啊奄;
- 觀察到保存在磁盤上的inode信息均存在一個nlinks變量用于表示當前文件的被鏈接的計數(shù)团赁,因而支持實現(xiàn)硬鏈接和軟鏈接機制育拨;
實驗結(jié)果
最終的實驗結(jié)果符合預期渐苏,并且能夠通過make grade腳本的檢查,如下圖所示:
參考答案分析
練習1
比較練習1的實現(xiàn)與參考答案的實現(xiàn)的區(qū)別在于一些細節(jié)方面的實現(xiàn)菇夸,主要體現(xiàn)在對完全被讀寫區(qū)域覆蓋的數(shù)據(jù)塊進行讀寫的時候琼富,提供的函數(shù)事實上是可以完成連續(xù)若干塊數(shù)據(jù)塊的讀寫的,但是參考答案沒有利用這個特點庄新,而是額外添加了一個循環(huán)鞠眉,然后在循環(huán)中對每一個數(shù)據(jù)塊逐次進行讀取操作,這有可能會造成時間效率的降低择诈;
練習2
本實驗在練習2中的實現(xiàn)與參考答案的實現(xiàn)大致一致械蹋,但是經(jīng)過仔細的比較,觀察到一個細節(jié)羞芍,參考答案在確定參數(shù)的長度的時候使用的函數(shù)時strnlen哗戈,而本實驗中的實現(xiàn)使用了strlen,而后者是不安全的荷科,有可能遭到棧溢出攻擊的唯咬,因此在這個區(qū)別上,參考答案的實現(xiàn)明顯優(yōu)于本實驗的實驗畏浆,這也其實我在完成實際的編程任務的時候需要充分考慮到魯棒性胆胰、安全性等細節(jié),這也是我自己覺得在本次操作系統(tǒng)實驗中做得有所欠缺的地方刻获;
實驗中涉及的知識點列舉
-
在本次實驗中涉及到的知識點如下:
- 虛擬文件系統(tǒng)蜀涨;
- SFS文件系統(tǒng);
- 將設(shè)備抽象為文件的管理方式蝎毡;
- 系統(tǒng)調(diào)用厚柳;
- 進程間的調(diào)度、管理沐兵;
- ELF文件格式草娜;
- ucore中用戶進程虛擬空間的劃分;
-
對應的OS中的知識點如下:
- 在ucore中文件系統(tǒng)痒筒、虛擬文件系統(tǒng)宰闰、以及SFS文件系統(tǒng)的具體實現(xiàn)茬贵;
- 在ucore中將stdin,stdout抽象成文件的機制移袍;
- 在ucore中系統(tǒng)調(diào)用的機制解藻;
- 在ucore中完成ELF文件從磁盤到內(nèi)存的加載的具體機制;
-
它們之間的關(guān)系為:
- 前者為后者提供了底層的支持葡盗,比如對SFS文件系統(tǒng)的了解才能夠使得可以在OS中正確地實現(xiàn)對使用該文件系統(tǒng)的磁盤的訪問螟左;
- 前者給后者提供了必要的基礎(chǔ)知識,比如只有了解了ELF文件的格式觅够,以及了解了用戶進程空間的劃分之后胶背,才能夠正確地在OS中實現(xiàn)將指定ELF文件加載到內(nèi)存中運行的操作(exec系統(tǒng)調(diào)用);
實驗中未涉及的知識點列舉
在本次實驗中未涉及到的知識點列舉如下:
- 進程之間的同步互斥機制喘先;
- 操作系統(tǒng)的啟動機制钳吟;
- 操作系統(tǒng)對網(wǎng)絡(luò)協(xié)議棧的支持;
實驗代碼
https://github.com/AmadeusChan/ucore_os_lab/tree/master/lab8
參考文獻
- INTEL 80386 PROGRAMMER'S REFERENCE MANUAL 1986