標籤:
一、系統調用初始化
void __init trap_init(void){ ......set_system_gate(SYSCALL_VECTOR,&system_call);//0x80......} 對0x80中斷向量,設定了系統調用的總入口system_call。
static void __init set_system_gate(unsigned int n, void *addr){_set_gate(idt_table+n,15,3,addr);}
在IDT中設定了門描述符,如:
Selector為_KERNEL_CS。P為1;DPL為11;DT為0;TYPE為15,陷阱門。Offset就是異常處理函數的位移。
二、系統調用響應
系統調用都是在使用者態發生的。我們從使用者空間對函數sethostname()的調用開始我們的情景分析。
int sethostname(cost char *name, size_t len);
對sethostname.o反組譯碼得到如下結果:
41行,由於ebx的值一會改變,我們先把它暫存在edx中;
42行,把參數len存入寄存器ecx。
43行,把參數name存入寄存器ebx。
44行,把系統調用號存入寄存器eax。
45行,系統調用陷入核心。
46行,把暫存在edx中的值歸還給寄存器ebx。
47行,比較傳回值eax,看是否系統調用出錯。
48行,如果出錯,跳到出錯處。
49行,執行完sethostname,返回。
1、執行系統調用處理函數之前
系統調用一定發生在使用者態,int0x80後,形成如:
但要注意此時ORIG_EAX不在是插斷要求號,而是系統調用號。
(1)、CPU根據具體的中斷向量(本例中為0x80),從中斷向量表IDT中找到相應的表項,而該表項應該是一個陷阱門。 首先把使用者態堆棧的SS,使用者堆棧的ESP,EFLAGS,使用者空間的CS,EIP存入到系統堆棧中(從TSS中擷取)。
(2)、CPU根據陷阱門的設定到達了系統調用的總入口system_call。
ENTRY(system_call)pushl %eax//系統調用號壓入堆棧SAVE_ALL GET_CURRENT(%ebx) //將指向當前進程的task_struct結構的指標置入寄存器EBXcmpl $(NR_syscalls),%eaxjae badsystestb $0x02,tsk_ptrace(%ebx)# PT_TRACESYSjne tracesyscall *SYMBOL_NAME(sys_call_table)(,%eax,4) //在數組sys_call_table[]找到sethostname,並執行movl %eax,EAX(%esp)//系統調用的傳回值會存在eax中,堆棧中eax原來是系統調用號,現在該為系統調用傳回值ENTRY(ret_from_sys_call)#ifdef CONFIG_SMPmovl processor(%ebx),%eaxshll $CONFIG_X86_L1_CACHE_SHIFT,%eaxmovl SYMBOL_NAME(irq_stat)(,%eax),%ecx# softirq_activetestl SYMBOL_NAME(irq_stat)+4(,%eax),%ecx# softirq_mask#elsemovl SYMBOL_NAME(irq_stat),%ecx# softirq_activetestl SYMBOL_NAME(irq_stat)+4,%ecx# softirq_mask#endifjne handle_softirqret_with_reschedule:cmpl $0,need_resched(%ebx)jne reschedulecmpl $0,sigpending(%ebx)jne signal_returnrestore_all:RESTORE_ALL
SAVE_ALL如下:
#define SAVE_ALL cld; pushl %es; pushl %ds; pushl %eax; pushl %ebp; pushl %edi; pushl %esi; pushl %edx; pushl %ecx; pushl %ebx; movl $(__KERNEL_DS),%edx; movl %edx,%ds; movl %edx,%es;
sethostname,需要傳遞的參數是兩個,分別是在%ebx和%eax中。在SAVE_ALL中%ebx是最後壓入堆棧的,%eax次之。所以堆棧中%ebx的內容就成為參數1,而%ecx的內容就是參數2,傳遞給sethostname。回到SAVE_ALL去看一下,可以看到被壓入堆棧的寄存器依次為:%es、%ds、%eax、%ebp、%edi、%esi、%edx、%ecx、%ebx。這裡的%eax持有系統調用號,顯然不能再用來傳遞參數;而%ebp是用作子程式調用過程中“幀”指標,也不能用來傳遞參數,這樣,實際上就只有最後5個寄存器可以用來傳遞參數,所以,在系統調用中獨立傳遞的參數不能超過5個。
2、執行系統調用處理函數
asmlinkage long sys_sethostname(char *name, int len)//name就是壓入堆棧的ebx,len就是壓入堆棧的ecx{int errno;if (!capable(CAP_SYS_ADMIN))return -EPERM;if (len < 0 || len > __NEW_UTS_LEN)return -EINVAL;down_write(&uts_sem);errno = -EFAULT;if (!copy_from_user(system_utsname.nodename, name, len)) {system_utsname.nodename[len] = 0;errno = 0;}up_write(&uts_sem);return errno;}
3、執行系統調用處理函數之後
ENTRY(system_call)pushl %eax//系統調用號壓入堆棧SAVE_ALL GET_CURRENT(%ebx) //將指向當前進程的task_struct結構的指標置入寄存器EBXcmpl $(NR_syscalls),%eaxjae badsystestb $0x02,tsk_ptrace(%ebx)# PT_TRACESYSjne tracesyscall *SYMBOL_NAME(sys_call_table)(,%eax,4) //在數組sys_call_table[]找到sethostname,並執行movl %eax,EAX(%esp)//系統調用的傳回值會存在eax中,堆棧中eax原來是系統調用號,現在該為系統調用傳回值ENTRY(ret_from_sys_call)#ifdef CONFIG_SMPmovl processor(%ebx),%eaxshll $CONFIG_X86_L1_CACHE_SHIFT,%eaxmovl SYMBOL_NAME(irq_stat)(,%eax),%ecx# softirq_activetestl SYMBOL_NAME(irq_stat)+4(,%eax),%ecx# softirq_mask#elsemovl SYMBOL_NAME(irq_stat),%ecx# softirq_activetestl SYMBOL_NAME(irq_stat)+4,%ecx# softirq_mask#endifjne handle_softirq //先處理非強制中斷ret_with_reschedule:cmpl $0,need_resched(%ebx) //是否需要調度jne reschedulecmpl $0,sigpending(%ebx) //是否有訊號需要處理jne signal_returnrestore_all:RESTORE_ALL
由於系統調用發生在使用者態,所以不用像中斷和異常那樣,檢查是否發生在使用者態。
Linux核心原始碼情景分析-系統調用