全球要聞:[ Linux ] 一篇帶你理解Linux下線程概念

2022-12-13 14:21:03 來源:51CTO博客

1.Linux線程的概念

1.1什么是線程

在之前我們談過Linux的進程,每一個進程都有自己的PCB,和自己的進程地址空間。地址空間和物理內存通過頁表建立映射。那么現在我要創建一個新的進程,操作系統要給這個新的進程創建pcb,建立自己的地址空間,頁表等等。因此創建一個進程的成本是非常大的。那么現在有這樣一種方法,要創建一個進程的時候我只創建PCB,所有的pcb訪問的是同一個地址空間。因此這幾個進程是共享地址空間的。我們把地址空間的某一區域劃分給一個tast_struct,那么這個tast_struct在地址空間中就有了自己的一小份區域。那么我們把這里的一個tast_struct的一個執行流就可以稱為線程。

因此總結一下什么是線程呢?

在一個程序里的一個執行路線就叫做線程(thread)。更準確的定義時:線程是“一個進程內部的控制序列”。一切進程至少都有一個執行線程線程在進程內部運行,本質是進程地址空間內運行在Linux系統中,在CPU眼中,看到的TCBP都要比傳統的進程更加輕量化透過進程虛擬地址空間,可以看到進程的大部分資源,將進程資源合理分配給每個執行流,就形成了線程執行流。

在Linux當中,沒有進程和線程的概念上的區分,只有一個叫做執行流!Linux的線程就是用進程PCB模擬的。CPU看到的所有的tast_struct都是一個執行流(線程)。


【資料圖】

重要總結:

以前的進程 = 內核數據結構(PCB)+進程對應的代碼和數據,而現在我們站在內核視角給進程重新下一個定義。(內核角度)進程是承擔分配系統資源的基本實體(進程的基座屬性)。因此Linux進程的最大意義不是被執行,而是向系統申請資源的基本單位!而之前我們所寫的進程內部就只有一個執行流的進程(單執行流),而今天我們學習了進程內部可以有多個執行流,我們叫做多執行流的進程(多執行流)。

CPU視角下,tast_struct <= 傳統的進程PCB. 【等于的時候就是單執行流】

因此,在Linux下,沒有真正意義的線程,而是用tast_struct模擬實現的。因此把Linux下的“進程”<= 其他操作系統的進程概念。也可以稱之為“輕量級進程”。

線程是調度的基本單位。進程內部有多個執行流,而一個執行流就可以稱之為一個線程。一個進程內部可以有多個線程。

1.1.1如何驗證一個進程內有多個線程?

Linux下沒有直接創建線程的接口,但是有原生的線程庫。pthread_create(不是操作系統的接口)。是程序員寫出來的庫。

pthread_create 線程創建

注意:編譯的時候要鏈接這個庫,因此要帶 -lpthread 選項,我們可以體現在makefile之中

mythread:mythread.cc  g++ -o $@ $^ -pthread -std=c++11.PHONY:cleanclean:  rm -f mythread

pthread_join線程等待

線程和進程一樣也要等待,因此線程等待的接口時pthread_join。

有了這兩個接口,我們就能寫出最簡單的線程代碼,至于函數和代碼內部的細節在線程控制會重點強調。

#include #include #include #include using namespace std;void *callback1(void *args){    string name = (char*)args;    while(true)    {        cout << name << endl;        sleep(1);    }}void *callback2(void *args){    string name = (char*)args;    while(true)    {        cout << name << endl;        sleep(1);    }}int main(){    pthread_t tid1;    pthread_t tid2;    pthread_create(&tid1, nullptr, callback1, (void *)" thread 1");    pthread_create(&tid2, nullptr, callback2, (void *)" thread 2");    while (true)    {        cout << "我是主線程............" << endl;        sleep(1);    }    pthread_join(tid1, nullptr);    pthread_join(tid2, nullptr);    return 0;}

我們發現果然是有3個執行流在運行。那么如何證明線程是用進程來模擬的呢?

我們使用ps axj | grep mypthread 命令來查看當前進程的狀態

我們發現,我們明明有3個執行流在運行,我們只查到了一個pid為27829進程,因為我們三個執行流是三個線程,屬于一個進程,那么我們如何查看線程呢,使用這條命令

ps -aL

我們看到了有3個線程在運行,和我們預期的一樣,三個執行流的pid都是27829,這也驗證了這三個執行流屬于同一個進程,其中第一個線程的LWP(Light weight process -- 輕量級進程編號)也是27829說明是主線程。

因此我們驗證了一個進程內部只有多個線程(執行流)。

1.2線程的優點

創建一個新線程的代價要比創建一個新進程小得多與進程之間的切換相比,線程之間的切換需要操作系統做得工作要少很多線程占有的資源要比進程少很多能充分利用多處理器的可并行數量在等待慢速I/O操作結束的同時,程序可執行其他的計算任務計算密集型應用,為了能在多處理器系統上運行,將計算分解到多個線程中實現I/O密集型應用,為了提高性能,將I/O操作重疊,線程可以同時等待不同的I/O操作

如果以上線程的有點無法理解,待線程學習完之后回頭看便可理解。

1.3線程的缺點

性能損失

一個很少被外部事件阻塞的計算密集型線程往往無法與共它線程共享同一個處理器。如果計算密集型

線程的數量比可用的處理器多,那么可能會有較大的性能損失,這里的性能損失指的是增加了額外的

同步和調度開銷,而可用的資源不變。

健壯性降低

編寫多線程需要更全面更深入的考慮,在一個多線程程序里,因時間分配上的細微偏差或者因共享了

不該共享的變量而造成不良影響的可能性是很大的,換句話說線程之間是缺乏保護的。

缺乏訪問控制

進程是訪問控制的基本粒度,在一個線程中調用某些OS函數會對整個進程造成影響。

編程難度提高

編寫與調試一個多線程程序比單線程程序困難得多

1.4 線程異常

單個線程如果出現除零,野指針問題導致線程崩潰,進程也會隨著崩潰 線程是進程的執行分支,線程出異常,就類似進程出異常,進而觸發信號機制,終止進程,進程終止,該進程內的所有線程也就隨即退出

1.5 線程用途

合理的使用多線程,能提高CPU密集型程序的執行效率 合理的使用多線程,能提高IO密集型程序的用戶體驗(如生活中我們一邊寫代碼一邊下載開發工具,就是多線程運行的一種表現)

?

2.Linux進程與線程

2.1進程和線程

進程時資源分配的基本單位線程是調度的基本單位線程共享進程數據,但也擁有自己的一部分數據(線程ID,一組寄存器,棧,errno,信號屏蔽字,調度優先級)進程的多個線程共享同一地址空間,因此Text Segment(代碼段),Data Segment(數據段)都是共享的,如果定義一個函數,在各線程中都可以調用,如果定義一個全局變量,在各線程中都可以訪問到。除此之外,各線程還共享以下進程資源和環境(文件描述符表,每個信號的處理方式,當前工作目錄,用戶id和組id)

2.2 進程和線程的關系

進程和線程的關系如下圖:

2.3如何看待之前學習的單進程?

之前學習的單進程是具有一個線程執行流的進程。

(本篇完)

標簽: 地址空間 操作系統

上一篇:【新要聞】Spring Integration對Redis的支持
下一篇:當前短訊!Ansible自動化部署K8S集群