系统调用(int 0x80)详解
1、系統調用初始化
在系統啟動時,會在sched_init(void)函數中調用set_system_gate(0x80,&system_call),設置中斷向量號0x80的中斷描述符:
#define set_system_gate(n,addr) _set_gate(&idt[n],15,3,addr)
其中15表示此中斷號對應的是陷阱門,注意,這個中斷向量不是中斷門描述符。比如硬盤中斷(hd_interrupt)或定時器中斷(timer_interrupt)等硬件類的中斷才設置為中斷門描述符。陷阱門是可被中斷的。關于陷阱門與中斷門的區別,及陷阱門中DPL為0或3原因,請參考書本《linux 0.12》的14.19的解釋,還有第四章部分內容,這些已經解釋的很清楚了。
Int 0x80的輸入輸出參數說明:
輸入參數:eax=功能號(比如2為fork系統調用)
用功能對應sys_call_table[]的下標,比如sys_call_table[2]表示fork系統調用函數。
fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
返回值:EAX=sys_fork函數的返回值
2、system_call的實現
當調用_system_call函數時,系統就從用戶態進入了內核態。注意,特權變化了!對于x86系統,因為所有的寄存器都只有一個物理寄存器(ARM就不一要樣了),因為內核態與用戶態共享所有寄存器(段、通用、棧寄存器),比如SS、ESP、eflags、CS、EIP這五個寄存器。為了能夠從內核態返回到調用處繼續執行,當前現場,即相關寄存器的內容都需要被保存起來。
那么,這些現場信息保存到那里呢?為什么不能保存到用戶態堆棧,如果保存了用戶態堆棧,那么,這些棧內存區域,用戶程序就可以必改定,那么,程序就很容易被攻擊了,直接修改CS:EIP對應的棧內存,那么,你懂的^_^。
現場信息是保存在當前進程的內核態堆棧中,由于已經進入了內核態,在_system_call函數中執行push操作,此時被push的數據是存儲也就在內核棧中。SS、ESP、eflags、CS、EIP這五個寄存器的內容是怎么進入內核棧的呢?是程序從用戶態進入了內核態時,即在執行_system_call函數中的指令之前,硬件已經自動把SS、ESP、eflags、CS、EIP五個寄存器壓入了內核棧,然后,根據函數的需要,再保存相關通用、段之類寄存器。當從內核返回用戶態時,這五個寄存器會自動從內核棧中恢復。
_system_call部分代碼分析:
push %ds
push %es
push %fs
pushl %eax # save the orig_eax
pushl %edx
pushl %ecx # push %ebx,%ecx,%edx asparameters
pushl %ebx # to the system call
movl $0x10,%edx // ds、es此時指向當前進程的內核態數據段
mov %dx,%ds
mov %dx,%es
movl $0x17,%edx //即使沒這二行也行吧,fs本來就指向當前進程的用戶態數據段
mov %dx,%fs //因為在fork進程時,fs已經在copy_process函數中設置了。
call_sys_call_table(,%eax,4) //根據EAX傳入的功能號,即可調用相關系統函數
pushl %eax //系統調用函數的返回值入棧
關于進程狀態的變化,參考書上的說明,這部分,理解的還不夠,后續再分析???
當在中斷處理函數(陷阱門)中執行時,是可被中斷(中斷門)的,因為eflags標志中的TF被設置為允許中斷的。因而有可能在時鐘中斷函數(do_timer)中,本進程的時間片可能被修改為0。所以,當從系統調用相關功能號對應函數返回時,需要檢查當前進程是否還在就緒態,或時間片是否用完,并確認是否需要重新執行調度程序。
2: movl _current,%eax
cmpl $0,state(%eax) # state
jne reschedule
cmpl $0,counter(%eax) # counter
je reschedule
//系統調用返回時,會處理當前任務的信號,進程的信號識別與信號處理,僅在系統調用或時鐘中斷(每10ms)返回時。就能處理信號,優先級還是蠻高的,至少在進程執行流中,到少每10m就能處理信號。
ret_from_sys_call:
movl _current,%eax
cmpl _task,%eax # task[0] cannot havesignals
je 3f
cmpw $0x0f,CS(%esp) # was old code segment supervisor?
jne 3f
cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ?
jne 3f
movl signal(%eax),%ebx
movlblocked(%eax),%ecx
notl %ecx
andl %ebx,%ecx
bsfl %ecx,%ecx
je 3f
btrl %ecx,%ebx
movl %ebx,signal(%eax)
incl %ecx
pushl %ecx
call _do_signal //調用信號處理函數處理當前進程信號
popl %ecx
testl %eax, %eax
jne 2b # see if we need toswitchtasks, or do more signals
3: popl %eax
popl %ebx
popl %ecx
popl %edx
addl $4, %esp # skip orig_eax
pop %fs
pop %es
pop %ds
iret //此指令會將內核棧中的數據彈出到這5個寄存器SS、ESP、eflags、CS、EIP。
from:http://www.voidcn.com/blog/maowenl/article/p-1845345.html
總結
以上是生活随笔為你收集整理的系统调用(int 0x80)详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Smart Template tile的
- 下一篇: 因特网是属于哪种网络类型