2011年10月6日 星期四

System Call

System Call簡介
System call 是 process 與OS之間的介面,由Linux kernel實做出來給user使用,system call提供user
programm和os溝通的界面,當user program需要os的服務時,user program便使用system call


System Call 流程圖




流程:
system call會伴隨一個trap(在Linux下會跳到int 0x80),此時系統將mode bit由user mode改成
monitor mode(1->0)並查尋trap vector找尋相對應trap service routine
(此時可做context switch 0->1)
執行完此routine發出interrupt告訴os已經完成




User program 與 wrapper routine 是 user space 的 code
system call handler 與 service routine 則是屬於 kernel space
從 User space 切換到 kernel space 是透過中斷
底下的用link這個system call當例子,解釋程式碼

syscall_table.S
PATH : /usr/src/linux/arch/i386/kernel/syscall_table.S
在 Linux 中, 每個 system call 都有自己獨有的號碼。
當 user-space 執行一個 system call 時,process是去參考 syscall 的號碼而不是名字。



unistd.h
PATH : /usr/src/linux/include/asm/unistd.h
unistd.h 是一個重要的標頭檔,裡頭是 system call 編號的定義,當 system call 發生時,system call 的號碼將透過 register (EAX) 傳給 kernel。





#define _syscall2(type,name,type1,arg1,type2,arg2) 
type name(type1 arg1,type2 arg2) 
{ 
long __res; 
__asm__ volatile ("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" 
         : "=a" (__res) 
         : "0" (__NR_##name),"ri" ((long)(arg1)),"c" ((long)(arg2)) 
         : "memory"); 
__syscall_return(type,__res); 
}

unistd.h 也定義了不同參數的 system call handler,上面的程式碼是處理 2 個參數的 handler
這是一個 macro,當遇到系統呼叫的時候,就會被展開。

參數傳遞與傳回值
system call 的編號透過 %eax 暫存器來指定;若要傳遞參數,則是透過其它暫存器來傳遞(最上面的流程圖上有標示)
Linux system call 最多可傳遞6個參數,參數的傳遞是透過以下的暫存器來完成:
%ebx:第1個參數。
%ecx:第2個參數。
%edx:第3個參數。
%esi:第4個參數。
%edi:第5個參數。
%ebp:第6個參數(做臨時用途)。

x86 的 Interrupt
x86 的interrupt(中斷)可分為系統定義與使用者自訂:
中斷向量0~8、10~14、16~18:predefined interrupts and exceptions。
中斷向量19-31:保留。
中斷向量32-255:user-defined interrupts(maskable interrupts)。

當從 shell 執行 link 時,0x80 號中斷向量會指到 system_call 進入點的位址,由於 link 有兩個參數,const char* oldfile 和 const char* newfile,因此 shell 會執行 syscall2(int link, const char* oldname, const char* newname),執行 sys_link(),sys_link()會呼叫linkat()。在 linux 中,目錄和檔案在系統中被視為同樣。

int link(const char* oldname, const char* newname)
{
    long _res;
    _asm_  volatile(“int $0x80”
         :”=a”(_res)
         :”0”(_NR_link),”b”((long)(oldname)),
         “c”((long)(newname)));
    do{
        if((unsigned long)(_res)>=(unsigned long)(-(128+1))){
            errno=-(_res);
            _res=-1;
    }
    return (int)(_res);
    }while(0);
}

下面這段組語是 system call 的進入點,也就是 system call table
PATH : /usr/src/linux/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

syscalls.h
PATH : /usr/src/linux/include/linux/syscalls.h
這個檔案包含了 system call 的宣告。





asmlinkage 是在i386 system call實做中,gcc 很重要的一個標籤。他是一個macro,會被展開成
#define asmlinkage __attribute__((regparm(0)))
這是/usr/include/asm/linkage.h 裡頭的定義,regparm(0)表示不使用register傳遞參數
如此一來所有的參數就會被強迫放在stack當中

這麼做的原因是因為system call handler是assembly code,但是system call routine是C code
為了要保證當system call handler呼叫相對應的system call routine時,符合C語言參數傳遞的規則
是以 stack 方式傳參數,在C function的 prototype前面就要加上 "asmlinkage"。

System call 結束
當 system call 執行完的時候,最後會執行 ret_from_sys_call() 離開。
最後回到 syscallX() 中去。在 syscallX() 中,檢測是否有錯誤碼,然後返回。

2 則留言:

  1. Dear Specialist

    可否請教您這方面的知識,可以參考哪些書籍呢?
    謝謝您

    回覆刪除
    回覆
    1. Understanding the Linux Kernel, by Daniel P. Bovet
      Linux Device Drivers, by Jonathan Corbet
      Linux Kernel Development, by Robert Love
      有關Linux kernel的部份我主要是看這些書
      可以參考看看~

      刪除