分析system_call中斷處理過程

最第一版本的MenuOS只支持version和help命令,顯然這並不能知足咱們的需求。咱們如今來爲它添加一個fork命令和fork-asm命令,其做用是測試fork的系統調用。linux

要增長一個命令也並不難,只須要~/LinuxKernel/linux-3.18.6/menu/test.c裏的main函數中添加下面一行,而後添加它的實現(須要定義在main函數前面)就能夠了。shell

MenuConfig("fork","Test system call fork",Fork);

最後一個參數Fork是一個函數指針,也就是咱們對它的定義:函數

int Fork(int argc, char *argv[])
{
    pid_t fpid;
    int count = 0;
    fpid = fork();
    printf("Now pid = %d\n", fpid);
    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("Now count = %d\n", count);
    return 0;
}

一樣的方法,也能夠添加ForkAsm函數與命令(在個人上一篇博文《Linux下嵌入彙編代碼調用API  using fork()》中就有Fork()與ForkAsm()的實現,只要改下函數名就行了)。測試

如今,咱們來經過gdb調試一下咱們剛剛添加的命令從調用到運行結束的過程:spa

qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S

打開另一個終端,啓動gdb,咱們來連接內核並添加斷點:指針

(gdb) file linux-3.18.6/vmlinux # 在gdb界面中targe remote以前加載符號表
(gdb) target remote:1234 # 創建gdb和gdbserver之間的鏈接,按c讓qemu上的Linux繼續運行
(gdb) break start_kernel # 斷點的設置能夠在target remote以前,也能夠在以後
(gdb) break sys_fork     # sys_fork是fork的系統調用入口

如今咱們按c讓內核啓動,啓動完成後咱們在help裏看一看fork和fork-asm有沒有被加進去:1調試

能夠看到,如今這兩個命令應該均可以用了,咱們來試一試fork-asm:2rest

能夠看到,系統調用sys_fork()時就到了咱們設置的斷點,咱們來繼續單步調試:3code

接下來咱們分析一下system_call的具體調用過程,詳細代碼見/kernel/entry_32.Sorm

這段彙編代碼較爲複雜,仍是來看一下簡化版本的吧:

.macro INTERRUPT_RETURN  ; 中斷返回
    iret
.endm
.macro SAVE_ALL          ; 保護現場
    ...
.macro RESTORE_INT_REGS
    ...
.endm
 
ENTRY(system_call)
    SAVE_ALL
syscall_call:
    call *sys_call_table(,%eax,4)
    movl %eax, PT_EAX(%esp)  ; store the return value
syscall exit:
    testl $_TIF_ALLWORK_MASK, %ecx # current->work
    jne syscall_exit_work
restore_all:
    RESTORE_INT_REGS
irq_return:
    INTERRUPT_RETURN      ; 到這裏就算執行完了
ENDPROC(system_call)
 
syscall_exit_work:
    testl  $_TIF_WORK_SYSCALL_EXIT, %ecx
    jz work_pending
END(syscall_exit_work)
 
work_pending:
    testb $_TIF_NEED_RESCHED, %cl
    jz work_notifysig
work_resched:
    call schedule
    jz restore_all
work_notifysig:
    ...                  ; deal with pending signals
END(work_pending)

畫了一個流程圖,見笑見笑~


總結:在系統調用結束返回(iret)以前,可能再次進行系統調度(call_schedule),調度過程當中還可能發生進程上下文與中斷上下文之間的切換。系統完成這一次調用後,會繼續檢查任務隊列,以後才執行iret返回。

陳政/arc001    原創做品轉載請註明出處  《Linux內核分析》MOOC課程

相關文章
相關標籤/搜索