Platform: msm8x60
Kernel: 2.6
程序是不局限于一段可執行代碼,還要包括其他資源,如打開的檔案、信号、處理器狀态、位址空間、資料及一個或者多個線程。多個程序可執行同一個程式。多個程序可共享打開的檔案、位址空間之類資源。
線程是程序中的活動對象,每個線程都有獨立的程序棧、程式計數器和寄存器組。兄弟線程間共享位址空間、檔案系統等資源。
程序描述符
核心将程序放在任務隊列雙向循環連結清單中,每項叫task_struct,如下圖。結構描述了一個正在執行程式的所有資訊。
Linux通過slab配置設定task_struct,在後面記憶體管理部分會講slab。通過slab能達到避免動态配置設定和釋放鎖帶來的資源消耗。task_struct由于結構太大,是以用圖表示了,如下:
為了快速擷取task_struct的位置,Linux在核心棧尾部建立了個新結構struct thread_info。其結構和存放位置如下:
每個程序用pid表示,可修改程序最大數目。通路task_struct的方法是通過thread_info計算偏移位址來查找。(thread_info中有task元素)
宏如下:
current_thread_info()->task或者 current 這兩個宏都可以得到目前task_struct位址。
程序狀态
程序通過state域描述目前狀态:
TASK_RUNNING: 程序正在執行,或者在運作隊列中等待執行。
TASK_INTERRUPTIBLE: 程序在睡眠,等待某事件滿足,可在接收到信号後激活。
TASK_UNINTERRUPTIBLE: 程序在睡眠,等待某事件滿足,忽略信号的接收。
TASK_ZOMBIE: 程序已經結束,但父程序沒調用wait4()系統調用。
TASK_STOPED: 程序沒投入運作也不能投入運作。在收到SIGSTOP SIGTSTP等信号時發生,在調試時會處于這種狀态。
程序間狀态轉換如下:
設定程序狀态可用set_task_state()函數, 帶記憶體屏障功能。也可用set_current_state()設定目前程序狀态。
當一個程式執行系統調用時,處于核心運作狀态,我們稱為程序上下文。
另外,我們可通過list_entry, list_for_each, for_each_process來周遊程序連結清單,用next_task, pre_task來擷取相鄰程序描述符。
程序建立
和其他系統不一樣,Linux建立程序通過fork()和exec()兩個步驟實作。Fork()拷貝父程序的内容建立,父子程序的差別在于pid, ppid、挂起的信号及某些資源。之後exec()讀取可執行檔案載入位址空間運作。
Fork()采用寫時拷貝的方法,就是說當真在寫入的時候,才進行将位址空間拷貝到子程序。提高了快速執行的能力。
fork()、vfork()、__clone()都是調用clone()來實作程序建立的。Clone()調用do_fork()。主要流程如下:
vfork()和fork()的差別在于不拷貝父程序頁表。子程序運作時,父程序阻塞直到子程序給其發送信号。
線程實作
核心把所有線程當程序來看待,隻是傳給clone()的參數有點差別而已。每個線程都有自己的task_struct,隻是它和其他一些線程共享一些資源,如位址空間、檔案系統資源、檔案描述符和信号處理程式。
線程建立:
clone(CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND, 0)
程序建立:
Clone(SIGCHILD, 0)
Vfork()建立:
Clone(CLONE_VFORK|CLONE_VM|SIGCHILD, 0)
核心線程和普通線程的差別在于其沒有獨立的位址空間,它們用的是前一個程序位址空間。不過他們和普通程序一樣,可被排程和搶占。函數如下:
程序終結
不管程序如何終結,該任務大部分都要靠do_exit()來完成。代碼如下:
至此,和程序相關資源被釋放掉了,程序不可運作處于TASK_ZOMBIE狀态,它所占用資源就是核心棧、thread_info和 task_struct結構。存在的目的是給父程序提供資訊,等父程序檢測到後,剩餘資源都被釋放。
在調用了wait4()系統調用之後,最終會調用release_task()來完成釋放程序描述符剩餘的資源。代碼如下: