Linux 系統程序 執行緒之間的愛恨糾葛

2022-06-05 18:09:09 字數 4654 閱讀 2652

當乙個程式開始執行後,在開始執行到執行完畢退出這段時間內,它在記憶體中的部分就叫稱作乙個程序。

linux 是乙個多工的作業系統,也就是說,在同一時間內,可以有多個程序同時執行。我們大家常用的單cpu計算機實際上在乙個時間片段內只能執行一條指令。

那麼linux是如何實現多程序的同時執行的呢?

原來linux使用了一種稱為」 程序排程 「的手段:

首先,為每個程序指派一定的執行時間,這個時間通常很短,短到以毫秒為單位,然後依照某種規則,從眾多的程序中挑選乙個投入執行,其他程序暫時等待,當正在執行的那個程序時間耗盡,或者執行完畢退出,或因某種原因暫停,linux就會重新排程,挑選乙個程序投入執行,因為每個程序占用的時間片段都很短,從使用者的角度來看,就好像多個程序同時執行一樣。

在linux中,每個程序在建立的時都會被分配乙個資料結構,稱為程序控制塊(pcb)。

pcb中包含了很多重要的資訊,供系統排程和程序本事執行使用,其中最重要的莫過於程序的id,程序的id也被稱為程序標示符,是乙個非負的整數,在linux操作性系統中唯一的標誌乙個程序。

在最常使用的i386架構上,乙個非負的整數的取值是0~32767,這也是我們所可能取到的程序id,它就是程序的身份證號碼。

殭屍程序就是已經結束的程序,但是還沒有從程序表中刪除。殭屍程序太多會導致程序表裡面條目滿了,進而導致系統崩潰,倒是不占用系統資源。

在程序的狀態中,殭屍程序是非常特殊的一種,它已經放棄了幾乎所有的記憶體空間,沒有任何可執行**,也不能被排程,僅僅在程序列表中保留乙個為位置,記載該程序的退出狀態等資訊供其他程序收集;

除此之外,殭屍程序不再占用任何記憶體空間,它需要它的父程序來給它收屍,如果父程序沒安裝sigchld訊號處理函式呼叫wait或waitpid()等待子程序結束,又沒有顯示的忽略該訊號,那麼它就一直處於殭屍狀態。

如果父程序結束了,那麼init程序會自動接手這個子程序,為它收屍,他還是能夠被清除的。但是如果父程序是乙個迴圈,不會結束,那麼子程序就一直處於殭屍狀態。

殭屍程序產生的原因:

每個linux程序在程序表中都有乙個進入點(entry),核心程式在執行該程序時使用到的一切資訊都儲存在進入點。當使用ps命令檢視系統中的程序資訊時,看到的就是程序表中的相關資料。

當fork系統呼叫建立乙個新的程序以後,核心程序就會在程序表中給這個新程序分配乙個進入點,然後將相關資訊儲存在該進入點所對應的程序表中,這些資訊中有一項是父程序的識別碼。

當這個程序走完了自己的生命週期後,它會執行exit()系統呼叫,此時原來程序表中的資料會被該程序的退出碼、執行時所用的cpu時間等資料所取代,這些資料會一直保留到系統將它傳遞給它的父程序為止。

由此可見,殭屍程序的出現時間實在子程式終止後,但是父程序尚未讀取這些資料之前。

1、父程序通過wait和waitpid等函式等待子程序結束,這會導致父程序掛起

2、如果父程序很忙,那麼可以用signal函式為sigchld安裝handler,因為子程序結束後,父程序會收到該訊號,可以在handler中呼叫wait**。

3、如果父程序不關心子程序什麼時候結束,那麼可以用「singal(sigchld),sig_ign」通知核心,自己對子程序的結束不感興趣,那麼子程序結束後,核心會**,並不再給父程序傳送訊號。

4、還有一些技巧,就是fork()兩次,父程序fork乙個子程序,然後繼續工作,子程序fork乙個孫程序後退出,那麼孫程序被init接管,孫程序結束後,init會**,不過子程序**還要自己做。

我們先打個比方,多執行緒是十字路口多執行緒是平面交通系統,造價低,但是紅綠燈多,老堵車,而多程序是則是立交橋,雖然造價高,上下坡多耗油,但是不堵車。這是乙個抽象的概念。相信大家看完會有這種感覺。

程序和執行緒是兩個相對的概念,通常來說,乙個程序可以定義程式的乙個例項(instance)。

在win32中,程序並不執行什麼,它只是佔據應用程式所使用的位址空間。

為了讓程序完成一定的工作,程序必須至少占有乙個執行緒,正是這個執行緒負責包含程序位址空間中的**。

實際上,乙個程序可以包含幾個執行緒,它們可以同時執行程序位址空間中的**。為了做到這一點,每個執行緒有自己的一組cpu暫存器和堆疊。

每個程序中至少有 乙個執行緒在執行其位址空間中的**。如果沒有執行緒執行程序位址空間中的**,程序也就沒有繼續存在的理由,系統將自動清除程序及其位址空間。

建立乙個程序時,它的第乙個執行緒稱為主線程(primary thread),由系統自動生成。然後可以由這個主線程生成額外的執行緒,而這些執行緒,又可以生成更多的執行緒。

在執行乙個多執行緒的程式時,從表面上看,這些執行緒似乎在同時執行。而實際情況並非如此,為了執行所有的這些執行緒,作業系統為每個獨立執行緒安排一些cpu時間。

單cpu作業系統以時間片輪轉方式向執行緒提供時間片(quantum),每個執行緒在使用完時間片後交出控制,系統再將cpu時間片分配給下乙個執行緒。

由於每個時間片足夠的短,這樣就給人一種假象,好像這些執行緒在同時執行。建立額外執行緒的唯一目的就是盡可能地利用cpu時間。

使用多執行緒程式設計可以給程式設計師帶來很大的靈活性,同時也使原來需要複雜技巧才能解決的問題變得容易起來。但是,不應該人為地將編寫的程式分成一些碎片,讓這些碎片按各自的執行緒執行,這不是開發應用程式的正確方法。

執行緒很有用,但當使用執行緒時,可能會在解決老問題的同時產生新問題。

例如要開發乙個字處理程式,並想讓列印功能作為單獨的執行緒自己執行。這聽起來是很好的主意,因為在列印時,使用者可立即返回,開始編輯文件。

但這樣一來,在該文件被列印時文件中的資料就有可能被修改,列印的結果就不再是所期望的內容。

也許最好不要把列印功能放在單獨的執行緒中,不過如果一定要用多執行緒的話,也可以考慮用下面的方法解決:

第一種方法是鎖定正在列印的文件,讓使用者編輯其他的文件,這樣在結束列印之前,該文件不會作任何修改;

另乙個方法可能更有效一些,即可以把該文件拷貝到乙個臨時檔案中,列印這個臨時檔案的內容,同時允許使用者對原來的文件進行修改。

當包含文件的臨時檔案列印完成時,再刪去這個臨時檔案。通過上面的分析可以看出,多執行緒在幫助解決問題的同時也可能帶來新問題。因此有必要弄清楚,什麼時候需要建立多執行緒,什麼時候不需要多執行緒。總的來說,多執行緒往往用於在前台操作的同時還需要進行後台的計算或邏輯判斷的情況。

在mfc中,執行緒被分為兩類,即工作執行緒和使用者介面執行緒。如果乙個執行緒只完成後台計算,不需要和使用者互動,那麼可以使用工作執行緒;

如果需要建立乙個處理使用者介面的執行緒,則應使用使用者介面執行緒。

這兩者的主要區別在於,mfc框架會給使用者介面執行緒增加乙個訊息迴圈,這樣使用者介面執行緒就可以處理自己訊息佇列中的訊息。

這樣看來,如果需要在後台作一些簡單的計算(如對電子**的重算),則首先應考慮使用工作執行緒,而當 後台執行緒需要處理比較複雜的任務;

確切地說,當後台執行緒的執行過程會隨著實際情況的不同而改變時,就應該使用使用者介面執行緒,以便能對不同的訊息作出響應。

當系統需要同時執行多個程序或多個執行緒時,有時會需要指定執行緒的優先順序。執行緒的優先順序一般是指這個執行緒的基優先順序,即執行緒相對於本程序的相對優先順序和包含此執行緒的程序的優先順序的結合。

作業系統以優先順序為基礎安排所有的活動執行緒,系統的每乙個執行緒都被分配了乙個優先順序,優先順序的範圍從0到31。

執行時,系統簡單地給第乙個優先順序為31的執行緒分配cpu時間,在該執行緒的時間片結束後,系統給下乙個優先順序為31的執行緒分配cpu時間。

當沒有優先順序為31的執行緒時,系統將開始給優先順序為30的執行緒分配cpu時間,以此類推。

除了程式設計師在程式中改變執行緒的優先順序外,有時程式在執行過程中系統也會自動地動態改變執行緒的優先順序,這是為了保證系統對終端使用者的高度響應性。

比如使用者按了鍵盤上的某個鍵時,系統就會臨時將處理wm_keydown訊息的執行緒的優先順序提高2到3。cpu按乙個完整的時間片執行執行緒,當時間片執行完畢後,系統將該執行緒的優先順序減1。

在使用多執行緒程式設計時,還有乙個非常重要的問題就是執行緒同步。所謂執行緒同步是指執行緒之間在相互通訊時避免破壞各自資料的能力。同步問題是由前面說到的win32系統的cpu時間片分配方式引起的。

雖然在某一時刻,只有乙個執行緒占用cpu(單cpu時)時間,但是沒有辦法知道在什麼時候,在什麼地方執行緒被打斷,這樣如何保證執行緒之間不破壞彼此的資料就顯得格外重要。

在mfc中,可以使用4個同步物件來保證多執行緒同時執行。它們分別是臨界區物件(ccriticalsection)、互斥量物件(cmutex)、訊號量物件(cs emaphore)和事件物件(cevent)。

在這些物件中,臨界區物件使用起來最簡單,它的缺點是只能同步同乙個程序中的執行緒。

另外,還有一種基本的方法,本文稱為線性化方法,即在程式設計過程中對一定資料的寫操作都在乙個執行緒中完成。

這樣,由於同一執行緒中的**總是按順序執行的,就不可能出現同時改寫資料的情況。

執行緒和程序在使用上各有優缺點:執行緒執行開銷小,但不利於資源管理和保護;而程序正好相反。

在資源利用率方面:執行緒的資源率比較好也是因為他們在同一位址空間內。 在同步方面:執行緒使用公共變數/記憶體時需要使用同步機制,因為他們在同一位址空間內程序中:子程序是父程序的複製品,子程序獲得父程序資料空間、堆和棧的複製品。

最後,不管你是轉行也好,初學也罷,高階也可,如果你想學程式設計~

【值得關注】我的c/c++程式設計學習交流俱樂部!

執行緒之間和程序之間的同步

今天學習的內容為 利用互斥事件來控制線程之間 程序之間的同步問題。其實,程序之間的同步也就類似於執行緒之間的同步。互斥事件的作用就是為了保證任乙個時間內,只有乙個執行緒對對公共資源進行操作。下面來看一下執行緒之間的同步,如果是在乙個程序內的執行緒同步問題的話。這樣的例子很多,我就舉乙個最簡單的,執行...

程序之間 執行緒之間的通訊方式

1 程序間的8中通訊方式 1 無名管道 pipe 管道是一種半雙工的通訊方式,資料只能單向流動,而且只能在具有親緣關係的程序間使用。程序的親緣關係通常是指父子程序關係。2 2 高階管道 popen 將另乙個程式當做乙個新的程序在當前程式程序中啟動,則它算是當前程式的子程序,這種方式我們成為高階管道方...

執行緒鎖用於程序之間

實現程序之間上鎖有多種方式,可以用檔案鎖,但檔案鎖要涉及到檔案系統,十分耗時。其實程序之間也可以用執行緒鎖,因為執行緒鎖不僅可以用於執行緒之間同樣可以用於程序之間。為了在多個程序之間使用執行緒鎖,必須做到 1 互斥鎖變數必須儲存在為所有程序所共享的記憶體中 2 必須通知執行緒函式庫互斥鎖是在不同程序...