fork系統調用過程

又是查找資料,又是看源碼,折騰了大半天,終於把fork的過程弄完了,但是後面的跟蹤狀態還不太懂,等具體後面弄清楚了,再加上。

內核是2.6.11版本的。

fork()系統調用:

我們運行一個系統調用時,系統將調用宏指令_syscall0
    #define _syscall0(type,name) \
    type name(void) \
    { \
      register long __a __asm__ ("r10"); \
      register long __n_ __asm__ ("r9") = (__NR_##name); \
      __asm__ __volatile__ (".ifnc %0%1,$r10$r9\n\t" \
                ".err\n\t" \
                ".endif\n\t" \
                "break 13" \
                : "=r" (__a) \
                : "r" (__n_)); \
      if (__a >= 0) \
         return (type) __a; \
      errno = -__a; \
      return (type) -1; \
    }
進而,調用0x80號中斷,中斷0x80 把調用(控制)傳給核心入口地址中的system_call()負責保護所有的寄存器,並檢查系統調用是否合法,如果合法那麼根據從sys_call_table中找出的偏移量,把控制權轉給真正的系統。

當用戶調用INT 0x80而進入system_call函數後,首先檢查用來存放系統調用編號的eax的值是否超出IDT表的項數NR_syscalls如沒有超出的話,就根據eax的值從系統調用表(sys_call_table)中得到對應的系統調用入口,並通過call 指令轉入各個具體函數(sys_*)的處理過程。)


對esp和eip進行處理,使其指向內核棧。然後把寄存器eax中的系統調用號入棧。
然後當切換到到內核態後,內核根據系統調用號來查找到對應的系統調用處理例程的
函數名(sys_fork),從而找到對應的代碼入口址。
long sys_fork(void)
{
    long ret;

    current->thread.forking = 1;
        ret = do_fork(SIGCHLD, 0, NULL, 0, NULL, NULL);
    current->thread.forking = 0;
    return(ret);
}
這裏面current->thread.forking 記錄了進程複製的過程,與複製本身無關。

 sys_fork(void)中調用do_fork()函數。


 long do_fork(unsigned long clone_flags,
      unsigned long stack_start,
      struct pt_regs *regs,
      unsigned long stack_size,
      int __user *parent_tidptr,
      iner *cild_tidptr )


do_fork()中首先通過查找pidmap_array位圖,爲子進程分配新的pid

long pid = alloc_pidmap();

copy_process複製進程描述符.如果所有必須的資源都是可用的,該函數返回剛創建
的task_struct描述符的地址. 這是創建進程的關鍵步驟.
struct task_struct *p;
p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, pid);
如果父子進程運行在同一個CPU上,並且不能共享同一組頁表(CLONE_VM標誌被清0).那麼,就把子進程插入父進程運行隊列.
並且子進程插在父進程之前.這樣做的目的是:如果子進程在創建之後執行新程序,就可以避免寫時複製機制執行不必要時頁面複製.
否則,如果運行在不同的CPU上,或者父子進程共享同一組頁表.就把子進程插入父進程運行隊列的隊尾.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章