docker中的資源隔離,一種就是IPC的隔離。IPC是進程間通信。
下面的文章轉載自https://blog.csdn.net/yyq_9623/article/details/78794775
原帖發表在IBM的developerworks網站上,是一個系列的文章,作者鄭彥興,通過講解和例子演示了Linux中幾種IPC的使用方式,我覺得很好,在這里做一個保留,能看完的話Linux IPC的基礎是沒有問題的了。
一)Linux環境進程間通信(一)管道及有名管道
http://www.ibm.com/developerworks/cn/linux/l-ipc/part1/
二)Linux環境進程間通信(二): 信號
上:http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index1.html
下:http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index2.html
三)Linux環境進程間通信(三)消息隊列
http://www.ibm.com/developerworks/cn/linux/l-ipc/part3/
四)Linux環境進程間通信(四)信號燈
http://www.ibm.com/developerworks/cn/linux/l-ipc/part4/
五)Linux環境進程間通信(五): 共享內存
上:http://www.ibm.com/developerworks/cn/linux/l-ipc/part5/index1.html
下:http://www.ibm.com/developerworks/cn/linux/l-ipc/part5/index2.html
六)Linux環境進程間通信(六): socket
http://www.ibm.com/developerworks/cn/linux/l-ipc/
==============================================================================================
linux下進程間通信的幾種主要手段簡介:
管道(Pipe)及有名管道(named pipe):管道可用於具有親緣關系進程間的通信,有名管道克服了管道沒有名字的限制,因此,除具有管道所具有的功能外,它還允許無親緣關系進程間的通信;
信號(Signal):信號是比較復雜的通信方式,用於通知接受進程有某種事件發生,除了用於進程間通信外,進程還可以發送信號給進程本身;linux除了支持Unix早期信號語義函數sigal外,還支持語義符合Posix.1標准的信號函數sigaction(實際上,該函數是基於BSD的,BSD為了實現可靠信號機制,又能夠統一對外接口,用sigaction函數重新實現了signal函數);
報文(Message)隊列(消息隊列):消息隊列是消息的鏈接表,包括Posix消息隊列system V消息隊列。有足夠權限的進程可以向隊列中添加消息,被賦予讀權限的進程則可以讀走隊列中的消息。消息隊列克服了信號承載信息量少,管道只能承載無格式字節流以及 緩沖區大小受限等缺點。
共享內存:使得多個進程可以訪問同一塊內存空間,是最快的可用IPC形式。是針對其他通信機制運行效率較低而設計的。往往與其它通信機制,如信號量結合使用,來達到進程間的同步及互斥。
信號量(semaphore):主要作為進程間以及同一進程不同線程之間的同步手段。
套接口(Socket):更為一般的進程間通信機制,可用於不同機器之間的進程間通信。起初是由Unix系統的BSD分支開發出來的,但現在一般可以移植到其它類Unix系統上:Linux和System V的變種都支持套接字。
管道兩端可分別用描述字fd[0]以及fd[1]來描述,需要注意的是,管道的兩端是固定了任務的。即一端只能用於讀,由描述字fd[0]表示,稱其為管道讀端;另一端則只能用於寫,由描述字fd[1]來表示,稱其為管道寫端。如果試圖從管道寫端讀取數據,或者向管道讀端寫入數據都將導致錯誤發生。一般文件的I/O函數都可以用於管道,如close、read、write等等。
管道的主要局限性正體現在它的特點上:
只支持單向數據流;
只能用於具有親緣關系的進程之間;
沒有名字;
管道的緩沖區是有限的(管道制存在於內存中,在管道創建時,為緩沖區分配一個頁面大小);
管道所傳送的是無格式字節流,這就要求管道的讀出方和寫入方必須事先約定好數據的格式,比如多少字節算作一個消息(或命令、或記錄)等等;
管道應用的一個重大限制是它沒有名字,因此,只能用於具有親緣關系的進程間通信,在有名管道(named pipe或FIFO)提出后,該限制得到了克服。FIFO不同於管道之處在於它提供一個路徑名與之關聯,以FIFO的文件形式存在於文件系統中。這樣,即使與FIFO的創建進程不存在親緣關系的進程,只要可以訪問該路徑,就能夠彼此通過FIFO相互通信(能夠訪問該路徑的進程以及FIFO的創建進程之間),因此,通過FIFO不相關的進程也能交換數據。值得注意的是,FIFO嚴格遵循先進先出(first in first out),對管道及FIFO的讀 總是從開始處返回數據,對它們的寫則把數據添加到末尾。它們不支持諸如lseek()等文件定位操作。
管道常用於兩個方面:(1)在shell中時常會用到管道(作為輸入輸入的重定向),在這種應用方式下,管道的創建對於用戶來說是透明的;(2)用於具有親緣關系的進程間通信,用戶自己創建管道,並完成讀寫操作。
FIFO可以說是管道的推廣,克服了管道無名字的限制,使得無親緣關系的進程同樣可以采用先進先出的通信機制進行通信。
管道和FIFO的數據是字節流,應用程序之間必須事先確定特定的傳輸"協議",采用傳播具有特定意義的消息。
要靈活應用管道及FIFO,理解它們的讀寫規則是關鍵。
信號本質
信號是在軟件層次上對中斷機制的一種模擬,在原理上,一個進程收到一個信號與處理器收到一個中斷請求可以說是一樣的。信號是異步的,一個進程不必通過任何操作來等待信號的到達,事實上,進程也不知道信號到底什么時候到達。
信號是進程間通信機制中唯一的異步通信機制,可以看作是異步通知,通知接收信號的進程有哪些事情發生了。信號機制經過POSIX實時擴展后,功能更加強大,除了基本通知功能外,還可以傳遞附加信息。
信號來源
信號事件的發生有兩個來源:硬件來源(比如我們按下了鍵盤或者其它硬件故障);軟件來源,最常用發送信號的系統函數是kill, raise, alarm和setitimer以及sigqueue函數,軟件來源還包括一些非法運算等操作。
進程可以通過三種方式來響應一個信號:(1)忽略信號,即對信號不做任何處理,其中,有兩個信號不能忽略:SIGKILL及SIGSTOP;(2)捕捉信號。定義信號處理函數,當信號發生時,執行相應的處理函數;(3)執行缺省操作,Linux對每種信號都規定了默認操作,詳細情況請參考[2]以及其它資料。注意,進程對實時信號的缺省反應是進程終止。
Linux究竟采用上述三種方式的哪一個來響應信號,取決於傳遞給相應API函數的參數。
從信號發送到信號處理函數的執行完畢
對於一個完整的信號生命周期(從信號發送到相應的處理函數執行完畢)來說,可以分為三個重要的階段,這三個階段由四個重要事件來刻畫:信號誕生;信號在進程中注冊完畢;信號在進程中的注銷完畢;信號處理函數執行完畢。相鄰兩個事件的時間間隔構成信號生命周期的一個階段。
消息隊列(也叫做報文隊列)能夠克服早期unix通信機制的一些缺點。作為早期unix通信機制之一的信號能夠傳送的信息量有限,后來雖然POSIX 1003.1b在信號的實時性方面作了拓廣,使得信號在傳遞信息量方面有了相當程度的改進,但是信號這種通信方式更像"即時"的通信方式,它要求接受信號的進程在某個時間范圍內對信號做出反應,因此該信號最多在接受信號進程的生命周期內才有意義,信號所傳遞的信息是接近於隨進程持續的概念(process-persistent),見 附錄 1;管道及有名管道及有名管道則是典型的隨進程持續IPC,並且,只能傳送無格式的字節流無疑會給應用程序開發帶來不便,另外,它的緩沖區大小也受到限制。
消息隊列就是一個消息的鏈表。可以把消息看作一個記錄,具有特定的格式以及特定的優先級。對消息隊列有寫權限的進程可以向中按照一定的規則添加新消息;對消息隊列有讀權限的進程則可以從消息隊列中讀走消息。消息隊列是隨內核持續的(參見 附錄 1)。
每個消息隊列的容量(所能容納的字節數)都有限制,該值因系統不同而不同。
消息隊列與管道以及有名管道相比,具有更大的靈活性,首先,它提供有格式字節流,有利於減少開發人員的工作量;其次,消息具有類型,在實際應用中,可作為優先級使用。這兩點是管道以及有名管道所不能比的。同樣,消息隊列可以在幾個進程間復用,而不管這幾個進程是否具有親緣關系,這一點與有名管道很相似;但消息隊列是隨內核持續的,與有名管道(隨進程持續)相比,生命力更強,應用空間更大。
信號燈與其他進程間通信方式不大相同,它主要提供對進程間共享資源訪問控制機制。相當於內存中的標志,進程可以根據它判定是否能夠訪問某些共享資源,同時,進程也可以修改該標志。除了用於訪問控制外,還可用於進程同步。
信號燈與其他進程間通信方式不大相同,它主要提供對進程間共享資源訪問控制機制。相當於內存中的標志,進程可以根據它判定是否能夠訪問某些共享資源,同時,進程也可以修改該標志。除了用於訪問控制外,還可用於進程同步。信號燈有以下兩種類型:
二值信號燈:最簡單的信號燈形式,信號燈的值只能取0或1,類似於互斥鎖。
注:二值信號燈能夠實現互斥鎖的功能,但兩者的關注內容不同。信號燈強調共享資源,只要共享資源可用,其他進程同樣可以修改信號燈的值;互斥鎖更強調進程,占用資源的進程使用完資源后,必須由進程本身來解鎖。
計算信號燈:信號燈的值可以取任意非負值(當然受內核本身的約束)。
1、 一次系統調用semop可同時操作的信號燈數目SEMOPM,semop中的參數nsops如果超過了這個數目,將返回E2BIG錯誤。SEMOPM的大小特定與系統,redhat 8.0為32。
2、 信號燈的最大數目:SEMVMX,當設置信號燈值超過這個限制時,會返回ERANGE錯誤。在redhat 8.0中該值為32767。
3、 系統范圍內信號燈集的最大數目SEMMNI以及系統范圍內信號燈的最大數目SEMMNS。超過這兩個限制將返回ENOSPC錯誤。redhat 8.0中該值為32000。
4、 每個信號燈集中的最大信號燈數目SEMMSL,redhat 8.0中為250。 SEMOPM以及SEMVMX是使用semop調用時應該注意的;SEMMNI以及SEMMNS是調用semget時應該注意的。SEMVMX同時也是semctl調用應該注意的。
共享內存可以說是最有用的進程間通信方式,也是最快的IPC形式。兩個不同進程A、B共享內存的意思是,同一塊物理內存被映射到進程A、B各自的進程地址空間。進程A可以即時看到進程B對共享內存中數據的更新,反之亦然。由於多個進程共享同一塊內存區域,必然需要某種同步機制,互斥鎖和信號量都可以。
采用共享內存通信的一個顯而易見的好處是效率高,因為進程可以直接讀寫內存,而不需要任何數據的拷貝。對於像管道和消息隊列等通信方式,則需要在內核和用戶空間進行四次的數據拷貝,而共享內存則只拷貝兩次數據[1]:一次從輸入文件到共享內存區,另一次從共享內存區到輸出文件。實際上,進程之間在共享內存時,並不總是讀寫少量數據后就解除映射,有新的通信時,再重新建立共享內存區域。而是保持共享區域,直到通信完畢為止,這樣,數據內容一直保存在共享內存中,並沒有寫回文件。共享內存中的內容往往是在解除映射時才寫回文件的。因此,采用共享內存的通信方式效率是非常高的。
Linux的2.2.x內核支持多種共享內存方式,如mmap()系統調用,Posix共享內存,以及系統V共享內存。linux發行版本如Redhat 8.0支持mmap()系統調用及系統V共享內存,但還沒實現Posix共享內存,本文將主要介紹mmap()系統調用及系統V共享內存API的原理及應用。
mmap()系統調用使得進程之間通過映射同一個普通文件實現共享內存。普通文件被映射到進程地址空間后,進程可以向訪問普通內存一樣對文件進行訪問,不必再調用read(),write()等操作。
注:實際上,mmap()系統調用並不是完全為了用於共享內存而設計的。它本身提供了不同於一般對普通文件的訪問方式,進程可以像讀寫內存一樣對普通文件的操作。而Posix或系統V的共享內存IPC則純粹用於共享目的,當然mmap()實現共享內存也是其主要應用之一。
共享內存允許兩個或多個進程共享一給定的存儲區,因為數據不需要來回復制,所以是最快的一種進程間通信機制。共享內存可以通過mmap()映射普通文件(特殊情況下還可以采用匿名映射)機制實現,也可以通過系統V共享內存機制實現。應用接口和原理很簡單,內部機制復雜。為了實現更安全通信,往往還與信號燈等同步機制共同使用。
共享內存涉及到了存儲管理以及文件系統等方面的知識,深入理解其內部機制有一定的難度,關鍵還要緊緊抓住內核使用的重要數據結構。系統V共享內存是以文件的形式組織在特殊文件系統shm中的。通過shmget可以創建或獲得共享內存的標識符。取得共享內存標識符后,要通過shmat將這個內存區映射到本進程的虛擬地址空間。
一個套接口可以看作是進程間通信的端點(endpoint),每個套接口的名字都是唯一的(唯一的含義是不言而喻的),其他進程可以發現、連接並且與之通信。通信域用來說明套接口通信的協議,不同的通信域有不同的通信協議以及套接口的地址結構等等,因此,創建一個套接口時,要指明它的通信域。比較常見的是unix域套接口(采用套接口機制實現單機內的進程間通信)及網際通信域。
下面列出了網絡編程中的其他重要概念,基本上都是給出這些概念能夠實現的功能,讀者在編程過程中如果需要這些功能,可查閱相關概念。
I/O復用提供一種能力,這種能力使得當一個I/O條件滿足時,進程能夠及時得到這個信息。I/O復用一般應用在進程需要處理多個描述字的場合。它的一個優勢在於,進程不是阻塞在真正的I/O調用上,而是阻塞在select()調用上,select()可以同時處理多個描述字,如果它所處理的所有描述字的I/O都沒有處於准備好的狀態,那么將阻塞;如果有一個或多個描述字I/O處於准備好狀態,則select()不阻塞,同時會根據准備好的特定描述字采取相應的I/O操作。
前面主要介紹的是PF_INET通信域,實現網際間的進程間通信。基於Unix通信域(調用socket時指定通信域為PF_LOCAL即可)的套接口可以實現單機之間的進程間通信。采用Unix通信域套接口有幾個好處:Unix通信域套接口通常是TCP套接口速度的兩倍;另一個好處是,通過Unix通信域套接口可以實現在進程間傳遞描述字。所有可用描述字描述的對象,如文件、管道、有名管道及套接口等,在我們以某種方式得到該對象的描述字后,都可以通過基於Unix域的套接口來實現對描述字的傳遞。接收進程收到的描述字值不一定與發送進程傳遞的值一致(描述字是特定於進程的),但是特們指向內核文件表中相同的項。
原始套接口提供一般套接口所不提供的功能:
原始套接口可以讀寫一些用於控制的控制協議分組,如ICMPv4等,進而可實現一些特殊功能。
原始套接口可以讀寫特殊的IPv4數據包。內核一般只處理幾個特定協議字段的數據包,那么一些需要不同協議字段的數據包就需要通過原始套接口對其進行讀寫;
通過原始套接口可以構造自己的Ipv4頭部,也是比較有意思的一點。
創建原始套接口需要root權限。
對數據鏈路層的訪問,使得用戶可以偵聽本地電纜上的所有分組,而不需要使用任何特殊的硬件設備,在linux下讀取數據鏈路層分組需要創建SOCK_PACKET類型的套接口,並需要有root權限。
如果有一些重要信息要立刻通過套接口發送(不經過排隊),請查閱與帶外數據相關的文獻。
linux內核支持多播,但是在默認狀態下,多數linux系統都關閉了對多播的支持。因此,為了實現多播,可能需要重新配置並編譯內核。具體請參考[4]及[2]。
結論:linux套接口編程的內容可以說是極大豐富,同時它涉及到許多的網絡背景知識,有興趣的讀者可在[2]中找到比較系統而全面的介紹。
至此,本專題系列(linux環境進程間通信)全部結束了。實際上,進程間通信的一般意義通常指的是消息隊列、信號燈和共享內存,可以是posix的,也可以是SYS v的。本系列同時介紹了管道、有名管道、信號以及套接口等,是更為一般意義上的進程間通信機制。