程序與執行緒(上)

2021-10-07 22:41:28 字數 4776 閱讀 3188

在學習linux的過程中,程序與執行緒可謂一對好兄弟,是必然要掌握的內容。

一:何所謂程序 何所謂執行緒

程序:程序(process)是計算機中的程式關於某資料集合上的一次執行活動,是系統進行資源分配與排程的基本單位。

執行緒:執行緒是作業系統程序排程器可以排程的最小執行單元。

(在第一次看到這樣的描述時,我的感覺是完全摸不著頭腦。既然執行緒是系統排程的最小執行單元,那麼為什麼還說程序是作業系統排程的基本單位?)

事實上,在linux的設計早期,只有程序的概念,沒有執行緒的概念。隨著計算機技術的不斷發展,作業系統需要順應時代,充分發揮多核心cpu的計算能力,所以引入的執行緒的概念。同時為了盡量少的修改原有的架構,linux中的執行緒可以說是程序的乙個閹割版,故而二者有著千絲萬縷的聯絡。

在《linux環境程式設計 從應用到核心》一書中,提到了將程序看作是執行緒組的說法,這樣做十分方便我們理解執行緒與程序的關係。如果把程序看作乙個執行緒組(每個程序中至少要有乙個執行緒),系統為每乙個執行緒組分配資源(例如記憶體),同乙個執行緒組內的所有執行緒共享這些資源。系統又決定了不同的執行緒的執行順序,(先決定當前所執行的程序,再決定程序內的哪乙個或多個執行緒)故而作業系統排程的最小單元是執行緒,最小單位是程序。

這裡可以順帶提一下並行和併發。二者都是描述多工時的執行方案的。不同的是,併發是交替進行,並行是同時執行。既然並行是交替執行的,為什麼還要提乙個『並』字呢?我是這樣理解的,計算機在同一時間要執行好多的任務,但是只有乙個cpu,那麼計算機就只好飛快的輪流執行各種任務,給人同時執行的錯覺。故而體現出『並』的意義。所以,提及多程序的,就是說併發。如果計算機有多個cpu,那麼多執行緒就是在說並行。

二:pcb

作業系統裡的pcb說的是程序控制塊,是每乙個程序都擁有的乙個資料結構。linux中的實現是一種名為task_struct的結構體。
它記錄了一下幾個型別的資訊:

1.狀態資訊,例如這個程序處於可執行狀態,休眠,掛起等。

2.性質,由於unix有很多變種,程序有自己獨特的性質。

3.資源,資源的鏈結比如記憶體,還有資源的限制和許可權等。

4.組織,例如按照家族關係建立起來的樹(父程序,子程序等)。

或者這樣說:

1)程序狀態:可以是new、ready、running、waiting或halted等。

(3)cpu暫存器:如累加器、索引暫存器(index register)、堆疊指標以及一般用途暫存器、狀況**等,主要用途在於中斷時暫時儲存資料,以便稍後繼續利用;其數量及類因計算機架構有所差異。

(4)cpu排班法:優先順序、排班佇列等指標以及其他引數。

(5)儲存器管理:如分頁表(page table)等。

(6)會計資訊:如cpu與實際時間之使用數量、時限、帳號、工作或程序號碼。

(7)輸入輸出狀態:配置程序使用i/o裝置,如磁帶機。

如果要查乙個程序的戶口的話,拿到它的pcb,就可以知道關於它的全部資訊。而所謂的資源分配,也是記錄在pcb中的。pcb維護著乙個程序對應的狀態,虛擬記憶體位址,開啟的檔案,排程優先順序,識別符號等等關鍵資訊。
三:tcp

tcp是類似pcb的用以管理執行緒的資料結構。通俗且不負責任的說法,tcp是pcb組成部分。
四:結構造就的現實

既然執行緒是程序的一部分,或者說程序是執行緒的執行緒組,同一組內的執行緒共享程序資源(例如全域性變數,檔案描述符,訊號處理器,當前工作目錄狀態等),但是各自執行各自的函式,也就是有自己的堆疊,那麼這樣的組織形式會帶來什麼樣的結果呢?

執行緒最大的好處就是發揮了多核心cpu的優勢,這一點無需多講(實際上在深入了解linux程式設計之後,對於多執行緒程式設計還需要有特別多的注意點,比如偽共享就是一種效能殺手,發生偽共享的時候多執行緒並發揮不出多核心的優勢。)。

此外,由於執行緒是在同乙個程序內的,共享程序的資源,也就是對於作用域是整個程序的變數或其他資源而言,不同執行緒是可以直接拿到並對其操作的。這一點也就使得執行緒擁有了一定意義上的速度優勢。

無論是程序還是執行緒,對於資源的操作,都需要協調合作,這樣就就需要各種各樣的鎖機制。執行緒我們用互斥量,讀寫鎖實現對共享資源的保護,程序我們通常使用訊號量設定合適的臨界區來實現互斥或同步。互斥量,讀寫鎖的生存空間,或者說作用域是在整個程序內的,而訊號量是在核心中的。(這一點也與程序和執行緒的結構相互吻合。)

另外,執行緒的引入使得我們需要考慮向乙個程序傳送訊號,具體由哪乙個執行緒負責接收與處理。

五:pid tgid

以往pid表示的就是程序id,但是現在程序已經變成執行緒組了。所以pid實際表示的是執行緒id,tgid表示的是執行緒組id,也是是程序id。主線程的id與程序的id是相同的。

也就是所,呼叫getpid實際上得到的是tgid,而呼叫gettid得到的是pid。

為了驗證,執行這樣的**:

#include #include #include #include #include void* handler(void *a)

int main()

; for(i = 0;i < 5; i++)

for(i = 0;i < 5; i++)

return 0;

}

得到的結果為:

im pthread no.0,geipid:4302,gettid:4303

im pthread no.1,geipid:4302,gettid:4304

im pthread no.2,geipid:4302,gettid:4305

im pthread no.3,geipid:4302,gettid:4306

im pthread no.4,geipid:4302,gettid:4307

可以看到確實如此。

但是,既然有執行緒id,為什麼pthread_create函式還需要在第乙個引數中記錄執行緒id呢?直接返回乙個pid不就可以了嗎?
注意!這兩個id是不同的。

注意!這兩個id是不同的。

注意!這兩個id是不同的。

重要的事情說三遍。
修改上邊的**,把p_t裡邊的內容列印出來看一下。

#include #include #include #include #include void* handler(void *a)

int main()

; for(i = 0;i < 5; i++)

for(i = 0;i < 5; i++)

for(i = 0;i < 5; i++)

return 0;

}

得到這樣的結果:
im pthread no.0,geipid:4660,gettid:4661

im pthread no.4,geipid:4660,gettid:4665

im pthread no.3,geipid:4660,gettid:4664

im pthread no.2,geipid:4660,gettid:4663

im pthread no.1,geipid:4660,gettid:4662

pthread_t:按整形列印1201022720,按位址列印0x7f9347962700

pthread_t:按整形列印1192630016,按位址列印0x7f9347161700

pthread_t:按整形列印1184237312,按位址列印0x7f9346960700

pthread_t:按整形列印1175844608,按位址列印0x7f934615f700

pthread_t:按整形列印1167451904,按位址列印0x7f934595e700

首先可以看出的是,兩次執行的tgid是不同的,但同一次執行內的tgid是相同的,再次印證了上面的內容。而p_t中記錄的執行緒id,與系統呼叫gettid得到的不是同乙個值,看起來更像是某個位址。我用的是centos系統,在其他系統下,比如ios中pthread_t儲存的資料可能又是另外的值。這是怎麼回事?乙個執行緒怎麼會有兩種id,並且不同系統下的id還會不一樣?

六:兩種執行緒id

gettid得到的執行緒id:《linux系統程式設計 從應用到核心》給出的解釋是:執行緒是一種輕量級程序,是作業系統排程器的最小單位,所以需要乙個執行緒id來操作執行緒。    也就是說,這的執行緒id是類似程序id的一種存在,對於每乙個執行緒,核心中有且僅有乙個數字將其標記,每個執行緒的id不同,通過執行緒id可以唯一的確定乙個執行緒。

pthread_t pthread_self pthread_equal 中的執行緒id

這一線程id是用來標記乙個程序內的執行緒的,通過上文我們知道,同一程序內的執行緒存在於程序的棧上,每個執行緒都有自己的棧位址。如果拿到了某個執行緒在棧上的起始位置,那麼就可以標記這個執行緒。所以有些系統中線程的位址填充執行緒id。因為程序中的位址都是虛擬位址,由mmap完成對映,所以兩個不同程序內的執行緒可能有相同的虛擬位址,故而有時會有不同程序內的不同執行緒,擁有相同的執行緒id。

不同作業系統對這一種執行緒id的實現是不同的,所以要注意以下幾點:

1)getpid gettid的返回都可以用int接受 也可以用pid_t 接受。但是pthread_t pthread_self pthread_equal中的執行緒id只能用pthread_t接受

2)因為不知道pthread_t的具體實現,固一般不列印pthread_t

3)同上,因為不知道ptread_t的具體實現,為了保證抑制性和正確性,最好使用pthread_equal來判斷兩個執行緒id是否相同。

程序與執行緒

程序 process 管理單元,管理分配應用程式的資料,的記憶體空間.執行緒 thread 執行單元,執行緒負責執行乙個預先編排好的 段,執行 棧是基於執行緒的.乙個應用程式啟動的時候,程序自動建立,並且會預設建立主線程,主線程負責執行main 方法.thread t new thread new ...

程序與執行緒

程序 是擁有資源的實體,包括 1。乙個程序有乙個虛擬位址空間,不同程序位於不同的 虛擬位址空間中。程序之間若要通訊,必須通過作業系統 的功能呼叫 ipc 2。程序擁有其他資源,例如開啟的檔案和i o裝置。程序結束時,作業系統會自動釋放該程序擁有的所有資源。例如,如果 open乙個檔案而不close它...

程序與執行緒

程序包括 1 私有的虛位址空間 2 可執行程式 與資料 3 開啟系統資源列表控制代碼 4 安全訪問標誌 5 程序id 6 至少有乙個執行執行緒 執行緒包括 1 一組cpu暫存器狀態 2 兩個堆疊 系統態與使用者態 3 tls 執行緒區域性儲存區 4 執行緒id 5 安全特性 因此程序是不活潑的,它從...