最第一版本的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有沒有被加進去:調試
能夠看到,如今這兩個命令應該均可以用了,咱們來試一試fork-asm:rest
能夠看到,系統調用sys_fork()時就到了咱們設置的斷點,咱們來繼續單步調試:code
接下來咱們分析一下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課程