進程間通訊的7種方式你都瞭解嗎

進程間通訊的概念

每一個進程各自有不一樣的用戶地址空間,任何一個進程的全局變量在另外一個進程中都看不到,因此進程之間要交換數據必須經過內核,在內核中開闢一塊緩衝區,進程1把數據從用戶空間拷到內核緩衝區,進程2再從內核緩衝區把數據讀走,內核提供的這種機制稱爲進程間通訊(IPC,InterProcess Communication)面試

進程間通訊的7種方式

第一類:傳統的Unix通訊機制
1. 管道/匿名管道(pipe)
  • 管道是半雙工的,數據只能向一個方向流動;須要雙方通訊時,須要創建起兩個管道。
  • 只能用於父子進程或者兄弟進程之間(具備親緣關係的進程);
  • 單獨構成一種獨立的文件系統:管道對於管道兩端的進程而言,就是一個文件,但它不是普通的文件,它不屬於某種文件系統,而是自立門戶,單獨構成一種文件系統,而且只存在與內存中。
  • 數據的讀出和寫入:一個進程向管道中寫的內容被管道另外一端的進程讀出。寫入的內容每次都添加在管道緩衝區的末尾,而且每次都是從緩衝區的頭部讀出數據。
管道的實質:

管道的實質是一個內核緩衝區,進程以先進先出的方式從緩衝區存取數據,管道一端的進程順序的將數據寫入緩衝區,另外一端的進程則順序的讀出數據。該緩衝區能夠看作是一個循環隊列,讀和寫的位置都是自動增加的,不能隨意改變,一個數據只能被讀一次,讀出來之後在緩衝區就不復存在了。當緩衝區讀空或者寫滿時,有必定的規則控制相應的讀進程或者寫進程進入等待隊列,當空的緩衝區有新數據寫入或者滿的緩衝區有數據讀出來時,就喚醒等待隊列中的進程繼續讀寫。shell

管道的侷限:

管道的主要侷限性正體如今它的特色上:服務器

  • 只支持單向數據流;
  • 只能用於具備親緣關係的進程之間;
  • 沒有名字;
  • 管道的緩衝區是有限的(管道制存在於內存中,在管道建立時,爲緩衝區分配一個頁面大小);
  • 管道所傳送的是無格式字節流,這就要求管道的讀出方和寫入方必須事先約定好數據的格式,好比多少字節算做一個消息(或命令、或記錄)等等;
2. 有名管道(FIFO)

匿名管道,因爲沒有名字,只能用於親緣關係的進程間通訊。爲了克服這個缺點,提出了有名管道(FIFO)。有名管道不一樣於匿名管道之處在於它提供了一個路徑名與之關聯,以有名管道的文件形式存在於文件系統中,這樣,即便與有名管道的建立進程不存在親緣關係的進程,只要能夠訪問該路徑,就可以彼此經過有名管道相互通訊,所以,經過有名管道不相關的進程也能交換數據。值的注意的是,有名管道嚴格遵循先進先出(first in first out),對匿名管道及有名管道的讀老是從開始處返回數據,對它們的寫則把數據添加到末尾。它們不支持諸如lseek()等文件定位操做。有名管道的名字存在於文件系統中,內容存放在內存中。網絡

匿名管道和有名管道總結:

(1)管道是特殊類型的文件,在知足先入先出的原則條件下能夠進行讀寫,但不能進行定位讀寫。(2)匿名管道是單向的,只能在有親緣關係的進程間通訊;有名管道以磁盤文件的方式存在,能夠實現本機任意兩個進程通訊。(3)無名管道阻塞問題:無名管道無需顯示打開,建立時直接返回文件描述符,在讀寫時須要肯定對方的存在,不然將退出。若是當前進程向無名管道的一端寫數據,必須肯定另外一端有某一進程。若是寫入無名管道的數據超過其最大值,寫操做將阻塞,若是管道中沒有數據,讀操做將阻塞,若是管道發現另外一端斷開,將自動退出。(4)有名管道阻塞問題:有名管道在打開時須要確實對方的存在,不然將阻塞。即以讀方式打開某管道,在此以前必須一個進程以寫方式打開管道,不然阻塞。此外,能夠以讀寫(O_RDWR)模式打開有名管道,即當前進程讀,當前進程寫,不會阻塞。多線程

3. 信號(Signal)
  • 信號是Linux系統中用於進程間互相通訊或者操做的一種機制,信號能夠在任什麼時候候發給某一進程,而無需知道該進程的狀態。
  • 若是該進程當前並未處於執行狀態,則該信號就有內核保存起來,知道該進程回覆執行並傳遞給它爲止。
  • 若是一個信號被進程設置爲阻塞,則該信號的傳遞被延遲,直到其阻塞被取消是才被傳遞給進程。
Linux系統中經常使用信號:

(1)SIGHUP:用戶從終端註銷,全部已啓動進程都將收到該進程。系統缺省狀態下對該信號的處理是終止進程。(2)SIGINT:程序終止信號。程序運行過程當中,按Ctrl+C鍵將產生該信號。(3)SIGQUIT:程序退出信號。程序運行過程當中,按Ctrl+鍵將產生該信號。(4)SIGBUS和SIGSEGV:進程訪問非法地址。(5)SIGFPE:運算中出現致命錯誤,如除零操做、數據溢出等。(6)SIGKILL:用戶終止進程執行信號。shell下執行kill -9發送該信號。(7)SIGTERM:結束進程信號。shell下執行kill 進程pid發送該信號。(8)SIGALRM:定時器信號。(9)SIGCLD:子進程退出信號。若是其父進程沒有忽略該信號也沒有處理該信號,則子進程退出後將造成殭屍進程。架構

信號來源

信號是軟件層次上對中斷機制的一種模擬,是一種異步通訊方式,,信號能夠在用戶空間進程和內核之間直接交互,內核能夠利用信號來通知用戶空間的進程發生了哪些系統事件,信號事件主要有兩個來源:異步

  • 硬件來源:用戶按鍵輸入Ctrl+C退出、硬件異常如無效的存儲訪問等。
  • 軟件終止:終止進程信號、其餘進程調用kill函數、軟件異常產生信號。
信號生命週期和處理流程

(1)信號被某個進程產生,並設置此信號傳遞的對象(通常爲對應進程的pid),而後傳遞給操做系統;(2)操做系統根據接收進程的設置(是否阻塞)而選擇性的發送給接收者,若是接收者阻塞該信號(且該信號是能夠阻塞的),操做系統將暫時保留該信號,而不傳遞,直到該進程解除了對此信號的阻塞(若是對應進程已經退出,則丟棄此信號),若是對應進程沒有阻塞,操做系統將傳遞此信號。(3)目的進程接收到此信號後,將根據當前進程對此信號設置的預處理方式,暫時終止當前代碼的執行,保護上下文(主要包括臨時寄存器數據,當前程序位置以及當前CPU的狀態)、轉而執行中斷服務程序,執行完成後在回覆到中斷的位置。固然,對於搶佔式內核,在中斷返回時還將引起新的調度。socket

4. 消息(Message)隊列
  • 消息隊列是存放在內核中的消息鏈表,每一個消息隊列由消息隊列標識符表示。
  • 與管道(無名管道:只存在於內存中的文件;命名管道:存在於實際的磁盤介質或者文件系統)不一樣的是消息隊列存放在內核中,只有在內核重啓(即,操做系統重啓)或者顯示地刪除一個消息隊列時,該消息隊列纔會被真正的刪除。
  • 另外與管道不一樣的是,消息隊列在某個進程往一個隊列寫入消息以前,並不須要另外某個進程在該隊列上等待消息的到達。延伸閱讀:消息隊列C語言的實踐
消息隊列特色總結:

(1)消息隊列是消息的鏈表,具備特定的格式,存放在內存中並由消息隊列標識符標識.(2)消息隊列容許一個或多個進程向它寫入與讀取消息.(3)管道和消息隊列的通訊數據都是先進先出的原則。(4)消息隊列能夠實現消息的隨機查詢,消息不必定要以先進先出的次序讀取,也能夠按消息的類型讀取.比FIFO更有優點。(5)消息隊列克服了信號承載信息量少,管道只能承載無格式字 節流以及緩衝區大小受限等缺。(6)目前主要有兩種類型的消息隊列:POSIX消息隊列以及System V消息隊列,系統V消息隊列目前被大量使用。系統V消息隊列是隨內核持續的,只有在內核重起或者人工刪除時,該消息隊列纔會被刪除。函數

5. 共享內存(share memory)
  • 使得多個進程能夠能夠直接讀寫同一塊內存空間,是最快的可用IPC形式。是針對其餘通訊機制運行效率較低而設計的。
  • 爲了在多個進程間交換信息,內核專門留出了一塊內存區,能夠由須要訪問的進程將其映射到本身的私有地址空間。進程就能夠直接讀寫這一塊內存而不須要進行數據的拷貝,從而大大提升效率。
  • 因爲多個進程共享一段內存,所以須要依靠某種同步機制(如信號量)來達到進程間的同步及互斥。
  • 延伸閱讀:Linux支持的主要三種共享內存方式:mmap()系統調用、Posix共享內存,以及System V共享內存實踐
6. 信號量(semaphore)

信號量是一個計數器,用於多進程對共享數據的訪問,信號量的意圖在於進程間同步。爲了得到共享資源,進程須要執行下列操做:(1)建立一個信號量:這要求調用者指定初始值,對於二值信號量來講,它一般是1,也但是0。(2)等待一個信號量:該操做會測試這個信號量的值,若是小於0,就阻塞。也稱爲P操做。
(3)掛出一個信號量:該操做將信號量的值加1,也稱爲V操做。爲了正確地實現信號量,信號量值的測試及減1操做應當是原子操做。爲此,信號量一般是在內核中實現的。Linux環境中,有三種類型:Posix(可移植性操做系統接口)有名信號量(使用Posix IPC名字標識)、Posix基於內存的信號量(存放在共享內存區中)、System V信號量(在內核中維護)。這三種信號量均可用於進程間或線程間的同步。image學習

兩個進程使用一個二值信號量

兩個進程因此用一個Posix有名二值信號量

信號量與普通整型變量的區別:

(1)信號量是非負整型變量,除了初始化以外,它只能經過兩個標準原子操做:wait(semap) , signal(semap) ; 來進行訪問;(2)操做也被成爲PV原語(P來源於荷蘭語proberen"測試",V來源於荷蘭語verhogen"增長",P表示經過的意思,V表示釋放的意思),而普通整型變量則能夠在任何語句塊中被訪問;
信號量與互斥量之間的區別:(1)互斥量用於線程的互斥,信號量用於線程的同步。這是互斥量和信號量的根本區別,也就是互斥和同步之間的區別。互斥:是指某一資源同時只容許一個訪問者對其進行訪問,具備惟一性和排它性。但互斥沒法限制訪問者對資源的訪問順序,即訪問是無序的。同步:是指在互斥的基礎上(大多數狀況),經過其它機制實現訪問者對資源的有序訪問。在大多數狀況下,同步已經實現了互斥,特別是全部寫入資源的狀況一定是互斥的。少數狀況是指能夠容許多個訪問者同時訪問資源(2)互斥量值只能爲0/1,信號量值能夠爲非負整數。也就是說,一個互斥量只能用於一個資源的互斥訪問,它不能實現多個資源的多線程互斥問題。信號量能夠實現多個同類資源的多線程互斥和同步。當信號量爲單值信號量是,也能夠完成一個資源的互斥訪問。(3)互斥量的加鎖和解鎖必須由同一線程分別對應使用,信號量能夠由一個線程釋放,另外一個線程獲得。

7. 套接字(socket)

套接字是一種通訊機制,憑藉這種機制,客戶/服務器(即要進行通訊的進程)系統的開發工做既能夠在本地單機上進行,也能夠跨網絡進行。也就是說它可讓不在同一臺計算機但經過網絡鏈接計算機上的進程進行通訊。

Socket是應用層和傳輸層之間的橋樑套接字是支持TCP/IP的網絡通訊的基本操做單元,能夠看作是不一樣主機之間的進程進行雙向通訊的端點,簡單的說就是通訊的兩方的一種約定,用套接字中的相關函數來完成通訊過程。

套接字特性

套接字的特性由3個屬性肯定,它們分別是:域、端口號、協議類型。

(1)套接字的域

它指定套接字通訊中使用的網絡介質,最多見的套接字域有兩種:一是AF_INET,它指的是Internet網絡。當客戶使用套接字進行跨網絡的鏈接時,它就須要用到服務器計算機的IP地址和端口來指定一臺聯網機器上的某個特定服務,因此在使用socket做爲通訊的終點,服務器應用程序必須在開始通訊以前綁定一個端口,服務器在指定的端口等待客戶的鏈接。另外一個域AF_UNIX,表示UNIX文件系統,它就是文件輸入/輸出,而它的地址就是文件名。

(2)套接字的端口號

每個基於TCP/IP網絡通信的程序(進程)都被賦予了惟一的端口和端口號,端口是一個信息緩衝區,用於保留Socket中的輸入/輸出信息,端口號是一個16位無符號整數,範圍是0-65535,以區別主機上的每個程序(端口號就像房屋中的房間號),低於256的端口號保留給標準應用程序,好比pop3的端口號就是110,每個套接字都組合進了IP地址、端口,這樣造成的總體就能夠區別每個套接字。

(3)套接字協議類型

因特網提供三種通訊機制,一是流套接字,流套接字在域中經過TCP/IP鏈接實現,同時也是AF_UNIX中經常使用的套接字類型。流套接字提供的是一個有序、可靠、雙向字節流的鏈接,所以發送的數據能夠確保不會丟失、重複或亂序到達,並且它還有必定的出錯後從新發送的機制。
二個是數據報套接字,它不須要創建鏈接和維持一個鏈接,它們在域中一般是經過UDP/IP協議實現的。它對能夠發送的數據的長度有限制,數據報做爲一個單獨的網絡消息被傳輸,它可能會丟失、複製或錯亂到達,UDP不是一個可靠的協議,可是它的速度比較高,由於它並一須要老是要創建和維持一個鏈接。三是原始套接字,原始套接字容許對較低層次的協議直接訪問,好比IP、 ICMP協議,它經常使用於檢驗新的協議實現,或者訪問現有服務中配置的新設備,由於RAW SOCKET能夠自如地控制Windows下的多種協議,可以對網絡底層的傳輸機制進行控制,因此能夠應用原始套接字來操縱網絡層和傳輸層應用。好比,咱們能夠經過RAW SOCKET來接收發向本機的ICMP、IGMP協議包,或者接收TCP/IP棧不可以處理的IP包,也能夠用來發送一些自定包頭或自定協議的IP包。網絡監聽技術很大程度上依賴於SOCKET_RAW。

原始套接字與標準套接字的區別在於:
原始套接字能夠讀寫內核沒有處理的IP數據包,而流套接字只能讀取TCP協議的數據,數據報套接字只能讀取UDP協議的數據。所以,若是要訪問其餘協議發送數據必須使用原始套接字。
套接字通訊的創建

服務器端

(1)首先服務器應用程序用系統調用socket來建立一個套接字,它是系統分配給該服務器進程的相似文件描述符的資源,它不能與其餘的進程共享。(2)而後,服務器進程會給套接字起個名字,咱們使用系統調用bind來給套接字命名。而後服務器進程就開始等待客戶鏈接到這個套接字。(3)接下來,系統調用listen來建立一個隊列並將其用於存放來自客戶的進入鏈接。(4)最後,服務器經過系統調用accept來接受客戶的鏈接。它會建立一個與原有的命名套接不一樣的新套接字,這個套接字只用於與這個特定客戶端進行通訊,而命名套接字(即原先的套接字)則被保留下來繼續處理來自其餘客戶的鏈接(創建客戶端和服務端的用於通訊的流,進行通訊)。

客戶端

(1)客戶應用程序首先調用socket來建立一個未命名的套接字,而後將服務器的命名套接字做爲一個地址來調用connect與服務器創建鏈接。(2)一旦鏈接創建,咱們就能夠像使用底層的文件描述符那樣用套接字來實現雙向數據的通訊(經過流進行數據傳輸)。
Android學習PDF+架構視頻+面試文檔+源碼筆記

相關文章
相關標籤/搜索