天天看點

程序、線程、fork、COW、父子程序、核心線程、優先級排程

程序、線程、fork、COW、父子程序、核心線程、優先級排程

系統中所有程序的 task_struct 資料結構都通過 list_head 類型的雙向連結清單鍊在一起,Linux 核心提供了一個很常用的宏for_each_process§,用來掃描系統中的所有的程序。這個宏從init_task程序開始周遊,一直循環到init_task為止。另外,宏for_each_porcess_thread()來周遊系統中所有的線程。

Linux 核心提供了current宏,用它可以很友善地找到目前正在運作的程序的task_struct資料結構,current宏的實作和具體的系統架構相關,使用專門的寄存器存放目前程序的 task_struct 資料結構。

fork() 通過寫時複制技術複制目前程序的相關資訊來建立一個全新的子程序。這時子程序和父程序運作在各自的程序位址空間中,但是共享相同的内容。另外,它們有各自的 PID。execve()函數負責讀取可執行檔案,将其裝入子程序的位址空間并開始運作,這時父程序和子程序才開始分道揚镳。

寫時複制技術就是父程序在建立子程序時不需要複制程序位址空間的内容給子程序,隻需要複制父程序的程序位址空間的頁表給子程序,這樣父子程序就可以共享相同的實體記憶體。當父子程序中有一方需要修改某個實體頁面的内容時,觸發寫保護的缺頁異常,然後才把共享頁面的内容複制出來,進而讓父子程序擁有各自的副本。寫時複制技術可以推遲甚至避免複制資料。

fork()函數。fork 原語是最基本的程序建立函數。使用 fork() 函數來建立子程序,子程序和父程序将擁有各自獨立的程序位址空間,但是共享實體記憶體資源,包括程序上下文、程序棧、記憶體資訊、打開的檔案描述符、程序優先級、根目錄、資源限制、控制終端等。在建立期間,子程序和父程序共享實體記憶體空間,當它們開始運作各自的程式時,他們的程序位址空間開始分道揚镳,這得益于寫時複制技術的優勢。

子程序和父程序有如下一些差別:

  • 子程序和父程序的 ID 不一樣。
  • 子程序不會繼承父程序的記憶體方面的鎖,比如mlock()
  • 子程序不會繼承父程序的一些定時器,比如 setitimer()、alarm()、timer_create()
  • 子程序不會繼承父程序的信号量,比如 semop()

clone()函數通常用來建立使用者線程。Linux 核心中沒有專門的線程,而是把線程當作普通程序對待,在核心中仍然以task_struct資料結構來描述,而沒有用特殊的資料結構或者排程算法來描述線程。

核心線程其實就是運作在核心位址空間中的程序,它和普通使用者程序的差別在于核心線程沒有獨立的程序位址空間,也就是 task_struct 資料結構中的 mm 指針被設定為 NULL,因而隻能運作在核心位址空間中,和普通程序一樣參與系統排程。所有的核心線程都共享核心位址空間。

繼續閱讀