1. 程式人生 > >程序和執行緒的概念、區別和聯絡

程序和執行緒的概念、區別和聯絡

程序(process)和執行緒(thread)是作業系統的基本概念,但是它們比較抽象,不容易掌握。

最近,我讀到一篇材料,發現有一個很好的類比,可以把它們解釋地清晰易懂。

在這個簡單易懂的類比下,瞭解一下程序和執行緒的巨集觀概念:

程序,是併發執行的程式在執行過程中分配和管理資源的基本單位,是一個動態概念,竟爭計算機系統資源的基本單位。每一個程序都有一個自己的地址空 間,即程序空間或(虛空間)。程序空間的大小 只與處理機的位數有關,一個 16 位長處理機的程序空間大小為 216 ,而 32 位處理機的程序空間大小為 232 。程序至少有 5 種基本狀態,它們是:初始態,執行態,等待狀態,就緒狀態,終止狀態。

執行緒,在網路或多使用者環境下,一個伺服器通常需要接收大量且不確定數量使用者的併發請求,為每一個請求都建立一個程序顯然是行不通的,——無論是從系統資源開銷方面或是響應使用者請求的效率方面來看。因此,作業系統中執行緒的概念便被引進了。執行緒,是程序的一部分,一個沒有執行緒的程序可以被看作是單執行緒的。執行緒有時又被稱為輕權程序或輕量級程序,也是 CPU 排程的一個基本單位。

說到這裡,我們對程序與執行緒都有了一個大體上的印象,現在開始說說二者大致的區別。

    程序的執行過程是線狀的, 儘管中間會發生中斷或暫停,但該程序所擁有的資源只為該線狀執行過程服務。一旦發生程序上下文切換,這些資源都是要被保護起來的。這是程序巨集觀上的執行過 程。而程序又可有單執行緒程序與多執行緒程序兩種。我們知道,程序有 一個程序控制塊 PCB ,相關程式段 和 該程式段對其進行操作的資料結構集 這三部分,單執行緒程序的執行過程在巨集觀上是線性的,微觀上也只有單一的執行過程;而多執行緒程序在巨集觀上的執行過程同樣為線性的,但微觀上卻可以有多個執行 操作(執行緒),如不同程式碼片段以及相關的資料結構集。執行緒的改變只代表了 CPU 執行過程的改變,而沒有發生程序所擁有的資源變化。

了 CPU 之外,計算機內的軟硬體資源的分配與執行緒無關,執行緒只能共享它所屬程序的資源。與程序控制表和 PCB 相似,每個執行緒也有自己的執行緒控制表 TCB ,而這個 TCB 中所儲存的執行緒狀態資訊則要比 PCB 表少得多,這些資訊主要是相關指標用堆疊(系統棧和使用者棧),暫存器中的狀態資料。程序擁有一個完整的虛擬地址空間,不依賴於執行緒而獨立存在;反之,執行緒是程序的一部分,沒有自己的地址空間,與程序內的其他執行緒一起共享分配給該程序的所有資源

    執行緒可以有效地提高系統的執行效率,但並不是在所有計算機系統中都是適用的,如某些很少做程序排程和切換的實時系統。使用執行緒的好處是有多個任務需要處理 機處理時,減少處理機的切換時間;而且,執行緒的建立和結束所需要的系統開銷也比程序的建立和結束要小得多。最適用使用執行緒的系統是多處理機系統和網路系統 或分散式系統。

執行緒在執行過程中與程序還是有區別的。每個獨立的執行緒有一個程式執行的入口、順序執行序列和程式的出口。但是執行緒不能夠獨立執行,必須依存在應用程式中,由應用程式提供多個執行緒執行控制。 

 從邏輯角度來看,多執行緒的意義在於一個應用程式中,有多個執行部分可以同時執行。但作業系統並沒有將多個執行緒看做多個獨立的應用,來實現程序的排程和管理以及資源分配。這就是程序和執行緒的重要區別。

1. 執行緒的執行特性。

    執行緒只有 3 個基本狀態:就緒,執行,阻塞。

    執行緒存在 5 種基本操作來切換執行緒的狀態:派生,阻塞,啟用,排程,結束。

2. 程序通訊。

    單機系統中程序通訊有 4 種形式:主從式,會話式,訊息或郵箱機制,共享儲存區方式。

    主從式典型例子:終端控制程序和終端程序。

    會話式典型例子:使用者程序與磁碟管理程序之間的通訊。

關於多程序和多執行緒:

一.為何需要多程序(或者多執行緒),為何需要併發?

這個問題或許本身都不是個問題。但是對於沒有接觸過多程序程式設計的朋友來說,他們確實無法感受到併發的魅力以及必要性。

我想,只要你不是整天都寫那種int main()到底的程式碼的人,那麼或多或少你會遇到程式碼響應不夠用的情況,也應該有嘗過併發程式設計的甜頭。就像一個快餐點的服務員,既要在前臺接待客戶點 餐,又要接電話送外賣,沒有分身術肯定會忙得你焦頭爛額的。幸運的是確實有這麼一種技術,讓你可以像孫悟空一樣分身,靈魂出竅,樂哉樂哉地輕鬆應付一切狀 況,這就是多程序/執行緒技術。

併發技術,就是可以讓你在同一時間同時執行多條任務的技術。你的程式碼將不僅僅是從上到下,從左到右這樣規規矩矩的一條線執行。你可以一條線在main函式裡跟你的客戶交流,另一條線,你早就把你外賣送到了其他客戶的手裡。

所以,為何需要併發?因為我們需要更強大的功能,提供更多的服務,所以併發,必不可少。

二.多程序

什麼是程序。最直觀的就是一個個pid,官方的說法就:程序是程式在計算機上的一次執行活動。

說得簡單點,下面這段程式碼執行的時候

 複製程式碼複製程式碼
int main()  
  
{  
  
printf(”pid is %d/n”,getpid() );  
  
return 0;  
  
}  
複製程式碼複製程式碼

進入main函式,這就是一個程序,程序pid會打印出來,然後執行到return,該函式就退出,然後由於該函式是該程序的唯一的一次執行,所以return後,該程序也會退出。

看看多程序。linux下建立子程序的呼叫是fork();

複製程式碼複製程式碼
#include <unistd.h>  
#include <sys/types.h>   
#include <stdio.h>  
  
   
  
void print_exit()  
{  
       printf("the exit pid:%d/n",getpid() );  
}  
  
main ()   
{   
   pid_t pid;   
   atexit( print_exit );      //註冊該程序退出時的回撥函式  
      pid=fork();   
        if (pid < 0)   
                printf("error in fork!");   
        else if (pid == 0)   
                printf("i am the child process, my process id is %d/n",getpid());   
        else   
        {  
               printf("i am the parent process, my process id is %d/n",getpid());   
              sleep(2);  
              wait();  
       }  
  
}  
複製程式碼複製程式碼

i am the child process, my process id is 15806
the exit pid:15806
i am the parent process, my process id is 15805
the exit pid:15805

這是gcc測試下的執行結果。

關於fork函式,功能就是產生子程序,由於前面說過,程序就是執行的流程活動。

那麼fork產生子程序的表現就是它會返回2次,一次返回0,順序執行下面的程式碼。這是子程序。

一次返回子程序的pid,也順序執行下面的程式碼,這是父程序。

(為何父程序需要獲取子程序的pid呢?這個有很多原因,其中一個原因:看最後的wait,就知道父程序等待子程序的終結後,處理其task_struct結構,否則會產生殭屍程序,扯遠了,有興趣可以自己google)。

如果fork失敗,會返回-1.

額外說下atexit( print_exit ); 需要的引數肯定是函式的呼叫地址。

這裡的print_exit 是函式名還是函式指標呢?答案是函式指標,函式名永遠都只是一串無用的字串。

某本書上的規則:函式名在用於非函式呼叫的時候,都等效於函式指標。

說到子程序只是一個額外的流程,那他跟父程序的聯絡和區別是什麼呢?

我很想建議你看看linux核心的註解(有興趣可以看看,那裡才有本質上的瞭解),總之,fork後,子程序會複製父程序的task_struct結構,併為子程序的堆疊分配物理頁。理論上來說,子程序應該完整地複製父程序的堆,棧以及資料空間,但是2者共享正文段。

關於寫時複製:由於一般 fork後面都接著exec,所以,現在的 fork都在用寫時複製的技術,顧名思意,就是, 資料段,堆,棧,一開始並不複製,由父,子程序共享,並將這些記憶體設定為只讀。直到父,子程序一方嘗試寫這些區域,則核心才為需要修改的那片記憶體拷貝副 本。這樣做可以提高 fork的效率。

三.多執行緒

執行緒是可執行程式碼的可分派單元。這個名稱來源於“執行的線索”的概念。在基於執行緒的多工的環境中,所有程序有至少一個執行緒,但是它們可以具有多個任務。這意味著單個程式可以併發執行兩個或者多個任務。

簡而言之,執行緒就是把一個程序分為很多片,每一片都可以是一個獨立的流程。這已經明顯不同於多程序了,程序是一個拷貝的 流程,而執行緒只是把一條河流截成很多條小溪。它沒有拷貝這些額外的開銷,但是僅僅是現存的一條河流,就被多執行緒技術幾乎無開銷地轉成很多條小流程,它的偉 大就在於它少之又少的系統開銷。(當然偉大的後面又引發了重入性等種種問題,這個後面慢慢比較)。

關於多執行緒與多程序,執行緒安全,函式可重入詳見http://blog.csdn.net/hairetz/article/details/4281931/