理解線程,必須先理解進程!!
什麼是進程(PCB) - 進程pid - 進程有很多,需要有一個區分和標識
- 內存指針 - 進程運行時需要依賴代碼和數據,所以有一個內存指針指向進程所需要的代碼和數據,指向虛擬地址空間
- 進程有很多,而CPU只有幾個,那麼進程就需要調度,爲了實現調度,進程還有以下的信息,爲調度做輔助
- 進程的狀態(R,S,T,t,X,Z)
- 上下文信息 - 記錄進程進行到哪裏,從哪裏開始
- 優先級
- 記賬信息 - 要統計進程在CPU上執行的時間和多少指令,是進程什麼時候從CPU離開的依據
- files指針 - 指向一個結構體,內含 fd_array(文件描述符表)
- 未決信號集/信號屏蔽字
|
線程解決的問題
- 解決“一心多用”問題 - 能夠幾遍只有一個CPU,也可以同時做多件事情,並且不受影響
- 充分利用多核CPU資源 - 讓多個CPU併發的執行任務中的一部分,從而縮短整體的運行時間
什麼是線程
- 線程是能夠運行和調度的最小單位
- 是進程實際運作的單位
- 是進程中單一順序的執行流(例如,一個進程中的main函數,是由線程執行起來的)
- 一個進程可以併發執行多個線程
進程和線程
- 進程/任務是資源分配和管理的基本單位
- 線程/輕量級進程 (這個叫法只針對於Linux而言,別的OS下不一定是,因爲在Linux上) 是程序調度和執行的基本單位
- 線程共享進程數據,但也擁有自己的一部分信息
線程ID : 線程的唯一標識符
一組寄存器 :線程的上下文信息
棧 :每個線程都維護者自己的一個棧
errno
信號屏蔽字
調度優先級
- 我們平常說的pid(getpid() 得到的),其實是這個進程的唯一標識符,也是這個進程中主線程的唯一標識符
主線程是創建進程時產生的第一個線程,也就是main函數創建的線程。
在Linux上
線程是由一個PCB描述的,進程是由一組PCB描述的。
在操作系統內核中,並不區分進程線程,只管理PCB,進程和線程的概念是在用戶層上的。
我們可以採用進程的方式,模擬實現線程,將操作統一在一起。
//進程和task_struct 形成 1:N 的關係
struct task_struct
{
...
pid_t pid; //線程id,每個PCB的id,唯一
pid_t tgid; //線程組id ,我們使用gitpid() 獲得的其實是t_gid
...
struct task_struct* group_leader; //指向組長的PCB
...
struct list_head thread_group; //在進程的任何一個線程中都可以通過這個鏈表找到其他的線程
...
};
主線程的pid和tgid是相同的,所以進程的pid就是組長pcb的id
一進程的多個線程共享
- 同一虛擬地址空間(重要!) : 數據段和代碼段都是共享的,如果定義了一個函數,那麼這個進程中的所有線程都可以調用,如果定義了一個全局變量,這個全局變量在各個線程中都可以訪問到,如果在堆上申請了一段空間,所有的線程也都可以對這個數據進行操作
- 文件描述符表(重要!):一個線程修改了一個文件,其他的線程訪問到的文件也都改變
- 共用信號處理機制中的未決信號機
- 信號處理方式
- 當前工作目錄
- 用戶id和組id
- 未決信號集 :只要進程收到了信號,也就意味着所有線程收到了信號
共享同一個虛擬地址空間,本質上其實是共用同一個頁表
線程之間獨自擁有
- 棧(重要!) :每個線程各自有一個調用棧,線程也可以訪問其他線程棧上的信息
- 上下文信息(一組寄存器)(重要!) :爲了執行調度的正確性,產生上下文信息
- 線程id
- errno :函數出錯就會在errno中設置錯誤碼,我們調用perror時會自動解析錯誤碼
- 信號屏蔽字
- 優先級調度
線程的優點(與進程對比)
創建一個進程,我們需要給他分配虛擬地址空間,這個空間佔用了一部分資源,比如我們的堆默認爲8M,耗費的時間當然比較大
創建一個線程,線程的很對區域都是和其他線程共享的,所以節約了很多的資源,增快了效率,銷燬也比較快
兩進程之間的切換,因爲佔用的空間和資源比較多,所以切換效率比較低
兩線程的切換,只用切換一小部分數據,效率較高
線程可以看到所有自己線程組中線程的信息
進程之間需要管道、消息隊列...
比如我們現在是4核CPU,現在需要不停歇的進行CPU的運算操作,當前用四個線程運算操作,CPU佔用率可達到400%(Linux下),可見充分利用了硬件資源
線程的缺點
線程之間不獨立,很有可能訪問到其他線程的數據,使程序發生未定義行爲
一個線程異常終止,很可能導致所有線程異常終止
編寫和調試一個多線程程序的難度比單線程的難度困難的多
多線程公用同一個公共資源,這個資源就成爲了臨界資源,訪問臨界資源的代碼就是臨界區,多進程訪問臨界資源很可能出現數據錯亂的情況。
如我們是4核CPU,現在有8個線程,期中4個線程在執行任務的時候,其他4個線程就在等待,也會和正在執行任務的線程競爭,消耗資源,而且很容易導致整個進程都掛掉