衛(wèi)洪春
摘 要: 為了實現(xiàn)線程間消息的有效傳遞,在探討進(jìn)程和線程的概念、進(jìn)程和線程的區(qū)別、同步和異步處理機(jī)制、同步訪問技術(shù)、Linux下的軟中斷等技術(shù)的基礎(chǔ)上,提出一種實現(xiàn)線程間有效傳遞消息的方法。該方法可以傳遞任意類型、大小和格式的消息,并且完全由實現(xiàn)者決定和控制,具有較好的實用價值。
關(guān)鍵詞: 進(jìn)程; 線程; 異步處理; 同步訪問技術(shù); 消息傳遞
中圖分類號: TN911?34; TP391 文獻(xiàn)標(biāo)識碼: A 文章編號: 1004?373X(2015)14?0074?04
0 引 言
基于Linux操作系統(tǒng)的嵌入式開發(fā)和應(yīng)用已非常廣泛,在嵌入式系統(tǒng)或子系統(tǒng)中大部分應(yīng)用都采用單個CPU,應(yīng)用層軟件開發(fā)大多使用多線程開發(fā)技術(shù)。在同一個功能模塊中使用多個線程來實現(xiàn)各個子功能模塊,線程間的通信就不可避免。本文研究了一種在同一進(jìn)程中的2個不同線程間消息傳遞的實現(xiàn)方法。該方法的優(yōu)點是實現(xiàn)了消息的透明傳遞。傳遞消息與其傳遞過程無關(guān)。在這種方法中,待傳遞消息的具體內(nèi)容、構(gòu)成形式、信息量等都不受限制,可根據(jù)實際需要來構(gòu)建消息。這就能實現(xiàn)信息與載體的完全無關(guān)性和分離。利用該方法,待傳遞的消息可以是字符或整形數(shù)據(jù),也可以是滿足實際需要的復(fù)雜構(gòu)造型數(shù)據(jù),攜帶的具體信息數(shù)據(jù)等都不受限制,可根據(jù)實際需要進(jìn)行定義和構(gòu)造。
1 進(jìn)程、線程與信號量
進(jìn)程是具有一定獨立功能的程序在一定的內(nèi)存地址空間中,屬于這個程序的數(shù)據(jù)集合上的一次運行活動,它具有產(chǎn)生、運行、死亡的動態(tài)變化的活動過程。在進(jìn)程產(chǎn)生時,獲得了地址空間的系統(tǒng)資源才能運行,這就需要系統(tǒng)給予它所需的資源分配,所以進(jìn)程是系統(tǒng)進(jìn)行資源分配的一個獨立單位,也是用于組織和管理系統(tǒng)資源的單位。進(jìn)程將相關(guān)的資源組織在一起,這些資源包括:內(nèi)存地址空間、程序、數(shù)據(jù)等。將這些資源以進(jìn)程的形式組織起來,可以使操作系統(tǒng)更容易管理這些資源。
線程是進(jìn)程的一個執(zhí)行實體,它必須屬于某一個進(jìn)程。線程是CPU調(diào)度和分派的基本單位,也是進(jìn)程中能獨立運行的基本單位。線程不獨占處理CPU之外的系統(tǒng)資源,它與同屬一個進(jìn)程的其他線程共享進(jìn)程所擁有的這些資源,但只擁有一個線程的進(jìn)程情況除外。線程基本上只獨占在它運行時必不可少的資源(如程序計數(shù)器、一組寄存器和棧),但是由于線程比進(jìn)程更小,擁有的系統(tǒng)資源更少,基本上不擁有系統(tǒng)資源。在線程間切換時,對運行的上下文處理工作就變得更少,故對它的調(diào)度所付出的開銷就小得多,能更高效地提高系統(tǒng)內(nèi)多個任務(wù)間并發(fā)執(zhí)行的程度[1?5]。
1.1 進(jìn)程與線程的主要區(qū)別
進(jìn)程和線程存在以下幾個方面的主要區(qū)別。
(1) 地址空間和其他資源:進(jìn)程獨占系統(tǒng)分配給它的地址空間,它有自己的數(shù)據(jù)空間、代碼和數(shù)據(jù)。不同進(jìn)程間的這些資源是隔離的,并受到系統(tǒng)的保護(hù),不允許一個進(jìn)程直接訪問2個進(jìn)程的資源。若需要則通過進(jìn)程間的通信來實現(xiàn)。而同屬于一個進(jìn)程的多個線程則共享該系統(tǒng)分配給該進(jìn)程的地址空間、代碼和數(shù)據(jù),每個線程都可以直接訪問這些共享的資源。線程間不可共享的資源是它自己的堆棧、程序計數(shù)器及其執(zhí)行的上下文。CPU資源無論對于進(jìn)程還是線程都不是獨占的,而是共享的。進(jìn)程不能直接參與CPU資源的分配和調(diào)度,而線程是CPU資源分配和調(diào)度的單位,可以直接競爭分配和調(diào)度。進(jìn)程必須依賴它的線程參與到CPU資源的競爭分配和調(diào)度中,從而獲得CPU資源。多線程主要是為了減少在CPU調(diào)度切換時消耗的時間,充分發(fā)揮CPU的效率。
(2) 通信:由于進(jìn)程的地址空間、代碼空間和數(shù)據(jù)都受到保護(hù),相互之間都是透明的。所以要想在進(jìn)程間進(jìn)行信息的傳遞通信,就需要使用IPC來實現(xiàn)。屬于一個進(jìn)程的不同線程間的通信相當(dāng)容易,直接讀/寫進(jìn)程數(shù)據(jù)段(如全局變量)就可實現(xiàn)通信,通信變簡單了,但容易引起通信數(shù)據(jù)的不完整或不一致性的問題。這就需要在進(jìn)行線程間通信時,保證通信時的原子操作。
(3) 調(diào)度和切換:進(jìn)程進(jìn)行上下文切換時,需要保存切換出去進(jìn)程的地址空間、代碼空間、數(shù)據(jù)、當(dāng)前堆棧和程序計數(shù)器等資源信息,還需恢復(fù)已調(diào)度進(jìn)程的資源信息。而在同一個進(jìn)程間的不同線程的上下文切換時,僅需要保存切換出去的線程的堆棧和程序計數(shù)器,恢復(fù)調(diào)度過的線程的相關(guān)資源。因線程不需要維護(hù)和管理數(shù)據(jù)空間和運行空間,在進(jìn)行切換時,需要保存和恢復(fù)的內(nèi)容就比進(jìn)程少得多,所以線程上下文切換比進(jìn)程上下文切換要快得多,線程更能提高CPU的利用率。
1.2 同步與異步處理
同步處理,就是共同實現(xiàn)完成某一特定功能的多個進(jìn)程或線程間存在嚴(yán)格的運行次序,且運行的結(jié)果及返回具有依賴性。這些進(jìn)程或線程之間是相互配合、協(xié)助與合作的關(guān)系。在這一特定功能的協(xié)作尚未完成之前,是不會響應(yīng)其他的請求。
異步處理,就是多個進(jìn)程或線程間不存在運行次序問題和依賴關(guān)系。他們的運行不需要相互協(xié)同和配合,而是各自獨立運行。在這種處理方式下,就會產(chǎn)生同步訪問的問題。
同步訪問過程所面臨的問題是:共享數(shù)據(jù)或代碼段被多個線程并發(fā)或同時訪問,會出現(xiàn)共享數(shù)據(jù)的不完整性和不一致性。導(dǎo)致功能錯誤或無法實現(xiàn)功能,或引起異常甚至系統(tǒng)崩潰。保證線程對共享數(shù)據(jù)的原子操作,以達(dá)到時刻維護(hù)數(shù)據(jù)完成性和一致性的目的,就是使用同步訪問技術(shù)。
1.3 同步訪問技術(shù)
(1) 臨界區(qū):是指一段不可重入的代碼,對于一個線程必須對臨界區(qū)執(zhí)行原子操作,即要么不執(zhí)行臨界區(qū)的代碼,要么在執(zhí)行臨界區(qū)代碼時一次性執(zhí)行完成且中間不得被其他線程執(zhí)行。在任意時刻只允許一個線程對共享資源進(jìn)行訪問,如果有多個線程試圖訪問臨界區(qū),那么在有一個線程進(jìn)入后,其他試圖訪問公共資源的線程將被掛起,并一直等到進(jìn)入臨界區(qū)的線程離開,臨界區(qū)在被釋放后,其他線程才可以搶占。
(2) 互斥量:采用互斥對象機(jī)制。只有擁有互斥對象的線程才有訪問公共資源的權(quán)限,因為互斥對象只有一個,所以能保證公共資源不會同時被多個線程訪問?;コ獠粌H能實現(xiàn)同一應(yīng)用程序的公共資源安全共享,還能實現(xiàn)不同應(yīng)用程序的公共資源安全共享。
(3) 信號量:其是一個互斥的資源,在任何時刻只能有一個線程擁有它。當(dāng)信號量被一個線程持有后,其他線程就獲取不到該信號量的持有權(quán),而被掛起等待,一直等待持有這個信號量的線程將他釋放出來,再進(jìn)行競爭這個信號量的持有權(quán)。信號量保證了在某一時間段內(nèi)只有一個線程訪問臨界區(qū),而其他需要訪問的線程進(jìn)行等待。保證了線程對臨界區(qū)的原子操作。
(4) 事件:通過通知的方式來保持線程的同步,可以方便地實現(xiàn)對多個線程的異步處理操作。
1.4 Linux下的軟中斷信號signal
軟中斷信號(signal,簡稱為信號)用來通知一個線程,有其他線程向它發(fā)送了異步事件,需要它進(jìn)行適當(dāng)?shù)奶幚?。信號只是用來通知某線程發(fā)生了什么事件,并不會給該線程傳遞任何有關(guān)該事件的數(shù)據(jù),線程對收到的各種信號如何處理,由該線程自己決定。不過在Linux的進(jìn)程中處理方法通常有3種:第1種是類似中斷的處理程序,對于需要處理的信號,進(jìn)程可以指定處理函數(shù),由該函數(shù)來處理;第2種是忽略某個信號,對該信號不做任何處理,就象未發(fā)生過一樣;第3種是對該信號的處理保留系統(tǒng)的默認(rèn)值,這種缺省操作對大部分的信號而言是進(jìn)程終止。進(jìn)程通過系統(tǒng)調(diào)用signal來指定進(jìn)程對某個信號的處理行為。在進(jìn)程表的表項中有一個軟中斷信號域,該域的每位對應(yīng)一個信號。當(dāng)有信號發(fā)送給進(jìn)程時,對應(yīng)位置位。由此可以看出,進(jìn)程對不同的信號可以同時保留,但對于同一個信號,進(jìn)程在處理之前并不知道來了多少個[6?10]。
1.5 不可靠信號與可靠信號
不可靠信號:當(dāng)向一個進(jìn)程發(fā)送singal時,當(dāng)進(jìn)程還沒有處理該信號(此時被稱作pending,未決信號)或正在調(diào)用信號處理函數(shù)時,進(jìn)程又收到了一個同樣的信號,kernel會把第2個信號丟棄,或者與一個信號合并,只對這個信號處理1次,而不是收到這個信號多少次就處理多少次。不可靠的信號是指1~31的信號,這些都是從Unix系統(tǒng)中保留下來的。
可靠信號是指不論信號是否相同,都不會丟棄或合并,信號進(jìn)行排隊處理,信號不丟失,收到多少次就會處理多少次。SIGRTMIN~SIGRTMAX都是可靠信號[11]。
2 線程間消息實現(xiàn)方法
消息傳遞方法實現(xiàn)原理:使用與1個進(jìn)程中的2個線程間的消息和參數(shù)傳遞,它由2部分組成,一部分是把待發(fā)送的信息內(nèi)容放到指定的共享內(nèi)存空間中,然后使用Linux下的signal信號向接收線程發(fā)異步信號;另一部分是接收線程收到信號后,到指定的所屬進(jìn)程內(nèi)存空間獲取具體的消息內(nèi)容,從而實現(xiàn)線程間的異步通信,且可以傳遞較多的數(shù)據(jù)和參數(shù)。由于在線程間使用共享的內(nèi)存空間用于存放傳遞消息的內(nèi)容;所以在存放和提取消息內(nèi)容時,需要使用前述的線程間同步與互斥機(jī)制,保證消息內(nèi)容在傳遞過程中不被破壞和保持?jǐn)?shù)據(jù)的完整性。在線程間實現(xiàn)消息通告方式采用Linux的signal方式,即軟件中斷方式。它是異步事件,由操作系統(tǒng)的內(nèi)核實現(xiàn)。采用異步通告方式,是為了充分利用系統(tǒng)資源,特別是CPU資源。
2.1 信號的選擇
在上述論述中已經(jīng)論述了信號分為不可靠信號和可靠信號2種。為了保證消息被安全、可靠地傳遞給對方,就必須選擇可靠信號。由于大部分可靠信號都有專門的用途,并且在這種消息實現(xiàn)中,所起的作用僅是異步方式通知對方有消息來了,并不指特定的消息。具體是什么消息,可通過獲取到的消息的內(nèi)容獲知,選擇用于應(yīng)用程序的SIGUSER1即可。傳遞具體消息的內(nèi)容、格式和大小,在資源夠用的前提下,沒有任何限制,有極大的靈活性,只要保證消息封裝和消息提取保持一致即可[12]。
2.2 消息存放的位置
消息存放的位置有2種處理方式,固定位置和動態(tài)位置。
固定位置方式:發(fā)送和接收消息的線程都知道消息存放的地址,發(fā)送消息的線程就將具體封裝好的消息放到這個固定的內(nèi)存空間中;接收線程收到信號后,就到這個固定的內(nèi)存空間來獲取消息。
動態(tài)位置方式:由發(fā)送消息的線程在發(fā)送消息之前確定消息內(nèi)容存放在共享內(nèi)存空間位置,將消息內(nèi)容存放在這個空間之后,向接收線程發(fā)送帶有消息位置參數(shù)信號。接收線程在收到信號時提取出信息的位置參數(shù),然后到該位置獲取消息的內(nèi)容。Linux下的signal在進(jìn)程間是不能帶有參數(shù)的,但在線程間可以帶有一個整型參數(shù)。在這里所帶的整型參數(shù)是一個地址,即線程共享內(nèi)存中的地址。線程都可以訪問共享內(nèi)存空間,所以可以使用。
3 消息傳遞過程
在圖1所示的消息傳遞示意圖中,進(jìn)程P中包含4個線程,實現(xiàn)線程4向線程1消息傳遞。線程4是消息的發(fā)送者,線程1是消息的接收者。首先,線程4將欲傳遞的消息內(nèi)容沿著①的方向放在進(jìn)程p的內(nèi)存空間的地址a處;然后,線程4沿著②的方向向線程1發(fā)放signal信號,通知線程1,有消息發(fā)送給它,通知他去共享內(nèi)存空間的地址a處獲取消息;最后,線程1收到信號后,到地址a處提取消息,然后進(jìn)行消息處理,完成了一次消息傳遞過程。
圖1 線程間消息傳遞示意圖
這種消息傳遞方法是滿足線程1和線程4間異步并發(fā)的運行,線程4在向線程1發(fā)送消息時,不需要知道線程1的當(dāng)前運行狀態(tài),只在它需要向線程1發(fā)消息的任何時刻發(fā)送即可。使得傳遞的消息的類型和所帶參數(shù)個數(shù)以及格式等都與消息傳遞過程無關(guān),只與系統(tǒng)資源的限制有關(guān)。這也對2個線程進(jìn)行相互隔離,屏蔽了消息的產(chǎn)生和消息處理的具體過程。
4 結(jié) 語
在用戶空間的應(yīng)用程序中,一個進(jìn)程由多個線程來實現(xiàn)一個功能模塊中的不同子模塊的功能,實現(xiàn)各個子功能模塊的線程間即相互獨立,又相互關(guān)聯(lián)。在他們之間,都處于同一個進(jìn)程內(nèi)部,進(jìn)行通信是不可避免的。使用消息方法既能保證線程間的異步并發(fā)和同步協(xié)作,又能充分利用系統(tǒng)的資源。在實際應(yīng)用中,該方法能有效降低線程間的耦合性,明確各個線程的職責(zé)和范圍[13?17],降低軟件實現(xiàn)的復(fù)雜度,具有較強(qiáng)的現(xiàn)實作用和意義,為線程間進(jìn)行消息交互提供了較好的參考方法和思路。
參考文獻(xiàn)
[1] 眭俊華,劉慧娜,王建鑫,等.多核多線程技術(shù)綜述[J].計算機(jī)應(yīng)用,2013,33(1):239?242.
[2] 駱斌,費翔林.多線程技術(shù)的研究與應(yīng)用[J].計算機(jī)研究與發(fā)展,2000,37(4):407?412.
[3] 閆偉,葉建栲.多線程技術(shù)在Android 手機(jī)開發(fā)中的應(yīng)用[J].信息通信,2012(1):46?46.
[4] 施惠豐,袁道華.基于多核的多線程程序優(yōu)化研究[J].計算機(jī)技術(shù)與發(fā)展,2010,20(6):70?73.
[5] TANENBAUM A S.現(xiàn)代操作系統(tǒng)[M].陳向群,馬洪兵,譯.北京:機(jī)械工業(yè)出版社,2009.
[6] 毛德操,胡希明.Linux內(nèi)核源代碼情景分析[M].杭州:浙江大學(xué)出版社,2001.
[7] STEVENS W R,RAGO Stephen A.Unix環(huán)境高級編程[M].尤晉元,張亞英,戚正偉,譯.北京:人民郵電出版社,2006.
[8] 王文義,武華北.Linux中進(jìn)程間信號通信機(jī)制的分析及其應(yīng)用[J].計算機(jī)工程與應(yīng)用,2005(3):108?109.
[9] 屈志強(qiáng),喬靜,胡珊珊.Linux 信號在進(jìn)程控制中的應(yīng)用[J].計算機(jī)與現(xiàn)代化,2010(6):150?152.
[10] 鄭尚志,趙小龍,昌杰.Linux信號機(jī)制的分析與研究[J].科技資訊,2008(11):98?100.
[11] 陳毅東,李堂秋,鄭旭玲.Linux不可靠信號與可靠信號的比較實驗[J].廈門大學(xué)學(xué)報:自然科學(xué)版,2002,41(6):720?725.
[12] 冉鵬,顏紀(jì)迅.分時分區(qū)操作系統(tǒng)互斥信號量的設(shè)計與分析[J].計算機(jī)技術(shù)與發(fā)展,2013,23(1):43?46.
[13] 劉文峰,李程遠(yuǎn),李善平.嵌入式Linux操作系統(tǒng)的研究[J].浙江大學(xué)學(xué)報:工學(xué)版,2004,38(4):448?452.
[14] MCCONNELL Steve.代碼大全[M].金戈,湯凌,陳碩,等譯.北京:電子工業(yè)出版社,2006.
[15] 肖竟華,陳嵐.Linux 內(nèi)存管理實現(xiàn)的分析與研究[J].計算機(jī)技術(shù)與發(fā)展,2007,17(2):187?189.
[16] 王兆文,蔣澤軍,陳進(jìn)朝.一種提高Linux 內(nèi)存管理實時性的設(shè)計方案[J].計算機(jī)工程,2014,40(9):291?294.
[17] 段哲.嵌入式系統(tǒng)通信機(jī)制的研究與應(yīng)用[J].艦船電子工程,2010,30(7):81?83.