天天看點

《Linux核心分析》第五周

20135103王海甯

《Linux核心分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000

這周的實驗在上周實驗四的基礎上,進一步的操作:

1.将系統調用函數getpid指令加入menuos中

2.通過gdb跟蹤sys_getpid系統調用執行的完整過程

步驟:給MenuOS增加getpid和getpid-asm指令

0)更新menu代碼到最新版

1)在main函數中增加MenuConfig

2)增加對應的getpid函數和getpid-asm函數

3)make rootfs

進入實驗樓環境後,敲入如下指令:

[plain] view plain copy

  1. <span style="font-size:14px;">cd LinuxKernel  
  2. cd menu  
  3. vi test.c</span>  

進入test.c源程式後添加如下代碼:

[cpp] view plain copy

  1. <span style="font-size:14px;">//添加兩個函數,别忘了加頭檔案#include <unistd.h>  
  2. int main() {  
  3.     pid_t tt;  
  4.     asm volatile (  
  5.             "movl $0x20, %%eax\n\t"  
  6.             "int $0x80\n\t"  
  7.             "movl %%eax, %0\n\t"  
  8.             :"=m"(tt)  
  9.             );  
  10.     printf("%u\n", tt);  
  11.     return 0;  
  12. }  
  13.     tt = getpid();  
  14. }</span>  
  1. <span style="font-size:14px;">//然後在main函數中添加  
  2. MenuConfig("time","Show System Time",Time);  
  3. MenuConfig("time-asm","Show System Time(asm)",TimeAsm);</span>  

代碼添加完成後make rootfs重新編譯,此時系統會自動啟動。如下圖:

下面用gdb跟蹤sys_getpid執行的過程:

1.執行以下指令開啟核心的調試功能 qemu -kernel linux-3.18.6/arch/x86/boot/bz/Image -initrd rootfs.img -s -S,此時系統處于停止狀态

2.再打開一個指令行視窗輸入gdb,在gdb指令提示符下依次輸入file linux-3.18.6/vmlinux, target remote:1234指令連接配接核心并跟蹤調試

3.設定斷點break sys_getpid, 接着continue開始運作,此時menuOS從stopped狀态開始執行。在menu程式的提示符下輸入who,程式執行到斷點時暫停,此時gdb視窗顯示程式斷在sys_getpid處

4.接着使用gdb單步執行指令。next:不進入函數體的單步執行;step:進入函數體的單步執行;finish:進入函數體後退回調用函數

分析

中斷相關的初始化代碼是通過linux-3.18.6/init/main.c檔案中的start_kernel函數裡的trap_init()初始化的。執行int $0x80指令後核心開始執行system_call入口處開始的代碼,位于entry_32.S彙編檔案中。

下面分析system_call彙編代碼:

1.SAVE ALL                                // 儲存調用前寄存器相關的資訊

2.call *sys_call_table(,%eax,4)   // 執行系統調用對應的處理函數,eax存放系統調用号

                                                    // 通過linux-3.18.6/arch/x86/syscalls/syscall_32.tbl找到系統調用号對應處理函數

3.movl %eax,PT_EAX(%esp)     // 儲存系統調用處理函數傳回值到exa

4. testl $_TIF_ALLWORK_MASK, %ecx    # current->work

    jne syscall_exit_work  

                                                   // 這兩句檢查調用退出前是否有其他工作要處理,如有則跳到syscall_exit_work處繼續處理,以下是syscall_exit_work相關代碼:

syscall_exit_work:

    testl $_TIF_WORK_SYSCALL_EXIT, %ecx    // 測試是否退出前還有工作要處理,如有則跳到work_pending

    jz work_pending           

    TRACE_IRQS_ON

    ENABLE_INTERRUPTS(CLBR_ANY)    # could let syscall_trace_leave() call

                    # schedule() instead

    movl %esp, %eax

    call syscall_trace_leave

    jmp resume_userspace

END(syscall_exit_work)

5.下面是work_pending的相關代碼,在注釋中解釋相關内容

work_pending:

    testb $_TIF_NEED_RESCHED, %cl       // 是否有要繼續排程的相關信号

    jz work_notifysig      #跳轉到處理信号相關的代碼處

work_resched:

    call schedule                                         // 時間排程,程序排程的時機在這裡處理

    LOCKDEP_SYS_EXIT

    DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt

                    # setting need_resched or sigpending

                    # between sampling and the iret

    TRACE_IRQS_OFF

    movl TI_flags(%ebp), %ecx

    andl $_TIF_WORK_MASK, %ecx    # is there any work to be done other  // 是否有其他工作要處理

                                                          # than syscall tracing?

    jz restore_all                // 如果沒有則恢複中斷上下文,即恢複進入之前儲存的寄存器内容

    testb $_TIF_NEED_RESCHED, %cl

    jnz work_resched

work_notifysig:                # deal with pending signals and    // 處理相關信号代碼

                                      # notify-resume requests

#ifdef CONFIG_VM86

    testl $X86_EFLAGS_VM, PT_EFLAGS(%esp)

    jne work_notifysig_v86        # returning to kernel-space or

                                              # vm86-space

1:

#else

#endif

    ENABLE_INTERRUPTS(CLBR_NONE)

    movb PT_CS(%esp), %bl

    andb $SEGMENT_RPL_MASK, %bl

    cmpb $USER_RPL, %bl

    jb resume_kernel

    xorl %edx, %edx

    call do_notify_resume

    ALIGN

work_notifysig_v86:

    pushl_cfi %ecx               # save ti_flags for do_notify_resume

    call save_v86_state        # %eax contains pt_regs pointer

    popl_cfi %ecx

    movl %eax, %esp

    jmp 1b

END(work_pending)

6. restore_all:

       RESTORE_INT_REGS     // 中斷傳回之前恢複相關寄存器的内容

7.     irq_return:

      INTERRUPT_RETURN     // 這兩行代碼主要是傳回到使用者态

總結

1.執行int 0x80指令後系統從使用者态進入核心态,跳到system_call()函數處執行相應服務程序。在此過程中核心先儲存中斷環境,然後執行系統調用函數。

2.system_call()函數通過系統調用号查找系統調用表sys_cal_table來查找具體系統調用服務程序。

3.執行完系統調用後,iret之前,核心會檢查是否有新的中斷産生、是否需要程序切換、是否學要處理其它程序發送過來的信号等。 

4.核心是處理各種系統調用的中斷集合,通過中斷機制實作程序上下文的切換,通過系統調用管理整個計算機軟硬體資源。

5.如沒有新的中斷,restore儲存的中斷環境并傳回使用者态完成一個系統調用過程。

上一篇: 第四周
下一篇: 第六周