進程和線程的概念、區別和聯繫

進程(process)和線程(thread)是操做系統的基本概念,可是它們比較抽象,不容易掌握。html

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

進程與線程的一個簡單解釋編程

在這個簡單易懂的類比下,瞭解一下進程和線程的宏觀概念安全

進程,是併發執行的程序在執行過程當中分配和管理資源的基本單位,是一個動態概念,竟爭計算機系統資源的基本單位。每個進程都有一個本身的地址空間,即進程空間或(虛空間)。進程空間的大小 只與處理機的位數有關,一個 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/

相關文章
相關標籤/搜索