<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # 十二、 來源:[JOS學習筆記(十二)](http://blog.csdn.net/roger__wong/article/details/9988619) 快找工作了,一直沒更新,放假一周的時間抽了點工夫做了LAB4的PART B,總體來說還是感覺比較難的,尤其是一段匯編代碼和異常棧那亂七八糟的堆棧。 ## 一、概述 本部分實驗主要是實現一個copy on write的fork函數,第一步是實現一個用戶態的page fault處理機制:首先用戶態使用一個系統調用傳遞給內核態一個函數指針作為page fault的回調函數,接著當發生page fault時內核進行簡單的判斷將該函數需要的一個特殊數據結構壓棧,再使用iret跳到用戶態執行此回調函數,執行完之后接著繼續執行原先的用戶態函數。第二步是在此基礎上實現一個copy on write的fork,首先復制父進程地址空間的映射(也就是頁目錄和頁表),然后把對應的頁表全部變成不可寫,并在保留位加入特殊符號,因此當寫操作時候會報page fault錯,報錯后轉入用戶態的,在用戶態把出錯的頁面復制后進行重新映射,之后繼續返回源程序執行。 看起來不復雜,實際調試起來非常繁瑣,內核crash上百次后總算是調通了。 ## 二、實驗 ### Exercise 7 實現一個設置page_fault_upcall的系統調用,較為簡單: ``` static int sys_env_set_pgfault_upcall(envid_t envid, void *func) { // LAB 4: Your code here. struct Env* env; int ret=envid2env(envid,&env,1); if(ret<0) return ret; env->env_pgfault_upcall=func; cprintf("func :0x%x \r\n",func); return 0; //panic("sys_env_set_pgfault_upcall not implemented"); } ``` ### Exercise 8 實現內核態的page_fault處理函數,該函數負責跳轉到用戶態的upcall(也就是pfentry.S),并為用戶態的page_fault_handler設置好參數。 為什么要在用戶態進行處理,即使用env_run進而調用而不是直接跳轉到upcall函數指針處執行?個人認為,是因為直接在內核態處理過于危險,用戶可以借此注入高權限的惡意代碼。 ``` void page_fault_handler(struct Trapframe *tf) { uint32_t fault_va; fault_va = rcr2(); if((tf->tf_cs & 3)==0) { //內核態的錯誤依然沒法處理 print_trapframe(tf); panic("kernel mode page faults!!"); } //判斷用戶是否給異常棧進行了映射 user_mem_assert(curenv,(void*)(UXSTACKTOP-PGSIZE),PGSIZE,0); if(curenv->env_pgfault_upcall==NULL ) { //沒有注冊用戶態的upcall函數 cprintf("[%08x] user fault va %08x ip %08x\n", curenv->env_id, fault_va, tf->tf_eip); env_destroy(curenv); return ; } //構造數據結構,并復制,這個數據結構將傳遞給用戶態的處理函數 struct UTrapframe utf; memmove((void*)(&utf.utf_regs),(void*)(&(tf->tf_regs)),sizeof(tf->tf_regs));//復制寄存器 (&utf)->utf_eflags=tf->tf_eflags;//復制flags (&utf)->utf_eip=tf->tf_eip;//復制eip (&utf)->utf_err=tf->tf_err;//復制err (&utf)->utf_esp=tf->tf_esp;//復制esp (&utf)->utf_fault_va=fault_va; int espaddr=0; if(tf->tf_esp>=UXSTACKTOP-PGSIZE && tf->tf_esp<=UXSTACKTOP-1) { //運行到這里說明是在用戶態的異常處理函數里產生了異常 struct Page* page=page_lookup(curenv->env_pgdir,(void*)(tf->tf_esp-4),0); if(page==NULL) { cprintf("non Page ...\r\n"); page=page_alloc(ALLOC_ZERO); page_insert(curenv->env_pgdir,page,(void*)(tf->tf_esp-4),PTE_U|PTE_W); } memmove((void*)((tf->tf_esp)-4-sizeof(utf)),&utf,sizeof(utf)); espaddr=tf->tf_esp-4-sizeof(utf);//新的棧頂 } else { //將UTrapframe放到棧頂 memmove((void*)(UXSTACKTOP-sizeof(utf)),&utf,sizeof(utf)); espaddr=UXSTACKTOP-sizeof(utf);//改變棧指針,注意棧的生長是從高到底生長 } struct Env *env=curenv; int calladdr=Paddr((int)env->env_pgfault_upcall); curenv->env_tf.tf_eip=(int)env->env_pgfault_upcall;//將eip設置為upcall curenv->env_tf.tf_esp=espaddr;//設置堆棧地址 env_run(curenv);//返回用戶態執行 } ``` ### Exercise 9 完成pfentry.S,主要是在用戶態的page_fault_handler結束后如何恢復現場并跳回原程序執行。 ``` .text .globl _pgfault_upcall _pgfault_upcall: // Call the C page fault handler. pushl %esp // function argument: pointer to UTF movl _pgfault_handler, %eax call *%eax addl $4, %esp // pop function argument addl $8, %esp movl %esp,%eax addl $32,%esp popl %ebx addl $4,%esp popl %esp pushl %ebx movl %eax,%esp popal addl $4,%esp popf popl %esp subl $4,%esp ret ``` 這段代碼較為難以閱讀,首先給出_pgfault_handler結束后的堆棧: ``` // trap-time esp // trap-time eflags // trap-time eip // utf_regs.reg_eax // ... // utf_regs.reg_esi // utf_regs.reg_edi // utf_err (error code) // utf_fault_va <-- %esp ``` 然后按順序匯編代碼做了這么以下幾件事: + 首先esp+8,即跳過utf_fault_va和errcode,指向reg_edi。 + 然后把這個esp存放在eax中。 + 接著esp+32,即指向trap-time eip。 + 然后調用popl,此時eip存放在了ebx中,esp指向eflags + 然后跳過eflags,指向trap-time esp + 接著把這個esp出棧替代原先的esp。 + 把ebx里的內容,也就是trap-time eip壓入新的堆棧里 + 將eax里的內容放入esp,此時esp又重新指向reg_edi + 使用popal恢復所有寄存器 + esp+4,跳過trap-time eip,然后popf恢復eflags。此時esp指向trap-time esp + 接著此esp出棧并替換原esp。 + 然后esp-4,即指向我們之前壓入的trap-time eip + 調用ret,彈出指令后堆棧指向trap-time esp所指向的位置,程序能夠正常執行。 ### Exercise 10 完成用戶態的set_pgfault_handler函數,較為簡單 ``` void set_pgfault_handler(void (*handler)(struct UTrapframe *utf)) { int r; if (_pgfault_handler == 0) { //如果是第一次賦值,則要先非配異常棧,然后再設置upcall int envid=sys_getenvid(); int r=sys_page_alloc(envid,(void*)UXSTACKTOP-PGSIZE,PTE_U|PTE_W|PTE_P); if(r<0) { panic("alloc uxstack fail"); } sys_env_set_pgfault_upcall(envid, (void*) _pgfault_upcall); } // Save handler pointer for assembly to call. _pgfault_handler = handler; } ``` ### Exercise 11 首先我發現了一個我在env.c中env_setup_vm中的一個錯誤,我只復制了頁目錄,沒有復制頁表導致所有進程共享了一個頁表,一個修改導致其余的也修改。下面是改正后的函數: ``` static int env_setup_vm(struct Env *e) { int i; struct Page *p = NULL; cprintf("env_setup_vm\r\n"); // Allocate a page for the page directory if (!(p = page_alloc(ALLOC_ZERO))) return -E_NO_MEM; e->env_pgdir=page2kva(p); for(i=PDX(UTOP);i<1024;i++) { if(kern_pgdir[i]!=0) { struct Page* page=page_alloc(ALLOC_ZERO); e->env_pgdir[i]=(int)page2pa(page)|PTE_P|PTE_W|PTE_U; if(page==NULL) { return -E_NO_MEM; } struct Page* kernpage=pa2page(PTE_ADDR(kern_pgdir[i])); memmove(page2kva(page),page2kva(kernpage),PGSIZE); } } p->pp_ref++; page_insert(e->env_pgdir,p,(void*)UVPT,PTE_P|PTE_U); return 0; } ``` 給出fork.c整個文件,較為簡單,即使出錯也是因為一些粗心導致的錯誤。 ``` // implement fork from user space #include <inc/string.h> #include <inc/lib.h> // PTE_COW marks copy-on-write page table entries. // It is one of the bits explicitly allocated to user processes (PTE_AVAIL). #define PTE_COW 0x800 // // Custom page fault handler - if faulting page is copy-on-write, // map in our own private writable copy. // static void pgfault(struct UTrapframe *utf) { void *addr = (void *) utf->utf_fault_va; uint32_t err = utf->utf_err; int r; extern volatile pte_t vpt[]; if((vpt[PDX(addr)] & (0 |PTE_W |PTE_COW))==0) { panic("PTE WRONG!!\r\n"); } int envid=sys_getenvid(); int result=sys_page_alloc(envid,PFTEMP,PTE_U|PTE_W|PTE_P); memmove(PFTEMP,ROUNDDOWN(addr,PGSIZE),PGSIZE); sys_page_map(envid,(void*) PFTEMP,envid,(void*)ROUNDDOWN(addr,PGSIZE), PTE_U|PTE_W|PTE_P); } // // Map our virtual page pn (address pn*PGSIZE) into the target envid // at the same virtual address. If the page is writable or copy-on-write, // the new mapping must be created copy-on-write, and then our mapping must be // marked copy-on-write as well. (Exercise: Why do we need to mark ours // copy-on-write again if it was already copy-on-write at the beginning of // this function?) // // Returns: 0 on success, < 0 on error. // It is also OK to panic on error. // static int duppage(envid_t envid, unsigned pn) { int r=sys_getenvid(); int result=0; int perm=0; if(pn*PGSIZE==UXSTACKTOP-PGSIZE) return 0; //整個地址空間除異常棧之外全部進行重新映射 perm = (perm |PTE_P| PTE_U|PTE_COW ); result=sys_page_map(r, (void*)(pn*PGSIZE),envid, (void*)(pn*PGSIZE), perm); result=sys_page_map(r, (void*)(pn*PGSIZE),r, (void*)(pn*PGSIZE), perm); return 0; } // // User-level fork with copy-on-write. // Set up our page fault handler appropriately. // Create a child. // Copy our address space and page fault handler setup to the child. // Then mark the child as runnable and return. // // Returns: child's envid to the parent, 0 to the child, < 0 on error. // It is also OK to panic on error. // // Hint: // Use vpd, vpt, and duppage. // Remember to fix "thisenv" in the child process. // Neither user exception stack should ever be marked copy-on-write, // so you must allocate a new page for the child's user exception stack. // envid_t fork(void) { // LAB 4: Your code here. //panic("fork not implemented"); //cprintf("this is Fork!\r\n"); set_pgfault_handler(pgfault); envid_t envid; uint8_t *addr; int r; extern unsigned char end[]; envid = sys_exofork(); if (envid < 0) panic("sys_exofork: %e", envid); if (envid == 0) { thisenv = &envs[ENVX(sys_getenvid())]; return 0; } // cprintf("user : create new env finish! %d\r\n",envid); sys_page_alloc(envid,(void*)UXSTACKTOP-PGSIZE,PTE_U|PTE_W|PTE_P); extern volatile pte_t vpt[]; int i,j; for(i=0;i<=UTOP/PGSIZE-1;i++) { if((vpt[i/1024] &(0|PTE_P))!=0 ) { duppage(envid,i); } } if ((r = sys_env_set_status(envid, ENV_RUNNABLE)) < 0) panic("sys_env_set_status: %e", r); cprintf("this is Fork finish!!\r\n"); return envid; } // Challenge! int sfork(void) { panic("sfork not implemented"); return -E_INVAL; } ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看