進程、線程

1. 定義

進程是執行中一段的程序,即一旦程序被載入到內存中並準備執行,它就是一個進程。進程是表示資源分配的基本概念,又是調度運行的基本單位,是系統中的併發執行的單位。

線程是每一個進程中執行的每個任務就是一個線程。是進程中的一條執行路線。線程是進程中執行運算的最小單位。線程是任務調度和執行的最小單元。

 

寫好的a.c文件,經過gcc編譯:gcc a.c,就會生成a.out文件(gcc -c 編譯;gcc -o 聯結),可通過 ./a.out 運行,可通過size來查看虛擬內存組織管理情況。

 

這麼說好像有點抽象,可以先來說說之前的單核CPU爲什麼可以執行多任務多程序,是因爲進程調度機制來實現的,每個進程都會被分配獨立的用戶空間(數據段、代碼段、堆棧段等)和內核空間(進程上下文等),這就會引起一些開銷,對於單機而言,雖然有影響,但不是那麼明顯,當多臺主機訪問服務器的時候,同時內一臺服務器會面對多個請求,這個問題就顯現出來了,首先,1. 切換進程,會佔用很多的資源,爲每個請求都創建一個進程,顯然會佔用大量的資源;2. 切換進程,會需要保存現場和恢復現場,這會佔用很多的時間,響應延遲就會比較高。

也這是因爲這兩個原因,出現了線程的概念。線程是進程中的一個執行單元,它共享進程的地址空間,但是有自己的獨立棧空間,這樣面對多個請求時只需要建立一個線程響應,就可以極大的節約空間。在同一個進程中的線程做切換時不需要太多的保存和恢復現場就能夠實現線程的切換;只有進程間的線程做切換時纔會進行進程切換,提高了效率。

推薦一個視頻:https://www.56.com/u50/v_MTUwMTU1Njcx.html

2. 進程的創建

fork創建的新進程被稱爲子進程(child process)。該函數被調用一次,但返回兩次。兩次返回的區別是子進程的返回值是0,而父進程的返回值則是新進程(子進程)的進程 id。將子進程id返回給父進程的理由是:因爲一個進程的子進程可以多於一個,沒有一個函數使一個進程可以獲得其所有子進程的進程id。對子進程來說,之所以fork返回0給它,是因爲它隨時可以調用getpid()來獲取自己的pid;也可以調用getppid()來獲取父進程的id。(進程id 0總是由交換進程使用,所以一個子進程的進程id不可能爲0 )。

fork之後,操作系統會複製一個與父進程完全相同的子進程,雖說是父子關係,但是在操作系統看來,他們更像兄弟關係,這2個進程共享代碼空間,但是數據空間互相獨立的,子進程數據空間中的內容是父進程的完整拷貝,指令指針也完全相同,子進程擁有父進程當前運行到的位置兩進程的程序計數器pc值相同,也就是說,子進程是從fork返回處開始執行的),但有一點不同,如果fork成功,子進程中fork的返回值是0,父進程中fork的返回值是子進程的進程號,如果fork不成功,父進程會返回錯誤。可以這樣想象,2個進程一直同時運行,而且步調一致,在fork之後,他們分別作不同的工作,也就是分岔了。這也是fork爲什麼叫fork的原因。

至於哪一個最先運行,可能與操作系統(調度算法)有關,而且這個問題在實際應用中並不重要,如果需要父子進程協同,可以通過原語的辦法解決。

3. 進程與線程的異同點

  1. 一個線程只能屬於一個進程,但是一個進程可以擁有多個線程。多線程處理就是允許一個進程中在同一時刻執行多個任務。(進程和線程的關係:1對n,n對1

  2. 線程是一種輕量級的進程,與進程相比,線程給操作系統帶來側創建、維護、和管理的負擔要輕,意味着線程的代價或開銷比較小

  3. 線程沒有地址空間,線程包含在進程的地址空間中。線程上下文只包含一個堆棧、一個寄存器、一個優先權,線程文本包含在他的進程 的文本片段中,進程擁有的所有資源都屬於線程所有的線程共享進程的內存和資源。 同一進程中的多個線程共享代碼段(代碼和常量),數據段(全局變量和靜態變量),擴展段(堆存儲)。但是每個線程擁有自己的段, 寄存器的內容,棧段又叫運行時段,用來存放所有局部變量和臨時變量

  4. 父和子進程使用進程間通信機制,同一進程的線程通過讀取和寫入數據進程變量來通信。

  5. 進程內的任何線程都被看做是同位體,且處於相同的級別。不管是哪個線程創建了哪一個線程,進程內的任何線程都可以銷燬、掛起、恢復和更改其它線程的優先權。線程也要對進程施加控制,進程中任何線程都可以通過銷燬主線程銷燬進程,銷燬主線程將導致該進程的銷燬,對主線程的修改可能影響所有的線程。

  6. 進程不對任何其他進程施加控制,進程的線程可以對同一進程的其它線程施加控制。子進程不能對父進程施加控制,進程中所有線程都可以對主線程施加控制。

  7. 無論是static還是非static的全局變量,如果不加限制隨意訪問的話易出現同步問題。無論是static還是非static的局部變量,每個線程都是私有的,其他線程不會對其進行干擾。

    在多線程中不加限制的隨意訪問非static局部變量可能會導致運算結果出錯
    在多線程中不加限制的隨意訪問非static全部變量可能會導致運算結果出錯
    在多線程中不加限制的隨意訪問static局部變量可能會導致運算結果出錯
    在多線程中不加限制的隨意訪問static全部變量可能會導致運算結果出錯

4.進程和線程的關係

  • 一個進程中有一個到多個線程。一個線程只屬於一個進程。
  • 一個進程中的多個線程之間是一種競爭關係。競爭的是進程獨立的內存空間和系統資源。

可參考視頻:https://v.qq.com/x/page/n0862wocvk7.html

5.進程間的通信機制

進程劃分的原則:如果執行和操作的資源比較獨立,一般建議使用進程(詳細介紹在進程和線程的內容),儘量減少進程間的耦合度。

各個進程比較獨立,不能直接訪問對方的資源,但是進程不是孤立的,必然涉及到數據的交互,舉例:一個網絡音頻播放器,主要涉及到兩個操作:(1)網絡下載;(2)音頻解碼和播放。一般會放在兩個進程,但是網絡下載的資源在哪裏來供音頻解碼和下載,於是誕生了進程間的通信。

基本通信方式:

參見視頻鏈接:https://www.bilibili.com/video/av48094762/?p=41,感謝萬能B站。

  • 爲什麼需要通信機制:
  1. 信號量及管程,只能傳遞很簡單的信息,不能傳遞複雜信息
  2. 管程不適用多處理器情況

所以在傳遞大量信息時,需要新的處理機制。

  • 消息傳遞

send & receive原語

發送進程將消息的內容和大小都準備好,但是由於在自己的地址空間進行操作,與接受進程的地址空間是相互獨立的,因此發送消息這件事就交由操作系統來完成。

操作系統在它的區域裏設置一組消息緩衝區,每一個消息都由一個buffer來存放。

當發送進程想要將消息發送給接受進程時,會調用發送原語send,而發送原語實際上就是陷入內核,由操作系統來完成發送工作,操作系統主要做的工作就是將發送消息拷貝到某一個空的消息緩衝區中,然後把消息掛接到接收進程的消息隊列的末尾。

此時消息進程已經完成了消息的發送工作。那麼對於接收進程,什麼時候被CPU調度執行了,那麼這個時候執行receive原語,也是陷入內核,操作系統會幫它把消息複製到接收進程的地址空間。

這樣就完成了發送 -> 接收的過程。

用P、V操作實現send原語:

  • 共享內存

1.需要在物理內存建立一塊大家都可以訪問的物理內存空間,並分別建立映射;

2.讀者寫者問題,可以同時讀,但是不能同時寫數據。

  • 管道通信方式 PIPE

可以有很多進程向管道中寫,有很多進程來接受消息。

  • 信號量和信號

信號和信號量是不同的,它們雖然都可以用來實現同步和互斥,但是前者是使用信號處理器來進行的,後者是使用P、V操作來實現的。

 

  • 原子操作(1)

  • 屏障

 


參考博客及文檔:

https://baijiahao.baidu.com/s?id=1611925141861592999&wfr=spider&for=pc

https://www.cnblogs.com/LUO77/p/5816326.html

https://blog.csdn.net/kuang_tian_you/article/details/86691725

只做學習交流,如果有侵權,請聯繫我,感謝。

發佈了57 篇原創文章 · 獲贊 36 · 訪問量 7059
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章