程序作為人類的發明,自然也免不了脫離人類的習性,也有通訊的需求。如果程序之間不進行任何通訊,那麼程序所能完成的任務就要大打折扣。人類的通訊方式無外乎對白(通過聲音溝通)、打手勢、寫信、發電報、擁抱等方法。同理,程序也可以通過同樣的方式來進行通訊。本篇我們就來看看程序的這些互動方式。
人們最常用的通訊手段就是對白,一方發出聲音,另一方接收聲音。而聲音的傳遞需要通過一些介質,例如:空氣(face to face)、線纜(有線**)等。類似的,程序對白就是乙個程序發出某種資料資訊,另外一方接收資料資訊,而這些資料資訊通過一片共享的儲存空間進行傳遞。
乙個程序向儲存空間的一端寫入資訊,另乙個程序儲存空間的另外一端讀取資訊,這個就是管道。就像兩個人對白的媒介是空氣也可以是線纜一樣,管道所佔的空間既可以是記憶體也可以是磁碟。
要建立乙個管道,乙個程序只需要呼叫管道建立的系統呼叫即可,該系統呼叫所做的事情就是在某種儲存介質上劃出一片空間,賦給其中乙個程序寫的權利,另乙個程序讀的權利即可。
例如在linux下,我們通過shell命令輸入兩個命令,中間通過符號「|」來建立兩個命令之間的管道:
$ sort < file1 | grep zou上面乙個命令表示:對file1的內容首先進行排序,排序完成後的結果將作為grep的輸入,在結果裡面找出所有包括字串zou的文字行。也就是說,在兩個任務「排序「(sort)和」查詢」(grep)之間建立了乙個管道,資料從sort流向了grep。
套接字(socket)的功能非常強大,可以支援不同層面、不同應用、跨網路的通訊。使用套接字進行通訊需要雙方均建立乙個套接字,其中一方作為伺服器方,另外一方作為客戶方。伺服器方必須首先建立乙個服務區套接字,然後在該套接字上進行監聽,等待遠方的連線請求。客戶方也要建立乙個套接字,然後向伺服器方傳送連線請求。伺服器套接字在受到連線請求之後,將在伺服器方機器上新建乙個客戶套接字,與遠方的客戶方套接字形成點到點的通訊通道。之後,客戶方和伺服器方便可以直接通過類似於send和recv的命令在這個建立的套接字管道上進行交流了。
例如,在c#中我們可以輕鬆地建立乙個伺服器方的socket:
//(1)必須首先在通訊的程序間建立連線(管道或套接字),這需要消耗系統資源;建立socket->繫結ip與埠->設定監聽佇列的長度->開啟監聽連線
socketwatch = new
socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp);
socketwatch.bind(
new ipendpoint(ipaddress.parse(txtipaddress.text), int
.parse(txtport.text)));
socketwatch.listen(
10);
(2)通訊是自願的,而管道和套接字需要強制雙方進行通訊;
(3)由於建立連線需要消耗時間,一旦建立就應該盡可能多的通訊,如果通訊資訊量很小,則就是「殺雞用牛刀」了;
訊號類似於我們生活中的電報,如果你想給某人發一封電報,就擬好電文,然後將報文和收報人的資訊都交給電報公司。電報公司則將電報傳送到收報人所在地的郵局,並通知收報人來取電報。其中,發報文時無需收報人實現知道,也無需進行任何協調。如果對方選擇不對訊號做出響應,則將被os終止執行。
在計算機中,訊號就是乙個核心物件或者是乙個核心資料結構。傳送方將該資料結構的內容填好,並指明該訊號的目標程序後,發出特定的軟體中斷(這就是乙個發電報的操作)。os接收到特定的中斷請求後,知道是有程序要傳送訊號,於是到特定的核心資料結構裡查詢訊號接收方,並進行通知。接到通知的程序則對訊號進行相應處理。
訊號量**於鐵路的執行:在一條單軌鐵路上,任何時候只允許有一列火車行駛在該鐵路上,而管理這條鐵路的系統就是訊號量。任何一列火車必須等到表明該鐵路可以行駛的訊號後才能進入軌道。當列車進入後,需要將訊號改為禁止狀態進入來防止別的列車同時進入。而當列車駛出單軌後,則需要將訊號變回允許進入狀態,這很像以前的旗語。當然,通過聯想到我們實際開發中經常用的鎖,這就更容易理解了。
在計算機中,訊號量實際上就是乙個簡單整數。乙個程序在訊號變為0或1的情況下推進,並將訊號變為1或0來防止別的程序同時推進。當該程序完成任務後,則將訊號再改為0或1,從而允許其他程序執行。從而我們也可以看出,訊號量已經不只是一種通訊機制,更是一種同步機制。
前面通過對話、發電報、旗語已經滿足了多種通訊需要,但是當兩個程序要共享大量資料時就沒法十分滿足需求。就如同兩個墜入愛河的騷年,它們互相喜歡並想要在一起同居(共享大量資料),這時打**、發電報、握手對話就顯得不夠了。這時候,它們需要的就是擁抱,只有緊緊擁抱才能盡可能地共享,feeling so good!
程序的擁抱就是共享記憶體,兩個程序共同擁有同一片記憶體。對於這片記憶體中的任何內容,二者均可以訪問。要使用共享記憶體進行通訊,程序a首先需要建立一片記憶體空間作為通訊用,而其他程序b則將片記憶體對映到自己的(虛擬)位址空間。這樣,程序a讀寫自己位址空間中對應共享記憶體的區域時,就是在和程序b進行通訊。
(1)使用共享記憶體機制通訊的兩個程序必須在同一臺物理機上;
(2)安全性脆弱,假如乙個程序有病毒,會很容易傳給另外乙個程序;
訊息佇列是一列具有頭和尾的訊息排列,新來的訊息放在佇列尾部,而讀取訊息則從佇列頭部開始,如下圖所示:
這樣看來,它和管道十分類似,一頭讀,一頭寫?的確,看起來很像管道,但又不是管道:
(1)訊息佇列無固定的讀寫程序,任何程序都可以讀寫;而管道需要指定誰讀和誰寫;
(2)訊息佇列可以同時支援多個程序,多個程序可以讀寫訊息佇列;即所謂的多對多,而管道是點對點;
(3)訊息佇列只在記憶體中實現,而管道還可以在磁碟上實現;
鄒恒明,《作業系統之哲學原理》,機械工業出版社
出處:
作業系統核心原理 3 程序原理(中) 程序排程
ps 在多程序併發的環境裡,雖然從概念上看,有多個程序在同時執行,但在單個cpu下,在任何時刻只能有乙個程序處於執行狀態,而其他程序則處於非執行狀態。那麼問題來了,我們是如何確定在任意時刻到底由哪個程序執行,哪些不執行呢?這就涉及到程序管理的乙個重要組成部分 程序排程,跟隨本篇來一起複習下程序排程吧...
作業系統核心原理 3 程序原理(上) 程序概要
程序管理 記憶體管理和檔案管理是作業系統的三大核心功能,那麼什麼是程序呢?顧名思義,程序就是進展中的程式,或者說程序是執行中的程式。當乙個程式被載入到記憶體之後就變為了程序。因此,我們可以得到 程序 程式 執行。本篇將會對程序 程序模型 程序狀態以及程序的缺陷等進行學習,為後續學習程序排程與程序通訊...
作業系統核心原理 3 程序原理(上) 程序概要
程序管理 記憶體管理和檔案管理是作業系統的三大核心功能,那麼什麼是程序呢?顧名思義,程序就是進展中的程式,或者說程序是執行中的程式。當乙個程式被載入到記憶體之後就變為了程序。因此,我們可以得到 程序 程式 執行。本篇將會對程序 程序模型 程序狀態以及程序的缺陷等進行學習,為後續學習程序排程與程序通訊...