1. 程式人生 > >2018-2019-1 20189221 《Linux核心原理與分析》第六週作業

2018-2019-1 20189221 《Linux核心原理與分析》第六週作業

2018-2019-1 20189221 《Linux核心原理與分析》第六週作業

實驗五

實驗過程

將Fork函式移植到Linux的MenuOS

fork()函式通過系統呼叫建立一個與原來程序幾乎完全相同的程序。在fork函式執行完畢後,如果建立新程序成功,則出現兩個程序,一個是子程序,一個是父程序。在子程序中,fork函式返回0,在父程序中,fork返回新建立子程序的程序ID。通過fork返回的值來判斷當前程序是子程序還是父程序。

啟動MenuOS:

在test.c中新增程式碼如下:


int Fork(int argc, char *argv[])
{

    pid_t fpid;
    int count = 0;
    fpid = fork();
    if (fpid < 0)
        printf("error in fork!");
    else if (fpid == 0) {
        printf("i am the child process, my process id is %d\n",getpid());
        count++;
    }
    else {
        printf("i am the parent process, my process id is %d\n",getpid());
        count++;
    }
    printf("count: %d\n",count);
    return 0;
}

int main()
{
    PrintMenuOS();
    SetPrompt("MenuOS>>");
    MenuConfig("version","MenuOS V1.0(Based on Linux 3.18.6)",NULL);
    MenuConfig("quit","Quit from MenuOS",Quit);
    MenuConfig("fork","fork a child process",Fork);

    ExecuteMenu();
}

執行結果:

sys_call過程分析

gdb除錯:

由於實驗樓多次卡頓,十分浪費時間

除錯程式碼:

$ gdb
(gdb) file linux-3.18.6/vmlinux
(gdb) target remote:1234
(gdb) continue

在sys_fork設定斷點,在qemu中輸入fork-asm命令,可以看到停在了sys_fork()函式中。

然後s單步執行,finish返回do_fork函式,返回值$2=866,即分配了pid=866的子程序。繼續單步,到了schedule()中,此時發生了程序排程。finish後schedule返回。

再次單步執行,出現Cannot find bounds of current function。此時gdb已無法跟蹤。

gdb中輸入c繼續執行,看到qemu中輸出fid = 866,即子程序的pid為866。

system_call的程式碼:

ENTRY(system_call)
     RING0_INT_FRAME    
     ASM_CLAC        
     pushl_cfi %eax            //儲存系統呼叫號;
     SAVE_ALL                  //可以用到的所有CPU暫存器儲存到棧中
     GET_THREAD_INFO(%ebp)     //ebp用於存放當前程序thread_info結構的地址
     testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
     jnz syscall_trace_entry
     cmpl $(nr_syscalls), %eax  //檢查系統呼叫號(系統呼叫號應小於NR_syscalls),
     jae syscall_badsys         //不合法,跳入到異常處理
 syscall_call:
     call *sys_call_table(,%eax,4) //合法,對照系統呼叫號在系統呼叫表中尋找相應服務例程
     movl %eax,PT_EAX(%esp)        //儲存返回值到棧中
 syscall_exit:  
     testl $_TIF_ALLWORK_MASK, %ecx   //檢查是否需要處理訊號
     jne syscall_exit_work        //需要,進入 syscall_exit_work
 restore_all: 
     TRACE_IRQS_IRET              //不需要,執行restore_all恢復,返回使用者態
 irq_return:
     INTERRUPT_RETURN             //相當於iret

system_call流程:

實驗總結

系統呼叫是特殊的中斷函式,是多種中斷處理過程的集合

系統呼叫的過程其實是另一種上下文切換的實現:

首先SAVE ALL儲存上下文

根據IDT呼叫核心函式

執行RESTORE_ALL並返回使用者模式

系統呼叫與中斷的共同之處

  • 儲存現場

    在系統呼叫時,用SAVE_ALL來儲存系統呼叫時的上下文。
    中斷處理的第一步也是要儲存中斷程式現場。
    中斷處理完之後,可以返回到原來被中斷的地方,在原有的執行環境下繼續正確的執行下去。

  • 確定中斷資訊

    在系統呼叫中,需要將系統呼叫號通過eax傳入,通過sys_call_table查詢到呼叫的系統呼叫,然後跳轉到相應的程式進行處理。
    中斷處理時系統也需要有一箇中斷號,通過檢索中斷向量表,瞭解中斷的型別和裝置。

  • 處理中斷

    跳轉到相應的中斷處理程式後,對中斷進行處理。

  • 返回

    系統呼叫時最後要restore_all恢復系統呼叫時的現場,並用iret返回使用者態。
    同樣,執行完中斷處理程式,核心也要執行特定指令序列,恢復中斷時現場,並使得程序回到使用者態。

系統呼叫與一般函式的不同之處

  • 不是通過“CALL”指令而是通過“INT”指令發起呼叫;

  • 不是通過“RET”指令,而是通過“IRET”指令完成呼叫返回;

  • 當到達核心態後,作業系統需要嚴格檢查系統呼叫傳遞的引數,確保不破壞整個系統的安全性;

  • 執行系統呼叫可導致程序等待某事件發生,從而可引起程序切換;