System call 是透過中斷來呼叫,而在 x86 系統的架構中,32-255 是所謂的 maskable interrupts 即使用者定義的中斷。Linux 在 i386 上實作system call可透過以下 2 個機制:
˙ lcall7/lcall27(call gates,呼叫閘道)
˙ int 0x80(software interrupt.軟體中斷)
Linux 應用程式使用 int 指令來觸發 0x80 號軟體中斷,其它作業系統像是 Solaris 的應用程式,則是使用 lcall7。在開機時,IDT是由arch/i386/kernel/traps.c:trap_init() 做初始化的設定:
void __init trap_init(void) { #ifdef CONFIG_EISA if (isa_readl(0x0FFFD9) == 'E'+('I'<<8)+('S'<<16)+('A'<<24)) EISA_bus = 1; #endif #ifdef CONFIG_X86_LOCAL_APIC init_apic_mappings(); #endif set_trap_gate(0,÷_error); set_trap_gate(1,&debug); set_intr_gate(2,&nmi); set_system_gate(3,&int3); /* int3-5 can be called from all */ set_system_gate(4,&overflow); set_system_gate(5,&bounds); set_trap_gate(6,&invalid_op); set_trap_gate(7,&device_not_available); set_trap_gate(8,&double_fault); set_trap_gate(9,&coprocessor_segment_overrun); set_trap_gate(10,&invalid_TSS); set_trap_gate(11,&segment_not_present); set_trap_gate(12,&stack_segment); set_trap_gate(13,&general_protection); set_intr_gate(14,&page_fault); set_trap_gate(15,&spurious_interrupt_bug); set_trap_gate(16,&coprocessor_error); set_trap_gate(17,&alignment_check); set_trap_gate(18,&machine_check); set_trap_gate(19,&simd_coprocessor_error); set_system_gate(SYSCALL_VECTOR,&system_call); /* * default LDT is a single-entry callgate to lcall7 for iBCS * and a callgate to lcall27 for Solaris/x86 binaries */ set_call_gate(&default_ldt[0],lcall7); set_call_gate(&default_ldt[4],lcall27); /* * Should be a barrier for any external CPU state. */ cpu_init(); #ifdef CONFIG_X86_VISWS_APIC superio_init(); lithium_init(); cobalt_init(); #endif }
由以上的程式碼可以看出,0x80 號中斷向量會指到 system_call 進入點的位址。system_call 位於 arch/i386/kernel/entry.S:
ENTRY(system_call) pushl %eax # save orig_eax SAVE_ALL GET_CURRENT(%ebx) testb $0x02,tsk_ptrace(%ebx) # PT_TRACESYS jne tracesys cmpl $(NR_syscalls),%eax jae badsys call *SYMBOL_NAME(sys_call_table)(,%eax,4) movl %eax,EAX(%esp) # save the return value ENTRY(ret_from_sys_call) cli # need_resched and signals atomic test cmpl $0,need_resched(%ebx) jne reschedule cmpl $0,sigpending(%ebx) jne signal_return
0x80 號中斷裡有很多 system call service routine,每個 system call service routine 的位址是經由 sys_call_table 查表得知。要呼叫 system call 時,必須告訴 Linux kernel 該 system call 的編號。System call 的編號透過 %eax 暫存器來傳遞給 Linux kernel。
例如 sys_open 這個 system call 的編號為 5。因此當我們呼叫 sys_open() 時,就要指定 %eax 暫存器的值為5。
Jollen's Blog 使用 Github issues 與讀者交流討論。請點擊上方的文章專屬 issue,或 open a new issue
您可透過電子郵件 jollen@jollen.org,或是 Linkedin 與我連絡。更歡迎使用微信,請搜尋 WeChat ID:jollentw