Linux程式管理、程式建立、執行緒實現、殭屍程式
目錄
概述
在 Linux 系統中,通過複製一個現有的程式來建立一個全新的程式。呼叫 fork() 的程式稱為父程式,新產生的程式稱為子程式。呼叫結束時,在返回點這個相同位置上,父程式恢復執行,子程式開始執行。fork() 系統呼叫從核心返回兩次:一次回到父程式,另一次回到新產生的子程式。
通常,建立新的程式都是為了執行不同的程式,接著呼叫exec()這組函式就可建立新地址空間,並將新程式載入其中。fork()底層是由clone()系統呼叫實現的。
最終,通過 exit() 系統呼叫推出執行,這個函式終結程式並釋放資源。父程式可通過 wait4() 系統呼叫查詢子程式是否終結(這使得父程式擁有等待特定程式的能力)。程式退出設定為僵死狀態,直到其父程式呼叫 wait() 或 waitpid() 為止。
程式描述符及任務結構
每個程式都是一個 task_struct 結構體,核心使用雙向連結串列 task_list 來儲存。task_struct 包含的資料用來描述程式:開啟的檔案、地址空間、掛起訊號、程式狀態及其他資訊。
分配程式描述符
Linux 通過 slab 分配器分配 task_struct,這樣能達到物件複用和快取著色(cache coloring) 的目的。slab 分配器在核心棧底建立新的結構 thread_info,該結構含指向 task_struct 的指標。
struct thread_info {
struct task_struct *task;
struct exec_domain *exec_domain;
__u32 flags;
__u32 status;
__u32 cpu;
int preempt_count;
mm_segment_t addr_limit;
struct restart_block restart_block;
void *sysenter_return;
int uaccess_err;
};
效果如圖:
程式描述符的存放
程式描述符 pid 預設最大值被設定為 32768。
系統管理員修改可通過/proc/sys/kernel/pid_max 來提高上限。
程式狀態
task_struct 的 state 域描述程式狀態。程式五狀態如下:
- TASK_RUNNING:就緒或者執行態。
- TASK_INTERRUPTIBLE: 程式被阻塞或者睡眠。條件達成或收到訊號可進入執行態。
- TASK_UNINTERRUPTIBLE:也是阻塞或者睡眠,只有條件達成響應,對訊號或中斷不響應。
- __TASK_TRACED: 被其他程式跟蹤的程式,如 pstrace 對除錯程式跟蹤。
- __TASK_STOPPPED:停止執行。通常收到 SIGSTOP 等訊號進入此狀態。
狀態轉化如圖:
程式家族樹
所有程式都是 pid 為 1 的 init 程式後代。
task_struct 中有指向父程式 task_struct 的 parent 指標,還包含一個稱為 children 的子程式連結串列。
程式建立
首先使用 fork() 拷貝當前程式建立一個子程式。子程式與父程式唯一區別僅為 pid,ppid 及某些資源的統計量(例如,掛起的訊號)。exec() 函式負責讀取可執行檔案並將其載入地址空間開始執行。
寫時拷貝
為避免把所有資源都複製給新程式的效率浪費,fork() 採用 copy-on-write 頁實現。fork() 的實際開銷就是複製父程式的頁表以及給子程式建立唯一的程式描述符。一般情況下,程式建立後馬上執行可執行檔案,這種優化可避免拷貝大量根本就不會使用的資料。
fork()
fork()、vfork() 和 __clone() 庫函式都通過相應引數標誌呼叫 clone(),然後clone()呼叫 do_fork(),do_fork() 呼叫 copy_process() 函式。
該函式之中為新程式建立核心棧、task_info、task_struct,並清零 task_struct 某些成員(主要是統計資訊),大多數資料依然未修改。子程式設定為 TASK_UNINTERRUPTIBLE 確保不會投入執行。分配 pid,根據傳遞給 clone() 的引數標誌,選擇拷貝或共享開啟的檔案、檔案系統資訊、訊號處理函式、程式地址空間和名稱空間等。最後返回一個子程式的指標。
vfork()
vfork() 與 fork() 的唯一區別是不拷貝父程式的頁表項。子程式作為父程式一個單獨的執行緒在它的地址空間執行,父程式被阻塞,直到子程式退出或執行 exec()。子程式不能像地址空間寫入。
執行緒實現
建立執行緒
建立執行緒和建立程式只是呼叫clone()時引數標誌不一樣:
clone(CLONE_VM | CLONE_FD | CLONE_FILES | CLONE_SIGHAND, 0);
引數就是共享的資源。由上可知,執行緒和父程式共享虛擬空間(VM)、檔案系統資源、檔案描述符和訊號處理程式,而fork()的實現是:
clone(SIGCHLD, 0);
核心執行緒
核心執行緒和普通執行緒區別在於沒有獨立的地址空間,只在核心空間執行。核心程式和普通程式一樣,可以被排程,也可以被搶佔。
程式終結
由 do_exit() 函式負責。程式終結會設定相關 task_struct 成員,刪除相關核心定時器,輸出記賬資訊,釋放其地址空間(如果沒有共享),減少相關資源引用計數,呼叫 exit_notify() 向父程式傳送訊號,給自己的子程式尋找養父,養父為執行緒組的其他程式或為 init 程式,並把 state 設定為 EXIT_ZOMBIE 狀態。do_exit()函式再呼叫 schedule() 切換到新的程式,而自己成為殭屍程式不會再被排程,所以這是程式執行的最後一段程式碼。do_exit() 永不返回。至此程式佔用的記憶體僅剩核心棧、thread_info 結構和 task_struct 結構。此時程式還存在的唯一目的就是向其父程式提供資訊,父程式檢索到資訊後,或者通知核心那是無關的資訊後,核心釋放程式剩餘記憶體。
刪除程式描述符
在呼叫 do_exit() 後,儘管程式成為殭屍程式,但是系統還保留了它的程式描述符。這樣可讓系統有辦法在該程式終結後仍能獲得它的資訊。因此,程式終結時所需的清理工作和程式描述符的刪除分開執行。在該程式的父程式獲知該程式的資訊後,或者父程式通知核心它並不關注那些資訊後,它的子程式(當前殭屍程式)的 task_struct 結構被釋放。
如何處理殭屍程式
1.使用父程式呼叫 wait() 或 waitpid() 函式等待子程式,父程式阻塞。
2.非同步方式,安裝 signal_handler,捕獲SIGCHILD訊號,在訊號處理函式中呼叫wait()函式等待。但是由於訊號不支援排隊,為避免訊號高發時丟失訊號,在處理函式中應使用 waitpid() 函式的非阻塞版本,迴圈對每一個子程式進行 wait 處理。方法如下:
while(pid = waitpid(-1, &status, WNOHANG)) > 0){
//printf("child %d exit\n", pid);
}
引數設定為-1是因為:
-1 meaning wait for any child process.
WNOHANG 則是非阻塞模式標誌。
3.直接使用 sigaction 結構體,當中的選項設定 SA_NOCLDWAIT,這樣就不需要任何處理,告訴核心直接終結子程式,不要進入殭屍狀態。
struct sigaction act;
act.sa_flags = SA_NOCLDWAIT;
sigaction(SIGCHLD, &act, NULL);
相關文章
- Linux 殭屍程式Linux
- 殭屍程式
- 檢視 Linux 殭屍程式Linux
- Linux殭屍程式處置Linux
- 殭屍程式,孤兒程式
- Linux中殭屍程式是什麼意思?怎麼檢視殭屍程式?Linux
- Linux 中殭屍程式詳解Linux
- Linux 系統中殭屍程式Linux
- Linux如何殺掉殭屍程式Linux
- 殭屍程式和孤兒程式
- 什麼是殭屍程式,如何找到並殺掉殭屍程式?
- fork和殭屍程式
- 【系統】 殭屍程式
- 殺死殭屍程式
- 子程式、孤兒程式,殭屍程式, init程式
- Perl程式:殭屍程式和孤兒程式
- 關於LINUX殭屍程式的出現和原理Linux
- Linux系統殭屍程式詳解Linux
- 多執行緒------執行緒與程式/執行緒排程/建立執行緒執行緒
- C#多執行緒程式設計實戰1.1建立執行緒C#執行緒程式設計
- Linux 效能優化之 CPU 篇 ----- 殭屍程式Linux優化
- 程式-程式-執行緒執行緒
- linux系統程式設計之程式(三):程式複製fork,孤兒程式,殭屍程式Linux程式設計
- 物聯網教程Linux系統程式設計——特殊程式之殭屍程式Linux程式設計
- SCP使用SIGSTOP後臺執行並簡單討論孤兒程式殭屍程式
- 程式執行緒篇——程式執行緒基礎執行緒
- Go Exec 殭屍與孤兒程式Go
- 檢視並殺死殭屍程式
- 執行緒池的實現程式碼分析執行緒
- Linux執行緒(程式)數限制Linux執行緒
- 使用Java實現多執行緒程式設計Java執行緒程式設計
- Linux中程式與程式、執行緒的區別!Linux執行緒
- 多執行緒-匿名內部類的方式實現多執行緒程式執行緒
- 執行緒(一)——執行緒,執行緒池,Task概念+程式碼實踐執行緒
- Posix執行緒程式設計指南(1)-執行緒建立與取消 (轉)執行緒程式設計
- Linux 執行緒(程式)數限制分析Linux執行緒
- Linux程式多執行緒入門Linux執行緒
- linux 多執行緒程式設計Linux執行緒程式設計