本篇爲網易雲課堂Linux內核分析課程的第五週做業,上一次做業中我以2個系統調用(getpid
, open
)做爲分析實例來分析系統調用的過程,本篇中我將深刻到system_call(彙編級別代碼)中來分析其執行過程.vim
system_call
, 系統調用
運行環境:異步
從上一次課後,咱們對於系統調用在執行過程當中的一些基本狀況有了一個比較抽象的認識,一個系統調用的基本過程是用戶態程序經過int 0x80
中斷向量指令實現從用戶態進入內核態,系統調用過程當中,eax
寄存器負責傳遞系統調用號,ebx
,ecx
等其餘寄存器負責傳遞其餘參數,可是對於執行完了int 0x80
以後,在內核態時:測試
中斷
中斷分爲2種":atom
- 可屏蔽中斷: I/O設備發出的全部的中斷請求(IRQ)都產生可屏蔽中斷。可屏蔽中斷產生兩種狀態:屏蔽的(masked)或非屏蔽的(unmasked);當中斷被屏蔽,則CPU控制單元就忽略它。
- 非可屏蔽中斷:老是由CPU辨認。只有幾個危急事件引發非屏蔽中斷。
進程上下文
通常來講,CPU在任什麼時候刻都處於如下三種狀況之一:spa
- 運行於用戶空間,執行用戶進程;
- 運行於內核空間,處於進程上下文;
- 運行於內核空間,處於中斷上下文。
應用程序經過系統調用陷入內核,此時處於進程上下文。現代幾乎全部的CPU體系結構都支持中斷。當外部設備產生中斷,向CPU發送一個異步信號,CPU調用相應的中斷處理程序來處理該中斷,此時CPU處於中斷上下文。
在進程上下文中,能夠經過current關聯相應的任務。進程以進程上下文的形式運行在內核空間,能夠發生睡眠,因此在進程上下文中,可使做信號量(semaphore)。實際上,內核常常在進程上下文中使用信號量來完成任務之間的同步,固然也可使用鎖。
中斷上下文不屬於任何進程,它與current沒有任何關係(儘管此時current指向被中斷的進程)。因爲沒有進程背景,在中斷上下文中不能發生睡眠,不然又如何對它進行調度。因此在中斷上下文中只能使用鎖進行同步,正是由於這個緣由,中斷上下文也叫作原子上下文(atomic context)(關於同步之後再詳細討論)。在中斷處理程序中,一般會禁止同一中斷,甚至會禁止整個本地中斷,因此中斷處理程序應該儘量迅速,因此又把中斷處理分紅上部和下部。
相對於進程而言,就是進程執行時的環境。具體來講就是各個變量和數據,包括全部的寄存器變量、進程打開的文件、內存信息等。一個進程的上下文能夠分爲三個部分:用戶級上下文、寄存器上下文以及系統級上下文操作系統
- 用戶級上下文: 正文、數據、用戶堆棧以及共享存儲區;
- 寄存器上下文: 通用寄存器、程序寄存器(IP)、處理器狀態寄存器(EFLAGS)、棧指針(ESP);
- 系統級上下文: 進程控制塊task_struct、內存管理信息(mm_struct、vm_area_struct、pgd、pte)、內核棧。
中斷上下文與進程上下文
硬件經過觸發信號,致使內核調用中斷處理程序,進入內核空間。這個過程當中,硬件的 一些變量和參數也要傳遞給內核,內核經過這些參數進行中斷處理。所謂的「 中斷上下文」,其實也能夠看做就是硬件傳遞過來的這些參數和內核須要保存的一些其餘環境(主要是當前被打斷執行的進程環境)。中斷時,內核不表明任何進程運行,它通常只訪問系統空間,而不會訪問進程空間,內核在中斷上下文中執行時通常不會阻塞.設計
# system call handler stub ENTRY(system_call) RING0_INT_FRAME # can't unwind into user space anyway ASM_CLAC pushl_cfi %eax # save orig_eax SAVE_ALL // 保存系統寄存器信息 GET_THREAD_INFO(%ebp) // 獲取thread_info結構的信息 # system call tracing in operation / emulation testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp) // 測試是否有系統跟蹤 jnz syscall_trace_entry // 若是有系統跟蹤,先執行,而後再回來 cmpl $(NR_syscalls), %eax // 比較eax中的系統調用號和最大syscall,超過則無效 jae syscall_badsys // 無效的系統調用 直接返回 syscall_call: call *sys_call_table(,%eax,4) // 調用實際的系統調用程序 syscall_after_call: movl %eax,PT_EAX(%esp) // 將系統調用的返回值eax存儲在棧中 syscall_exit: 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 testl $_TIF_ALLWORK_MASK, %ecx //檢測是否全部工做已完成 jne syscall_exit_work //工做已經完成,則去進行系統調用推出工做 restore_all: TRACE_IRQS_IRET // iret 從系統調用返回
System_Call的基本處理流程爲:指針
call *sys_call_table(,%eax,4)
經過系統查詢系統調用查到相應的系統調用程序地址,執行相應的系統調用syscall_exit_work
(完成系統調用退出工做)接下來對於中間比較關鍵的片斷代碼進行重點分析rest
syscall_exit_work: testl $_TIF_WORK_SYSCALL_EXIT, %ecx //測試syscall的工做完成 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 //返回用戶空間,只須要檢查need_resched END(syscall_exit_work)
該過程爲系統調用完成後如何退出調用的過程,其中比較重要的是work_pending
,詳見以下:code
work_pending: testb $_TIF_NEED_RESCHED, %cl // 判斷是否須要調度 jz work_notifysig // 不須要則跳轉到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 // 是否全部工做都已經作完 jz restore_all // 是則退出 testb $_TIF_NEED_RESCHED, %cl // 測試是否須要調度 jnz work_resched // 從新執行調度代碼 work_notifysig: // 處理未決信號集 #ifdef CONFIG_VM86 testl $X86_EFLAGS_VM, PT_EFLAGS(%esp) // 判斷是否在虛擬8086模式下 movl %esp, %eax jne work_notifysig_v86 // 返回到內核空間 1: #else movl %esp, %eax #endif TRACE_IRQS_ON // 啓動跟蹤中斷請求響應 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 // 將信號投遞到進程 jmp resume_userspace // 恢復用戶空間 #ifdef CONFIG_VM86 ALIGN work_notifysig_v86: pushl_cfi %ecx # save ti_flags for do_notify_resume call save_v86_state // 保存VM86模式下的CPU信息 popl_cfi %ecx movl %eax, %esp jmp 1b #endif END(work_pending)
首先是work_pending
這段彙編邏輯:
work_notifying
,處理信號而後是work_notifysig
的這段彙編邏輯:
最後返回系統調用
系統調用中斷本質上是一個保存當前工做狀態,而後處理,最後返回而且恢復進程的過程.
吳欣偉 原創做品轉載請註明出處:《Linux內核分析》MOOC課程:http://mooc.study.163.com/course/USTC-1000029000