計算機基礎
追的番進度條永遠不會成長,學習的東西倒是變得更長更難了(ㄒoㄒ)/~~ 相比于上一篇的內(nèi)存管理郑原,我覺得這篇更容易理解一些唉韭。
操作系統(tǒng)篇4-進程和線程,進程間的通信
進程
我們編寫的代碼只是一個存儲在硬盤的靜態(tài)文件犯犁,通過編譯后就會生成二進制可執(zhí)行文件属愤。當我們運行這個可執(zhí)行文件后,它會被裝載到內(nèi)存中酸役,接著CPU會執(zhí)行程序中的每一條指令, 那么這個運行中的程序住诸,就被稱為進程驾胆。
我們把操作系統(tǒng)做某件事,抽象成一種概念,稱之為一個任務只壳。一個進程可以對應一個任務俏拱,也可以對應多個任務。
早期的計算機只有一個CPU,多個任務需要運行怎么辦?需要依次排隊等待吼句。串行執(zhí)行锅必,一個任務執(zhí)行完畢,才能執(zhí)行下一個惕艳。這種方式存在著明顯的弊端搞隐,假設排在前面的A任務需要執(zhí)行5小時,而排后面的B任務僅需要1分鐘远搪,那么B任務必須等待A任務5小時完成后劣纲,才能執(zhí)行,這種方式顯得極其不靈活谁鳍。
后來就有了多任務系統(tǒng)癞季,在CPU同一時間只能處理一個任務的前提下, 每個任務有一定的執(zhí)行時長倘潜, 比如任務A執(zhí)行0.001s, 切換到任務B執(zhí)行0.05s, 再切換到任務C執(zhí)行0.1..不斷循環(huán)绷柒。這種機制也就可以在一定程度 上解決上述任務B需要長時間等待的問題。
由于CPU速度非充桃颍快废睦,這種多個任務不斷切換,會給用戶一種任務并行執(zhí)行的錯覺养泡,這種也被稱為是偽并行調度嗜湃。既然有偽并行,那么也會有真并行澜掩。
在現(xiàn)代計算機中购披,常見的CPU核數(shù)可以達到8核(ps:?核數(shù)即一個CPU由多少個核心組成,核心數(shù)越多肩榕,代表這個CPU的運轉速度越快今瀑,性能越好。對于同一個數(shù)據(jù)處理点把,一核CPU相當于1個人處理數(shù)據(jù)橘荠,雙核CPU相當于2個人處理同一個數(shù)據(jù),4核CPU相當于4個人去處理同一個數(shù)據(jù)郎逃,因此處理核心數(shù)越多哥童,CPU的工作效率也就越高。)甚至更多,操作系統(tǒng)可將每一個核視為一個CPU,那么8核CPU就可以真并行執(zhí)行8個任務褒翰。
并發(fā)(偽并行)和并行如下圖:
偽并行雖然可以解決上述任務等待的問題贮懈,但是依然還存在一系列未解之謎:
●每個任務應該執(zhí)行多長時間?
●如何找到要執(zhí)行的下一個任務?
●有些任務涉及了資源操作匀泊,執(zhí)行到一半,切換任務,那么這些資源怎么辦?
為了解決上面一系列謎題朵你,我們需要一種模型對任務進行詳盡的描述記錄各聘。
進程的狀態(tài)
那么什么原因會導致進程會被創(chuàng)建,從而生成PCB呢?常見的有以下幾種
1.系統(tǒng)初始化
2.用戶通過系統(tǒng)提供的API創(chuàng)建新進程
3.批處理作業(yè)初始化(什么是批處理作業(yè))
4.由現(xiàn)有進程派生子進程一個進程
因為某種原因被創(chuàng)建了抡医,那么它可以按照以下步驟進行一系列的初始化
1.給新進程分配一個進程ID
2分配內(nèi)存空間
3.初始化PCB
4.進入就緒隊列
五狀態(tài)模型
如圖躲因,進入就緒隊列,其狀態(tài)就會變?yōu)榫途w態(tài)忌傻。各個狀態(tài)之間的關系描述如下:
就緒->運行:當操作系統(tǒng)內(nèi)存在著調度程序大脉,當需要運行一個新進程時, 調度程序選擇一個就緒態(tài)的進程, 讓其進入運行態(tài)水孩。
運行->就緒:運行態(tài)的進程镰矿,會占有CPU 。每個進程會被分配一定的執(zhí)行時間俘种,當時間結束后,重新回到就緒態(tài)秤标。
運行->阻塞:進程請求調用系統(tǒng)的某些服務,但是操作系統(tǒng)沒法立即給它(比如這種服務可能要耗時初始化宙刘,比如/0資源需要等待) ,那么它就會進入阻塞態(tài)苍姜。
阻塞->就緒:當?shù)却Y束了,就由阻塞態(tài)進入就緒態(tài)。
運行->終止:當進程表示自己已經(jīng)完成了荐类,它會被操作系統(tǒng)終止怖现。
當存在多個進程時茁帽,由于同一時間只能有一個進程在執(zhí)行玉罐,那么如何去管理這一系列的處于阻塞態(tài)和就緒態(tài)的進程?
一般來說,會使用就緒隊列潘拨,和阻塞隊列,讓處于阻塞態(tài)和就緒態(tài)的進程進入隊列吊输,排隊執(zhí)行。
七狀態(tài)模型
(與五狀態(tài)模型一樣铁追,只是描述狀態(tài)的說法不同)
一旦排隊的進程多了季蚂,對于有限的內(nèi)存空間將會是極大的考驗。為了解決內(nèi)存占用問題琅束,可以將一部分內(nèi)存中的進程交換到磁盤中扭屁,這些被交換到磁盤的進程,會進入掛起狀態(tài)
掛起狀態(tài)可以分為兩種:
●阻塞掛起狀態(tài):進程在外存(硬盤)并等待某個事件的出現(xiàn);
●就緒掛起狀態(tài):進程在外存(硬盤) ,但只要進入內(nèi)存,即刻立刻運行;
這兩種掛起狀態(tài)加上前面的五種狀態(tài),就變成了七種狀態(tài)變遷,見如下圖:
進程的控制結構
對于一個被執(zhí)行的程序,操作系統(tǒng)會為該程序創(chuàng)建一個進程涩禀。 進程作為一種抽象概念料滥, 可將其視為一個容器,該容器聚集了相關資源艾船,包括地址空間葵腹,線程高每,打開的文件,保護許可等践宴。而操作系統(tǒng)本身是一個程序,有一句經(jīng)典的話程序=算法+數(shù)據(jù)結構鲸匿,因此對于單個進程,可以基于一種數(shù)據(jù)結構來表示它阻肩,這種數(shù)據(jù)結構稱之為進程控制塊(PCB)
在操作系統(tǒng)中带欢,是用進程控制塊(process control block, PCB) 數(shù)據(jù)結構來描述進程的。
PCB是進程存在的唯一標識磺浙,這意味著一個進程的存在洪囤,必然會有一個PCB,如果進程消失了,那么PCB也會隨之消失撕氧。
PCB具體包含什么信息呢?
通常是通過鏈表的方式進行組織瘤缩,把具有相同狀態(tài)的進程鏈在一起,組成各種隊列伦泥。比如:
●將所有處于就緒狀態(tài)的進程鏈在一起剥啤,稱為就緒隊列;
●把所有因等待某事件而處于等待狀態(tài)的進程鏈在一 起就組成各種阻塞隊列;
●另外,對于運行隊列在單核CPU系統(tǒng)中則只有一個運行指針了,因為單核CPU在某個時間不脯,只能運行一個程序府怯。那么,就緒隊列和阻塞隊列鏈表的組織形式如下圖:
除了鏈接的組織方式防楷,還有索引方式牺丙,它的工作原理:將同一狀態(tài)的進程組織在一個索引表中, 索引表項指向相應的PCB,不同狀態(tài)對應不同的索引表。
一般會選擇鏈表,因為可能面臨進程創(chuàng)建复局,銷毀等調度導致進程狀態(tài)發(fā)生變化冲簿,所以鏈表能夠更加靈活的插入和刪除。
進程的切換
當一個正在運行中的進程被中斷亿昏,操作系統(tǒng)指定另一個就緒態(tài)的進程進入運行態(tài)峦剔,這個過程就是進程切換,也可以叫上下文切換角钩。該切換過程一般涉及以下步驟:
1.保存處理器上下文環(huán)境:將CPU程序計數(shù)器和寄存器的值保存到當前進程的私有堆棧里吝沫。
2.更新當前進程的PCB (包括狀態(tài)更變)。
3.將當前進程移到就緒隊列或者阻塞隊列递礼。
4.根據(jù)調度算法惨险,選擇就緒隊列中一個合適的新進程,將其更改為運行態(tài)脊髓。
5.更新內(nèi)存管理的數(shù)據(jù)結構辫愉。
6.新進程內(nèi)對堆棧所保存的上下文信息載入到CPU的寄存器和程序計數(shù)器,占有CPU供炼。
發(fā)生進程上下文切換有哪些場景?
●為了保證所有進程可以得到公平調度一屋,CPU 時間被劃分為一段段的時間片,這些時間片再被輪流分配給各個進程窘疮。這樣,當某個進程的時間片耗盡了冀墨,就會被系統(tǒng)掛起,切換到其它正在等待CPU的進程運行;
●進程在系統(tǒng)資源不足(比如內(nèi)存不足)時闸衫,要等到資源滿足后才可以運行,這個時候進程也會被掛起诽嘉,并由系統(tǒng)調度其他進程運行;
● 當進程通過睡眠函數(shù)sleep這樣的方法將自己主動掛起時蔚出,自然也會重新調度;
● 當有優(yōu)先級更高的進程運行時,為了保證高優(yōu)先級進程的運行虫腋,當前進程會被掛起骄酗,由高優(yōu)先級進程來運行;
●發(fā)生硬件中斷時,CPU上的進程會被中斷掛起悦冀,轉而執(zhí)行內(nèi)核中的中斷服務程序;
以上,就是發(fā)生進程上下文切換的常見場景了趋翻。
線程
在早期的操作系統(tǒng)中都是以進程作為獨立運行的基本單位,直到后面盒蟆,計算機科學家們又提出了更小的能獨立運行的基本單位踏烙,也就是線程。
線程是進程當中的一條執(zhí)行流程历等。
同一個進程內(nèi)多個線程之間可以共享代碼段讨惩、數(shù)據(jù)段、打開的文件等資源寒屯,但每個線程各自都有一套獨立的寄存器和棧荐捻,這樣可以確保線程的控制流是相對獨立的。
我們一開始提及過,操作系統(tǒng)底層存在調度程序寡夹,調度程序可調度任務处面,而單線程進程,每個進程可以對應一個任務。現(xiàn)在要出,對于多線程的進程鸳君,每一個線程最終對于調度程序來說农渊,都是一個任務,如下圖(Linux系統(tǒng))患蹂。因此也有一種流行的說法線程是CPU調度的基本單位。
線程的上下文切換
線程與進程最大的區(qū)別在于:線程是調度的基本單位砸紊,而進程則是資源擁有的基本單位传于。
所以,所謂操作系統(tǒng)的任務調度醉顽,實際上的調度對象是線程,而進程只是給線程提供了虛擬內(nèi)存沼溜、全局變量等資源。
對于線程和進程游添,我們可以這么理解:
●當進程只有一個線程時系草,可以認為進程就等于線程;
●當進程擁有多個線程時通熄,這些線程會共享相同的虛擬內(nèi)存和全局變量等資源,這些資源在上”下文切換時是不需要修改的;
另外找都,線程也有自己的私有數(shù)據(jù)唇辨,比如棧和寄存器等,這些在上下文切換時也是需要保存的能耻。
線程上下文切換的是什么?
這還得看線程是不是屬于同一個進程:
●當兩個線程不是屬于同一個進程赏枚,則切換的過程就跟進程上下文切換一樣;
●當兩個線程是屬于同一個進程,因為虛擬內(nèi)存是共享的晓猛,所以在切換時饿幅,虛擬內(nèi)存這些資源就保持不動,只需要切換線程的私有數(shù)據(jù)戒职、寄存器等不共享的數(shù)據(jù);
所以栗恩,線程的上下文切換相比進程,開銷要小很多洪燥。
進程調度
進程都希望自己能夠占用CPU進行工作,那么這涉及到前面說過的進程上下文切換摄凡。
一旦操作系統(tǒng)把進程切換到運行狀態(tài),也就意味著該進程占用著CPU在執(zhí)行蚓曼,但是當操作系統(tǒng)把進程切換到其他狀態(tài)時亲澡,那就不能在CPU中執(zhí)行了,于是操作系統(tǒng)會選擇下一個要運行的進程。
選擇一個進程運行這一功能是在操作系統(tǒng)中完成的纫版,通常稱為調度程序(scheduler) 床绪。
那到底什么時候調度進程,或以什么原則來調度進程呢?
什么時候調度進程
在進程的生命周期中其弊,當進程從一個運行狀態(tài)到另外一狀態(tài)變化的時候癞己,其實會觸發(fā)一次調度。
比如梭伐,以下狀態(tài)的變化都會觸發(fā)操作系統(tǒng)的調度:
●從就緒態(tài)->運行態(tài)痹雅,當進程被創(chuàng)建時,會進入到就緒隊列糊识,操作系統(tǒng)會從就緒隊列選擇一個進程運行;
●從運行態(tài)->阻塞態(tài)當進程發(fā)生1/0事件而阻塞時绩社,操作系統(tǒng)必須另外一個進程運行;
●從運行態(tài)->結束態(tài)當進程退出結束后,操作系統(tǒng)得從就緒隊列選擇另外一個進程運行;
因為赂苗,這些狀態(tài)變化的時候愉耙,操作系統(tǒng)需要考慮是否要讓新的進程給CPU運行,或者是否讓當前進程從CPU上退出來而換另一個進程運行拌滋。
另外朴沿,如果硬件時鐘提供某個頻率的周期性中斷,那么可以根據(jù)如何處理時鐘中斷
把調度算法分為兩類:
●非搶占式調度算法挑選一個進程,然后讓該進程運行直到被阻塞赌渣,或者直到該進程退出魏铅,才會調用另外一個進程,也就是說不會理時鐘中斷這個事情坚芜。
●搶占式調度算法挑選一個進程, 然后讓該進程只運行某段時間沦零,如果在該時段結束時,該進程仍然在運行時货岭,則會把它掛起路操,接著調度程序從就緒隊列挑選另外一個進程。這種搶占式調度處理千贯,需要在時間間隔的末端發(fā)生時鐘中斷屯仗,以便把CPU控制返回給調度程序進行調度,也就是常說的時間片機制搔谴。
以什么原則來調度進程
五種調度原則:
●CPU利用率:調度程序應確保CPU是始終匆忙的狀態(tài)魁袜,這可提高CPU的利用率;
●系統(tǒng)吞吐量:吞吐量表示的是單位時間內(nèi)CPU完成進程的數(shù)量,長作業(yè)的進程會占用較長的CPU資源敦第,因此會降低吞吐量峰弹,相反,短作業(yè)的進程會提升系統(tǒng)吞吐量;
●周轉時間:周轉時間是進程運行和阻塞時間總和芜果,一個進程的周轉時間越小越好;
●?等待時間:這個等待時間不是阻塞狀態(tài)的時間鞠呈,而是進程處于就緒隊列的時間,等待的時間越長右钾,用戶越不滿意;
●響應時間:用戶提交請求到系統(tǒng)第一次產(chǎn)生響應所花費的時間蚁吝,在交互式系統(tǒng)中,響應時間是衡量調度算法好壞的主要標準。
說白了,這么多調度原則,目的就是要使得進程要快背犯。^_^
一直都在,加油I搅帧(*゜Д゜)σ凸←自爆按鈕
進程調度算法
常見的進程調度算法有:
●先來先服務
●時間片輪轉
●最短作業(yè)優(yōu)先
●最短剩余時間優(yōu)先
●優(yōu)先級調度
●多級反饋隊列調度
先來先服務
先來先服務(First Come First Serverd, FCFS)。先進就緒隊列邢羔,則先被調度驼抹,先來先服務是最簡單的調度算法。
先來先服務存在上面談論過的問題:當前面任務耗費很長時間執(zhí)行张抄,那么后面的任務即使只需要執(zhí)行很短的時間砂蔽,也必須一直等待洼怔。屬于非搶占式署惯。
時間片輪轉調度
每一個進程會被分配一個時間片,表示允許該進程在這個時間段運行镣隶,如果時間結束了极谊,進程還沒運行完畢诡右,那么會通過搶占式調度,將CPU分配給其他進程轻猖,該進程回到就緒隊列帆吻。這是一種最簡單最公平的調度算法,但是有可能會存在問題咙边。由于進程的切換猜煮,需要耗費時間,如果時間片太短败许,頻繁進行切換王带,會影響效率。
如果進程時間片太長市殷,有可能導致排后面的進程等待太長時間愕撰。因此時間片的長度,需要有大致合理的數(shù)值醋寝。( 《現(xiàn)代操作系統(tǒng)》的觀點是建議時間片長度在20ms~50ms)搞挣。
最短作業(yè)優(yōu)先
最短作業(yè)優(yōu)先(Shortest Job First, SJF),顧名思義即進程按照作業(yè)時間長短排隊音羞,作業(yè)時間段的排前面先執(zhí)行囱桨,如下圖。
這顯然對長作業(yè)不利嗅绰,很容易造成一種極端現(xiàn)象蝇摸。
比如,一個長作業(yè)在就緒隊列等待運行办陷,而這個就緒隊列有非常多的短作業(yè)貌夕,那么就會使得長作業(yè)不斷的往后推,周轉時間變長民镜,致使長作業(yè)長期不會被運行啡专。
最短剩余時間優(yōu)先
最短剩余時間優(yōu)先(Shortest Remaining Time Next),從就緒隊列中選擇剩余時間最短的進程進行調度制圈。該算法可以理解為最短作業(yè)優(yōu)先和時間片輪轉的結合们童。如果沒有時間片,那么最短剩余時間其實就是最短作業(yè)時間鲸鹦,因為每個進程都是從頭執(zhí)行到尾慧库。
優(yōu)先級調度
假設就緒隊列中有如下進程
按照優(yōu)先級調度,執(zhí)行順序為p1->p3- >p2.如果多個進程優(yōu)先級相同,則按照先來先服務的方式依次執(zhí)行馋嗜。
優(yōu)先級調度可以進一步細分為搶占式和非搶占式齐板。
非搶占式:和上面提及的非搶占式類似,一旦該進程占有CPU就將一直執(zhí)行到結束或者阻塞。
搶占式:進程執(zhí)行期間甘磨,一旦有更高優(yōu)先級的進程進入就緒隊列橡羞,那么該進程就會被暫停,重回就緒隊列济舆,讓更高優(yōu)先級的進程執(zhí)行卿泽。但是為了防止最高優(yōu)先級進程一直執(zhí)行,每個進程依然有自己的時間片滋觉,每次時間片結束后签夭,會根據(jù)一定規(guī)則降低該進程優(yōu)先級,避免某些最高優(yōu)先級長作業(yè)進程一直占用CPU椎侠。
但是依然有缺點覆致,可能會導致低優(yōu)先級的進程永遠不會運行。
多級反饋隊列調度
多級反饋隊列調度基于時間片輪轉和優(yōu)先級調度肺蔚,設置多個就緒隊列煌妈,賦予每個就緒隊列優(yōu)先級,優(yōu)先級越高的隊列進程的時間片越短宣羊。如下圖璧诵,第1級就緒隊列優(yōu)先級最高,進程的時間片長度最短,第2級就緒隊列次之仇冯,以此類推之宿。
當有新的進程創(chuàng)建時,先進入第1級就緒隊列苛坚,時間片結束之前就運行完畢,則終止比被,否則進入第2級隊列等待下一次調度。 在n級隊列之前泼舱,進程按照先到先服務規(guī)則依次調度等缀,到了第n級隊列(最后一級)采用時間片輪轉調度。僅當?shù)?級隊列為空時娇昙,才調度第2級隊列的進程尺迂,如果第1級隊列的進程正在運行,此時有一個更高優(yōu)先級的進程進入冒掌,則會停下第i級的進程噪裕,讓它回到第i級隊列尾部,轉而執(zhí)行更高優(yōu)先級的進程股毫,即滿足優(yōu)先級調度算法的原則膳音。
可以發(fā)現(xiàn),對于短作業(yè)可能可以在第一級隊列很快被處理完铃诬。對于長作業(yè)祭陷,如果在第一級隊列處理不完苍凛,可以移入下次隊列等待被執(zhí)行,雖然等待的時間變長了,但是運行時間也會更長了颗胡,所以該算法很好的兼顧了長短作業(yè)毫深,同時有較好的響應時間吩坝。
拿去銀行辦業(yè)務的例子毒姨,把上面的調度算法串起來
辦理業(yè)務的客戶相當于進程,銀行窗口工作人員相當于CPU钉寝。
現(xiàn)在弧呐,假設這個銀行只有一個窗口(單核 CPU),那么工作人員一次只能處理一個業(yè)務嵌纲。
那么最簡單的處理方式俘枫,就是先來的先處理,后面來的就乖乖排隊逮走,這就是先來先服務(FCFS) 調度算法鸠蚪。但是萬一先來的這位老哥是來貸款的,這一談就好幾個小時师溅,一直占用著窗口茅信,這樣后面的人只能干等,或許后面的人只是想簡單的取個錢,幾分鐘就能搞定墓臭,卻因為前面老哥辦長業(yè)務而要等幾個小時蘸鲸,你說氣不氣人?
有客戶抱怨了,那我們就要改進,我們干脆優(yōu)先給那些幾分鐘就能搞定的人辦理業(yè)務窿锉,這就是短作業(yè)優(yōu)先(SJF)調度算法酌摇。聽起來不錯,但是依然還是有個極端情況嗡载,萬一辦理短業(yè)務的人非常的多窑多,這會導致長業(yè)務的人一直得不到服務,萬一這個長業(yè)務是個大客戶洼滚,那不就撿了芝麻丟了西瓜怯伊。
那就公平起見,現(xiàn)在窗口工作人員規(guī)定判沟,每個人我只處理10分鐘耿芹。如果10分鐘之內(nèi)處理完,就馬上換下一個人挪哄。如果沒處理完吧秕,依然換下一個人,但是客戶自己得記住辦理到哪個步驟了。這個也就是時間片輪轉(RR)調度算法迹炼。但是如果時間片設置過短,那么就會造成大量的上下文切換砸彬,增大了系統(tǒng)開銷颠毙。如果時間片過長,相當于退化成FCFS算法了砂碉。
既然公平也可能存在問題蛀蜜,那銀行就對客戶分等級,分為普通客戶、VIP 客戶增蹭、SVIP 客戶滴某。只要高優(yōu)先級的客戶-來,就第一時間處理這個客戶,這就是最高優(yōu)先級(HPF)調度算法滋迈。但依然也會有極端的問題霎奢,萬一當天來的全是高級客戶,那普通客戶不是沒有被服務的機會饼灿,不把普通客戶當人是嗎?那我們把優(yōu)先級改成動態(tài)的幕侠,如果客戶辦理業(yè)務時間增加,則降低其優(yōu)先級,如果客戶等待時間增加碍彭,則升高其優(yōu)先級晤硕。
那有沒有兼顧到公平和效率的方式呢?這里介紹一種算法,考慮的還算充分的,多級反饋隊列(MFQ)調度算法庇忌,它是時間片輪轉算法和優(yōu)先級算法的綜合和發(fā)展舞箍。
它的工作方式:
●銀行設置了多個排隊(就緒)隊列,每個隊列都有不同的優(yōu)先級漆枚,各個隊列優(yōu)先級從高到低创译,同時每個隊列執(zhí)行時間片的長度也不同,優(yōu)先級越高的時間片越短墙基。
●新客戶(進程)來了软族,先進入第一級隊列的末尾,按先來先服務原則排隊等待被叫號(運行)残制。如果時間片用完客戶的業(yè)務還沒辦理完成立砸,則讓客戶進入到下一級隊列的
末尾,以此類推初茶,直至客戶業(yè)務辦理完成颗祝。
●當?shù)谝患夑犃袥]人排隊時,就會叫號= 級隊列的客戶恼布。如果客戶辦理業(yè)務過程中, 有新的客戶加入到較高優(yōu)先級的隊列螺戳,那么此時辦理中的客戶需要停止辦理,回到原隊
列的末尾等待再次叫號折汞,因為要把窗口讓給剛進入較高優(yōu)先級隊列的客戶倔幼。
可以發(fā)現(xiàn),對于要辦理短業(yè)務的客戶來說爽待,可以很快的輪到并解決损同。對于要辦理長業(yè)務的客戶翩腐,一下子解決不了,就可以放到下一個隊列膏燃,雖然等待的時間稍微變長了茂卦,但是輪到自己的辦理時間也變長了,也可以接受组哩,不會造成極端現(xiàn)象等龙,可以說是綜合上面幾種算法的優(yōu)點。
進程間通信
每個進程的用戶地址空間都是獨立的禁炒,一般而言是不能互相訪問的而咆,但內(nèi)核空間是每個進程都共享的霍比,所以進程之間要通信必須通過內(nèi)核幕袱。
進程間通信目的一般有共享數(shù)據(jù),數(shù)據(jù)傳輸,消息通知,進程控制等悠瞬。以Unix/Linux為例们豌,介紹幾種重要的進程間通信方式:共享內(nèi)存,管道浅妆,消息隊列,信號量,信號望迎。
管道
如果你學過Linux命令,那你肯定很熟悉用“|”這個豎線。
1 $ ps auxf | grep mysql
上面命令行里的用豎線就是一個管道凌外,它的功能是將前一個命令(ps auxf)的輸出辩尊,作為后一個命令(grep mysql)的輸入,從這功能描述康辑,可以看出管道傳輸數(shù)據(jù)是單向的摄欲,如果想相互通信,我們需要創(chuàng)建兩個管道才行疮薇。
同時胸墙,我們得知.上面這種管道是沒有名字的,所以用“|”表示的管道稱為匿名管道按咒,用完了就銷毀迟隅。
管道還有另外一個類型是命名管道,也被叫做FIFO,因為數(shù)據(jù)是先進先出的傳輸方式励七。
在使用命名管道前智袭,先需要通過mkfifo命令來創(chuàng)建,并且指定管道名字:
1 $ mkfifo myPipe
myPipe就是這個管道的名稱,基于Linux一切皆文件的理念,所以管道也是以文件的方式存在,我們可以用ls看一下掠抬,這個文件的類型是p,也就是pipe (管
道)的意思:
1 $ ls-1
2 prw-r--r--,1 root? ?root?? ?0Jul 17 02:45 myPipe
接下來吼野,我們往myPipe這個管道寫入數(shù)據(jù):
1 $ echo "hello" > myPipe 1//將數(shù)據(jù)寫進管道
2
? ? ? ? ? ? ? ? ? ? ? ? ? ?//停住了...
你操作了后,你會發(fā)現(xiàn)命令執(zhí)行后就停在這了剿另,這是因為管道里的內(nèi)容沒有被讀取箫锤,只有當管道里的數(shù)據(jù)被讀完后贬蛙,命令才可以正常退出。
于是谚攒,我們執(zhí)行另外一個命令來讀取這個管道里的數(shù)據(jù):
1 $ cat < myPipe //讀取管道里的數(shù)據(jù)
2 hello
可以看到阳准,管道里的內(nèi)容被讀取出來了,并打印在了終端上馏臭,另外一方面野蝇,echo那個命令也正常退出了。
我們可以看出括儒,管道這種通信方式效率低绕沈,不適合進程間頻繁地交換數(shù)據(jù)。當然它的好處帮寻,自然就是簡單乍狐,同時也我們很容易得知管道里的數(shù)據(jù)已經(jīng)被另一個進程讀取了。
我們可以得知固逗,對于匿名管道浅蚪,它的通信范圍是存在父子關系的進程。因為管道沒有實體,也就是沒有管道文件,只能通過fork 來復制父進程fd文件描述符,來達到通信的目的烫罩。
在shell里面執(zhí)行A | B命令的時候惜傲, A進程和B進程都是shell創(chuàng)建出來的子進程,A和B之間不存在父子關系贝攒,它倆的父進程都是shell盗誊。
另外,對于命名管道隘弊,它可以在不相關的進程間也能相互通信哈踱。因為命令管道,提前創(chuàng)建了一個類型為管道的設備文件长捧,在進程里只要使用這個設備文件嚣鄙,就可以相互通信。
消息隊列
前面說到管道的通信方式是效率低的串结,因此管道不適合進程間頻繁地交換數(shù)據(jù)哑子。
對于這個問題,消息隊列的通信模式就可以解決肌割。比如卧蜓,A進程要給B進程發(fā)送消息,A進程把數(shù)據(jù)放在對應的消息隊列后就可以正常返回了把敞,B進程需要的時候再去讀取數(shù)據(jù)就可以了弥奸。同理,B進程要給A進程發(fā)送消息也是如此奋早。
再來盛霎,消息隊列是保存在內(nèi)核中的消息鏈表赠橙,在發(fā)送數(shù)據(jù)時,會分成一個一個獨立的數(shù)據(jù)單元愤炸,也就是消息體(數(shù)據(jù)塊)期揪,消息體是用戶自定義的數(shù)據(jù)類型,消息的發(fā)送方和接收方要約定好消息體的數(shù)據(jù)類型,所以每個消息體都是固定大小的存儲塊规个,不像管道是無格式的字節(jié)流數(shù)據(jù)凤薛。如果進程從消息隊列中讀取了消息體,內(nèi)核就會把這個消息體刪除诞仓。
消息隊列生命周期隨內(nèi)核缤苫,如果沒有釋放消息隊列或者沒有關閉操作系統(tǒng),消息隊列會一直存在墅拭,而前面提到的匿名管道的生命周期活玲,是隨進程的創(chuàng)建而建立,隨進程的結束而銷毀。
缺點:
消息隊列通信過程中帜矾,存在用戶態(tài)與內(nèi)核態(tài)之間的數(shù)據(jù)拷貝開銷翼虫,因為進程寫入數(shù)據(jù)到內(nèi)核中的消息隊列時屑柔,會發(fā)生從用戶態(tài)拷貝數(shù)據(jù)到內(nèi)核態(tài)的過程屡萤,同理另一進程讀取內(nèi)核中的消息數(shù)據(jù)時,會發(fā)生從內(nèi)核態(tài)拷貝數(shù)據(jù)到用戶態(tài)的過程掸宛。
共享內(nèi)存
消息隊列的讀取和寫入的過程死陆,都會有發(fā)生用戶態(tài)與內(nèi)核態(tài)之間的消息拷貝過程。那共享內(nèi)存的方式唧瘾,就很好的解決了這一問題措译。
現(xiàn)代操作系統(tǒng),對于內(nèi)存管理饰序,采用的是虛擬內(nèi)存技術领虹,也就是每個進程都有自己獨立的虛擬內(nèi)存空間,不同進程的虛擬內(nèi)存映射到不同的物理內(nèi)存中。所以求豫,即使進程A和進程B的虛擬地址是一樣的塌衰,其實訪問的是不同的物理內(nèi)存地址,對于數(shù)據(jù)的增刪查改互不影響。
共享內(nèi)存的機制蝠嘉,就是拿出一塊虛擬地址空間來最疆,映射到相同的物理內(nèi)存中。這樣這個進程寫入的東西蚤告,另外一個進程馬上就能看到了,都不需要拷貝來拷貝去努酸,傳來傳去,大大提高了進程間通信的速度杜恰。
信號量
用了共享內(nèi)存通信方式获诈,帶來新的問題仍源,那就是如果多個進程同時修改同一個共享內(nèi)存,很有可能就沖突了舔涎。例如兩個進程都同時寫一個地址,那先寫的那個進程會發(fā)現(xiàn)內(nèi)容被別人覆蓋了镜会。
為了防止多進程競爭共享資源,而造成的數(shù)據(jù)錯亂终抽,所以需要保護機制戳表,使得共享的資源,在任意時刻只能被一個進程訪問昼伴。正好匾旭,信號量就實現(xiàn)了這一保護機制。
信號量其實是一個整型的計數(shù)器圃郊,主要用于實現(xiàn)進程間的互斥與同步价涝,而不是用于緩存進程間通信的數(shù)據(jù)。
信號量表示資源的數(shù)量持舆,控制信號量的方式有兩種原子操作:
●一個是P操作,這個操作會把信號量減去1, 相減后如果信號量< 0,則表明資源已被占用色瘩,進程需阻塞等待;相減后如果信號量>= 0,則表明還有資源可使用,進程可正常繼續(xù)執(zhí)行。
●另一個是V操作,這個操作會把信號量加上1,相加后如果信號量<= 0,則表明當前有阻塞中的進程逸寓,于是會將該進程喚醒運行;相加后如果信號量> 0,則表明當前沒有阻塞中的進程;
P操作是用在進入共享資源之前居兆,V操作是用在離開共享資源之后,這兩個操作是必須成對出現(xiàn)的竹伸。
接下來,舉個例子,如果要使得兩個進程互斥訪問共享內(nèi)存,我們可以初始化信號量為1泥栖。
具體的過程如下:
●進程A在訪問共享內(nèi)存前,先執(zhí)行了P操作,由于信號量的初始值為1,故在進程A執(zhí)行P操作后信號量變?yōu)?,表示共享資源可用勋篓,于是進程A就可以訪問共享內(nèi)存吧享。
●若此時,進程B也想訪問共享內(nèi)存譬嚣,執(zhí)行了P操作,結果信號量變?yōu)榱?1,這就意味著臨界資源已被占用钢颂,因此進程B被阻塞。
●直到進程A訪問完共享內(nèi)存,才會執(zhí)行V操作,使得信號量恢復為0,接著就會喚醒阻塞中的線程B,使得進程B可以訪問共享內(nèi)存拜银,最后完成共享內(nèi)存的訪問后,執(zhí)行V操作殊鞭,使信號量恢復到初始值1。
可以發(fā)現(xiàn)盐股,信號初始化為1, 就代表著是互斥信號量,它可以保證共享內(nèi)存在任何時刻只有一個進程在訪問,這就很好的保護了共享內(nèi)存钱豁。
另外,在多進程里疯汁,每個進程并不一定是順序執(zhí)行的牲尺,它們基本是以各自獨立的、不可預知的速度向前推進,但有時候我們又希望多個進程能密切合作,以實現(xiàn)一個共同的任務谤碳。
例如溃卡,進程A是負責生產(chǎn)數(shù)據(jù),而進程B是負責讀取數(shù)據(jù)蜒简,這兩個進程是相互合作瘸羡、相互依賴的,進程A必須先生產(chǎn)了數(shù)據(jù)搓茬,進程B才能讀取到數(shù)據(jù)犹赖,所以執(zhí)行是有前后順序的。
那么這時候卷仑,就可以用信號量來實現(xiàn)多進程同步的方式峻村,我們可以初始化信號量為0。
具體過程:
●如果進程B比進程A先執(zhí)行了锡凝,那么執(zhí)行到P操作時粘昨,由于信號量初始值為0,故信號量會變?yōu)?1,表示進程A還沒生產(chǎn)數(shù)據(jù)窜锯,于是進程B就阻塞等待;
●接著张肾,當進程A生產(chǎn)完數(shù)據(jù)后,執(zhí)行了V操作,就會使得信號量變?yōu)?, 于是就會喚醒阻塞在P操作的進程B;
●最后,進程B被喚醒后锚扎,意味著進程A已經(jīng)生產(chǎn)了數(shù)據(jù)吞瞪,于是進程B就可以正常讀取數(shù)據(jù)了。
可以發(fā)現(xiàn)工秩,信號初始化為0,就代表著是同步信號量尸饺,它可以保證進程A應在進程B之前執(zhí)行。
信號
信號一般用于一些異常情況下的進程間通信助币,是一種異步通信,它的數(shù)據(jù)結構一般就是一個數(shù)字在Linux操作系統(tǒng)中螟碎,為了響應各種各樣的事件,提供了幾十種信號眉菱,分別代表不同的意義。我們可以通過kill -1 命令,查看所有的信號:
運行在shell終端的進程掉分,我們可以通過鍵盤輸入某些組合鍵的時候俭缓,給進程發(fā)送信號。例如
●Ctrl+C 產(chǎn)生SIGINT 信號酥郭,表示終止該進程;
●Ctrl+Z 產(chǎn)生SIGTSTP信號华坦,表示停止該進程,但還未結束;
如果進程在后臺運行不从,可以通過kill 命令的方式給進程發(fā)送信號,但前提需要知道運行中的進程PID號,例如:
●Jkill -9 1050,表示給PID為1050的進程發(fā)送SIGKILL信號惜姐,用來立即結束該進程;
所以,信號事件的來源主要有硬件來源(如鍵盤CItr+C )和軟件來源(如kill命令)。
信號是進程間通信機制中唯一的異步通信機制。
進程需要為信號設置相應的監(jiān)聽處理歹袁,當收到特定信號時坷衍,執(zhí)行相應的操作,類似很多編程語言里的通知機制。
Socket
前面提到的管道条舔、消息隊列枫耳、共享內(nèi)存、信號量和信號都是在同一臺主機上進行進程間通信孟抗,那要想跨網(wǎng)絡與不同主機上的進程之間通信迁杨,就需要Socket通信了。
實際上凄硼,Socket 通信不僅可以跨網(wǎng)絡與不同主機的進程間通信仑最,還可以在同主機上進程間通信。
總結
(看到這上面的都忘了吧??(=′ω`=)帆喇,那就看看總表回憶一下咯)
感謝您的閱讀警医,希望您能攝取到知識!加油坯钦!沖沖沖Tせ省(發(fā)現(xiàn)光,追隨光婉刀,成為光吟温,散發(fā)光!)我是程序員耶耶突颊!有緣再見鲁豪。<-biubiu-?(`ω′∩)