
在之前我們談過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下的“進程”<= 其他操作系統的進程概念。也可以稱之為“輕量級進程”。
線程是調度的基本單位。進程內部有多個執行流,而一個執行流就可以稱之為一個線程。一個進程內部可以有多個線程。
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說明是主線程。
因此我們驗證了一個進程內部只有多個線程(執行流)。
如果以上線程的有點無法理解,待線程學習完之后回頭看便可理解。
一個很少被外部事件阻塞的計算密集型線程往往無法與共它線程共享同一個處理器。如果計算密集型
線程的數量比可用的處理器多,那么可能會有較大的性能損失,這里的性能損失指的是增加了額外的
同步和調度開銷,而可用的資源不變。
健壯性降低編寫多線程需要更全面更深入的考慮,在一個多線程程序里,因時間分配上的細微偏差或者因共享了
不該共享的變量而造成不良影響的可能性是很大的,換句話說線程之間是缺乏保護的。
缺乏訪問控制進程是訪問控制的基本粒度,在一個線程中調用某些OS函數會對整個進程造成影響。
編程難度提高編寫與調試一個多線程程序比單線程程序困難得多
?
進程和線程的關系如下圖:
之前學習的單進程是具有一個線程執行流的進程。
(本篇完)