複習---Linux系統調用

一、系統調用知識
####Linux內核在系統調用時是通過寄存器而不是通過堆棧傳遞參數的,顯然能夠通過寄存器傳遞的信息量並不大,所以傳遞的參數大多是指針,這樣才能通過指針找到更大的數據塊
####從內核中可以直接訪問當前進程的用戶空間,所使用的虛擬地址也與當前進程處於用戶空間時的地址完全相同,但是,反過來就不可以了
####文件include/asm-i386./unstd.h上爲每個系統調用定義了一個唯一的編號 ----》系統調用號
####系統調用跳轉表sys_call_table[]是一個函數指針數組,跳轉時以系統調用號爲下標在數組中找到相應的函數指針。(entry.s)
####凡是內核不支持的系統調用號全部都指向sys_ni_syscall(),這個函數只是返回一個出錯代碼:-ENOSYS,表示該系統調用尚未實現
####系統調用的參數也是通過寄存器傳給內核的,在x86系統上,系統調用的前5個參數放在ebx,ecx,edx,esi和edi中,如果參數多的話,還需要用個單獨的寄存器存放指向所有參數在用戶空間地址的指針。
####爲Linux添加新的系統調用是件相對容易的事情,主要包括有4個步驟:編寫系統調用服務例程;添加系統調用號;修改系統調用表;重新編譯內核並測試新添加的系統調用。

Linux用戶態到內核態之間的切換示意圖(在X86下,系統調用由0x80號中斷完成)



二、系統調用源碼剖析
在Unistd.h (include\asm-i386)文件中,定義了288個系統調用號以及7個_syscall宏
####_syscall宏(7個)
_syscall0(type,name);
_syscall1(type,name,type1,arg1);
_syscall2(type,name,type1,arg1,type2,arg2); _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3); _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4); _syscall5type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5); _syscall6type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5,type6,arg6);
其中,type表示所生成系統調用的返回值類型,name表示該系統調用的名稱,typeN、argN分別表示第N個參數的類型和名稱,它們的數目和_syscall後面的數字一樣大。這些宏的作用是創建名爲name的函數,_syscall後面跟的數字指明瞭該函數的參數的個數。
分析一個最簡單的宏(沒有任何參數的宏)
#define _syscall0(type,name) \
type name(void) \
{ \
long __res; \    
__asm__ volatile ("int $0x80" \      //80號中斷
    : "=a" (__res) \      
   ///例如執行fork(),轉到內核態以後執行完的結果會放入eax寄存器中,將eax的值存放在_res中
    : "0" (__NR_##name)); \      
   //例如執行fork(),匹配成_NR##fork宏,替換成fork()的系統調用號存放在eax中
__syscall_return(type,__res); \
}
關於進程創建的三個系統調用:
fork() 全部複製,父進程所有的資源全部通過數據結構的複製遺傳給子進程
clone() 可以將資源有選擇性的複製給子進程,而沒有複製的數據結構則通過指針的複製讓子進程共享
vfork() 創建線程,除PCB和系統空間堆棧以外的資源全都通過數據結構指針的複製遺傳

系統函數pid_t fork(void); 調用內核中的 asmlinkage int sys_fork(struct pt_regs regs);
regs指向通用寄存器的值,是在從用戶態切換到內核態時被保存在內核堆棧中的。
asmlinkage int sys_fork(struct pt_regs regs)
{
  return do_fork(SIGCHLD, regs.esp, &regs, 0, NULL, NULL);
}
long do_fork(unsigned long clone_flags,
  unsigned long stack_start,
  struct pt_regs *regs,
  unsigned long stack_size,
  int __user *parent_tidptr,
  int __user *child_tidptr)
do_fork()中調用的一個至關重要的函數copy_process函數的分析:
1、調用dup_task_struct()爲子進程創建一個內核棧、thread_info結構和task_struct,這些值與當前進程的值相同。此時子進程和父進程的描述符是完全相同的。
p = dup_task_struct(current)---->(struct task_struct *tsk---------->tsk = alloc_task_struct()從slab層分配了一個關於進程描述符的slab)
2、檢查並確保新創建這個子進程後,當前用戶所擁有的進程數目沒有超出給它分配的資源的限制。
3、子進程着手使自己與父進程區別開來,爲進程的task_struct、tss做個性化設置,進程描述符內的許多成員都要被清0或設置爲初始值。那些不是繼承而來的進程描述符成員,主要是統計信息。task_struct中的大多數數據都依然未被修改。
4、爲子進程創建第一個頁表,將進程0的頁表項內容賦給這個頁表。
copy_process()————>copy_fs():爲子進程複製父進程的頁目錄項
,_copy_fs_struct(current->fs)中current指針表示當前進程也就是父進程的
關於fork()系統調用,可以參考http://www.cnblogs.com/qiuheng/p/5752284.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章