Linux fork原始碼剖析

2021-09-23 13:37:26 字數 3774 閱讀 2535

fork是複製程序,那麼首先要清楚程序是什麼?

程序是乙個正在執行的程式,是資源分配的最小單位,系統管理程序是依靠對程序控制塊(pcb)的管理完成的,每個程序的產生分兩步,一是:分配pcb,二是 :準備程序實體,如分配記憶體空間等 .

值得注意的是 :

有三個建立程序的函式fork(),vfork(),clone(),呼叫時通過中斷(128號中斷 , 也稱0x80 中斷 , 是系統呼叫所用的中斷),從使用者態進入核心態,進入系統呼叫,根據呼叫號不同進入對應系統呼叫函式 sys_fork() , sys_vfork() , sys_clone() ;但這三個系統函式底層呼叫都是do_fork();只是傳的引數,和標誌不同.

注:通過128號終端(即0x80 中斷), 從使用者態切到核心態 , 進入系統呼叫處理

大致的流程就是 :

allco_pidmap()給子程序分配pid,task_struct分配程序描述符,呼叫了copy_process()來實現父程序到子程序的複製 .父程序的時間片分成了兩份,父、子各佔一半 .再用set_links§把子程序放入核心程序佇列(插在父程序之前),即就是放到了就緒佇列上等待。最後將新程序pid插到pidhash雜湊表中 . 這樣乙個程序就fork()好了 .

我們對於do_fork() 轉到定義:

do_fork():

定義pcb指標struct task_struct *p;

分配pid,(cat /proc/sys/kernel/pid_max命令可以檢視乙個系統支援的最大程序數) ,程序數的範圍0~32768,理論值.

呼叫alloc_pidmap方法 , 為子程序分配pid

呼叫copy_process方法,建立子程序的task_struct. copy_process複製程序描述符.如果所有必須的資源都是可用的,該函式返回剛建立的task_struct描述符的位址.

然後進入copy_process();

p=dup_task_struct(current)之前都是對程序的一些判斷(檢查標誌位合法性)和安全性檢查 .

呼叫dup_task_struct 為子程序獲取程序描述符 , 跳轉進去 .

static struct task_struct *dup_task_struct(struct task_struct *orig); (為子程序獲取程序描述符)

分配pcb,繼承父程序的pcb中的值,只是將特有的資訊改過來。每個程序都有task_thread,thread_info結構體儲存的是程序上下文的資訊 .

要修改thread_info *info,子程序的task_struct的成員struct thread_info *info指向自己的struct thread_info , 而且struct thread_info結構體的成員struct task_struct *p指向子程序自己的struct task_struct .即

然後跳回到copy_process()中

其中copy_mm(clone_flags, p)複製位址空間,struct mm_struct *mm,*active_mm,mm表示:程序所擁有的記憶體空間的描述符,對於核心執行緒的mm為null,active_mm表示:程序執行時所使用的程序描述符.

注意 :在copy_mm(clone_flags,p); 中 , 對使用者空間進行複製,同時利用dup_mmap(struct mm_struct* mm)對深層的vm_area_struct和頁面對映表也進行了複製 .

那麼建立的是進城還是執行緒?clone_vm為 0 時建立的是新程序(子程序),子程序複製了父程序的頁面目錄項;clone_vm為1時通過複製的指標共享了父程序空間,只通過copy_page_range()複製的是父程序頁面表項,而不是頁面目錄項 .如果說這裡生成了子程序,那就會有寫時拷貝的存在了 .

寫時拷貝 :一種可以推遲拷貝甚至免除拷貝資料的技術 .

這裡存在乙個寫時拷貝write_on_copy , 使用寫時拷貝 , 核心並不複製整個程序位址空間,而是讓父子程序共享同乙個拷貝 .只有當兩個程序中任何乙個程序要進行寫入操作時 , 此刻就會拷貝乙份父程序資源給子程序 .

寫時拷貝本質是將位址空間上的頁的拷貝推遲到實際發生寫入的時候 .

fork()的實際開銷:複製父程序的頁表(頁表是一種特殊的資料結構,放在系統空間的頁表區,存放邏輯頁與物理頁幀的對應關係)以及給子程序建立唯一的程序描述符。

至此 , task_struct中低端(使用者空間)copy完成 .

我們接著回到copy_process()中

呼叫copy_thread,用發出clone系統呼叫時cpu暫存器的值(它們儲存在父程序的核心棧中)來初始化子程序的核心棧 .不過,copy_thread把eax暫存器對應欄位的值(這是fork和clone系統呼叫在子程序中的返回值) 強行置為 0 .子程序描述符的thread.esp欄位初始化為子程序核心棧的基位址 .ret_from_fork的位址存放在thread.eip中 .如果父程序使用io許可權位圖 .則子程序獲取該位圖的乙個拷貝 .

最後,如果clone_settls標誌被置位,則子程序獲取由clone系統呼叫的引數tls指向的使用者態資料結構所表示的tls段 .

注:這就是為什麼父子程序沿著統一位置執行,以及子程序的返回值是0.

至此 , task_struct中高階(系統堆疊空間)copy完成 .

set_links( p ); //把新程序放入核心程序佇列(就緒佇列)

//把新程序描述符的pid插入pidhash雜湊表中

attach_pid(p, pidtype_pid, p->pid);

attach_pid(p, pidtype_tgid, p->tgid);

fork()結束

原始碼剖析 Hashtable 原始碼剖析

hashtable同樣是基於雜湊表實現的,同樣每個元素都是key value對,其內部也是通過單鏈表解決衝突問題,容量不足 超過了閾值 時,同樣會自動增長。hashtable也是jdk1.0引入的類,是執行緒安全的,能用於多執行緒環境中。hashtable同樣實現了serializable介面,它支...

python原始碼剖析 Python原始碼剖析

第頁共 頁python 原始碼剖析 物件機制 1.物件 在python 的世界中,一切都是物件,乙個整數是乙個物件,乙個字串也是 乙個物件,更為奇妙的是,型別也是乙個物件,整數型別是乙個物件,字串類 型也是乙個物件。從 年guido 在那個聖誕節揭開 python 世界的大幕開始,一直到現在,pyt...

Erlang hotwheels原始碼剖析

整體構架 janus transport sup 實質為transport,supervisor,client instance supervisor 每個tcp會話建立乙個transport程序來處理對應客戶端的請求。janus topman sup 實質為topman,worker,topic ...