進程線程及其狀態
阿新 • • 發佈:2018-01-01
文件描述符 原子操作 lee 具體實現 進程id join 進程調度 循環 寫時復制
進程線程及其狀態
進程
進程的概念
- 進程就是執行中的程序。
進程的狀態
進程有五種狀態,分別是:
- 新建:進程正在被創建
- 運行:進程正在被執行
- 阻塞:進程等待某個事件(如I/O操作)
- 就緒:進程等待分配處理器
- 終止:進程完成執行
進程調度流程圖
線程
線程的概念
- 線程是程序執行流的最小單元,線程早期也有輕量級進程之稱。一個進程中可能包含多個線程。在系統內核層面,進程與線程並無本質的不同。進程與線程最大的不同點是資源分配。
線程與進程的比較
- 線程與進程都可以實現多任務。
- 線程是CPU調度的基本單元,進程是系統資源分配的基本單元。
- windows下進程線程是涇渭分明,區別明顯的。在Linux中它們有很多共同特性。
- 在早期Linux的內核結構中:進程和線程的區別只是創建子進程和子線程時,是否設置為共享內存,二者在內核中的存儲結構並無區別,系統調度的單位也是輕量級進程。2.6以後的Linux內核版本才將線程和進程完全獨立開來。
- 線程的狀態改變只代表了CPU執行過程的改變,線程操作的資源仍然是進程的。除了 CPU外,計算機內的軟硬件資源的分配都是以進程為單位的。進程擁有一個完整的虛擬地址空間,不依賴於線程而獨立存在,而線程只是進程的一部分,與進程內的其他線程一起共享分配給該進程的所有資源。
線程的狀態
- 同進程的實現原理類似,線程也可主要概括為五種狀態(實際上Linux將線程狀態細分為十幾種):
- 新建,由於不需要進行必要的內存復制等工作,新建線程要比新建進程更快。
- 就緒
- 運行
- 阻塞
- 死亡,線程死亡後,也需要回收處理。
- 調度的過程參考進程。
線程的內核調度
多線程編程具有響應度高、資源共享、經濟和多處理器體系結構的利用四個優點。用戶線程是映射到內核線程池進行CPU調度的,映射關系模型包含有:
- (1)多對一
- (2)一對一
- (3)多對多
內核調度圖
- 這裏為什麽沒有一對多?因為線程是CPU資源調度的最小單位,即:單線程在一個時間點上只能利用到一個核心(進行一個原子操作),一個原子操作不能再分開由不同核心執行。而多核CPU在執行單線程任務時,可能會切換多個核心輪流來執行這個任務(每個原子操作的CPU核心可能並不相同),例如在執行循環時,這次循環和下次循環可能並不是同一個核心來執行的(這跟你的系統有關,但可以看到單線程最多只能占用到 (1/CPU核心數) 的CPU資源(超線程CPU占用1/(CPU線程數))。
- 而資源上,多核CPU調用同一資源時,X86架構會使用總線鎖,對該資源進行鎖定,保證原子操作執行完整不被打斷。當操作完成時,會解鎖並通知其他線程,我操作完了,你們可以來操作了(實際上,此方法效率很低,僅作為最後一道保險)。
- 因此確定一個操作是原子操作時,沒有必要浪費外圍昂貴的開銷再來給他加鎖,原子操作本身就是一道互斥鎖。互斥鎖的目的,也正是將一系列操作變為原子操作。
進程的誕生與消亡
- 進程的誕生
- (1)fork函數:子進程拷貝父進程的數據(具體實現是讀時共享,寫時復制)
- (2)vfork函數:子進程與父進程共享數據
- vfork是一個過時的函數,雖然與fork相比有那麽一點性能優勢,但其帶來一連串的坑並不那麽好填,不建議使用,除非你對性能追求到極致。
- 進程的消亡
- (1)正常結束和異常終止;
- (2)linux系統設計時規定:每個進程結束時,系統會自動回收open但沒有close的文件資源,malloc但沒有free的資源等,但並不會回收進程本身占用的資源(即進程的屍體,主要包括進程本身的文件描述符對應的資源(task_struct結構體)和進程的棧空間),這需要由進程的父進程來完成回收(收屍)。
- 僵屍進程
- 在上述(2)中,如果父進程沒有結束,而且也不回收已結束的子進程(收屍),已經結束的子進程,就變成了僵屍進程。
- 父進程可以使用wait或waitpid,顯式地回收子進程(剩余待回收)的內存資源並且獲取子進程退出狀態。
- 父進程結束時也會自動回收僵屍進程,但應避免這種不嚴謹的方式。
- 孤兒進程
- 子進程還在執行,而父進程先結束了,子進程就成為了孤兒進程,托管到系統了。
- 此時子進程的父進程變為了系統的init進程,init進程會在孤兒進程結束後自動回收孤兒進程的資源。
線程的誕生與消亡
- 線程標識(線程ID)
- 進程ID在整個系統中是唯一的。
- 線程ID(pthread_t類型)只在它所屬的進程中有效。
- pthread_t(Linux中為unsigned int類型,OS中為結構體指針,Windows中為handle句柄)用於聲明線程ID。
- 函數:pthread_self取得自身線程ID。
- 創建線程
- 使用函數pthread_create,線程創建後,就開始運行相關的線程函數。
- 退出線程。
- 線程執行完畢。可以return,不能exit(exit是退出進程)。
- 使用函數pthread_exit,主動退出線程。主線程使用該函數時,進程並不會結束,而是等待其他線程結束。
- 進程結束時,線程也結束(線程依賴於其所在的進程)。
- 線程回收
- 由於線程使用的資源是屬於進程的,退出線程而進程仍然運行時資源並未完全釋放,形成僵屍線程。
- pthread_join(tid)函數類似wait/waitpid函數,用於阻塞等待線程tid結束,調用它的線程一直等待到tid線程結束時,tid線程資源就被回收。
- pthread_detach(tid)函數線程分離,讓系統自動回收tid線程。
- 按以下步驟回收:
- pthread_attr_t attr;//線程創建前,定義線程屬性
- pthread_attr_init(&attr);//進行初始化線程屬性
- pthread_attr_getdetachsate(&attr,&status);//獲取分離狀態
- pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);//設置線程分離狀態.
- pthread_create(&tid, &attr,func,NULL);//創建線程
- pthread_attr_destroy(&attr);//線程結束時,調用回收函數
- 線程回收代碼示例:
void * func(void *p) { printf("我是子線程\n"); } int main(int argc, char *argv[]) { pthread_attr_t attr; //定義一個變量 pthread_t tid; pthread_attr_init(&attr);//初始化 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//設置分離 pthread_create(&tid, &attr, func, NULL);//創建線程 sleep(1);//等1秒讓子線程執行完 pthread_attr_destroy(&attr);//釋放 return 0; }
進程線程及其狀態