linux進(jìn)程間通信方式
1. 管道
管道的實(shí)質(zhì)是一個(gè)內(nèi)核緩沖區(qū),管道的作用正如其名,需要通信的兩個(gè)進(jìn)程在管道的兩端,進(jìn)程利用管道傳遞信息。管道對(duì)于管道兩端的進(jìn)程而言,就是一個(gè)文件,但是這個(gè)文件比較特殊,它不屬于文件系統(tǒng)并且只存在于內(nèi)存中。

管道克服了文件通信的問題:
- 限制管道的大小。實(shí)際上,管道是一個(gè)固定大小的緩沖區(qū)。進(jìn)程A向管道內(nèi)write(),當(dāng)管道內(nèi)存寫滿的時(shí)候,進(jìn)程A會(huì)阻塞,直到進(jìn)程B開始read()讀出數(shù)據(jù),此時(shí)管道中就可以有內(nèi)存供進(jìn)程A進(jìn)行write。
- 讀進(jìn)程比寫進(jìn)程快的問題。當(dāng)進(jìn)程B進(jìn)行read()操作時(shí),進(jìn)程A還沒有寫入文件,此時(shí)進(jìn)程B就會(huì)阻塞,指導(dǎo)進(jìn)程A開始寫入。
管道分為下面兩種:- 無名管道,半雙工的通信方式,數(shù)據(jù)只能單向流程,只能具有父子關(guān)系的進(jìn)程間使用,
- 有名管道,有名管道也是一種半雙工的通信方式,但是它允許無親緣關(guān)系進(jìn)程間的通信。
2. 信號(hào)量
信號(hào)量是一個(gè)計(jì)數(shù)器,可以用來控制多個(gè)線程對(duì)共享資源的訪問.,它不是用于交換大批數(shù)據(jù),而用于多線程之間的同步.它常作為一種鎖機(jī)制,防止某進(jìn)程在訪問資源時(shí)其它進(jìn)程也訪問該資源.因此,主要作為進(jìn)程間以及同一個(gè)進(jìn)程內(nèi)不同線程之間的同步手段.
3. 信號(hào)
信號(hào)是在軟件層次上對(duì)中斷機(jī)制的一種模擬。在原理上,一個(gè)進(jìn)程收到一個(gè)信號(hào)與處理器收到一個(gè)中斷請(qǐng)求可以說是一樣的。信號(hào)是異步的,一個(gè)進(jìn)程不必通過任何操作來等待信號(hào)的到達(dá),事實(shí)上,進(jìn)程也不知道信號(hào)到底什么時(shí)候到達(dá)。信號(hào)可以直接進(jìn)行用戶空間進(jìn)程和內(nèi)核進(jìn)程之間的交互,內(nèi)核進(jìn)程也可以利用它來通知用戶空間進(jìn)程發(fā)生了哪些系統(tǒng)事件。它可以在任何時(shí)候發(fā)給某一進(jìn)程,而無須知道該進(jìn)程的狀態(tài)。如果該進(jìn)程當(dāng)前并未處于執(zhí)行態(tài),則該信號(hào)就由內(nèi)核保存起來,直到該進(jìn)程恢復(fù)執(zhí)行再傳遞給它為止;如果一個(gè)信號(hào)被進(jìn)程設(shè)置為阻塞,則該信號(hào)的傳遞被延遲,直到其阻塞被取消時(shí)才被傳遞給進(jìn)程。
4. 消息隊(duì)列
消息隊(duì)列是消息的鏈表,存放在內(nèi)核中并由消息隊(duì)列標(biāo)識(shí)符標(biāo)識(shí).消息隊(duì)列克服了信號(hào)傳遞信息少,管道只能承載無格式字節(jié)流以及緩沖區(qū)大小受限等特點(diǎn).消息隊(duì)列是UNIX下不同進(jìn)程之間可實(shí)現(xiàn)共享資源的一種機(jī)制,UNIX允許不同進(jìn)程將格式化的數(shù)據(jù)流以消息隊(duì)列形式發(fā)送給任意進(jìn)程.對(duì)消息隊(duì)列具有操作權(quán)限的進(jìn)程都可以使用msget完成對(duì)消息隊(duì)列的操作控制.通過使用消息類型,進(jìn)程可以按任何順序讀信息,或?yàn)橄才艃?yōu)先級(jí)順序.
5. 共享內(nèi)存
共享內(nèi)存就是允許兩個(gè)或多個(gè)進(jìn)程共享一定的存儲(chǔ)區(qū)。當(dāng)一個(gè)進(jìn)程改變了這塊地址中的內(nèi)容的時(shí)候,其它進(jìn)程都會(huì)察覺到這個(gè)更改。因?yàn)閿?shù)據(jù)不需要在客戶機(jī)和服務(wù)器端之間復(fù)制,數(shù)據(jù)直接寫到內(nèi)存,不用若干次數(shù)據(jù)拷貝,所以這是最快的一種IPC。
優(yōu)點(diǎn):傳輸速度最快的通信方式?jīng)]有之一,客戶進(jìn)程和服務(wù)進(jìn)程傳遞的數(shù)據(jù)直接從內(nèi)存里存取、放入,數(shù)據(jù)不需要在兩進(jìn)程間復(fù)制,
缺點(diǎn):共享內(nèi)存并未提供同步機(jī)制,也就是說,在一個(gè)服務(wù)進(jìn)程結(jié)束對(duì)共享內(nèi)存的寫操作之前,并沒有自動(dòng)機(jī)制可以阻止另一個(gè)進(jìn)程(客戶進(jìn)程)開始對(duì)它進(jìn)行讀取,有時(shí)候需要結(jié)合信號(hào)量來進(jìn)行同步。
6. 套接字
socket是TCP/IP網(wǎng)絡(luò)的API接口函數(shù),可以實(shí)現(xiàn)不同進(jìn)程之間的通信(IPC),本機(jī)和遠(yuǎn)程都可以;socket最先應(yīng)用于Unix操作系統(tǒng),而在Unix/Linux中有種思想是一切皆文件,所以socket就是種特殊的I/O,有文件描述符,但是只是用于區(qū)分,類似的還有進(jìn)程ID
Android 進(jìn)程通信方式
雖然Android是基于Linux,但是Android有自己的一套通信方式,下面簡(jiǎn)單介紹下Android常用的進(jìn)程間通信方式。
1. Intent
包括Activity,Service,Receiver之間通信都可以通過Intent進(jìn)行通信,在此不贅述。
2. ContentProvider
ContentProvider是Android中提供的專門用于不同應(yīng)用間數(shù)據(jù)交互和共享的組件。ContentProvider實(shí)際上是對(duì)SQLiteOpenHelper的進(jìn)一步封裝,以一個(gè)或多個(gè)表的形式將數(shù)據(jù)呈現(xiàn)給外部應(yīng)用,通過Uri映射來選擇需要操作數(shù)據(jù)庫中的哪個(gè)表,并對(duì)表中的數(shù)據(jù)進(jìn)行增刪改查處理。ContentProvider其底層使用了Binder來完成APP進(jìn)程之間的通信,同時(shí)使用匿名共享內(nèi)存來作為共享數(shù)據(jù)的載體。ContentProvider支持訪問權(quán)限管理機(jī)制,以控制數(shù)據(jù)的訪問者及訪問方式,保證數(shù)據(jù)訪問的安全性。
3. 文件共享
將對(duì)象序列化之后保存到文件中,在通過反序列,將對(duì)象從文件中讀取出來。
文件共享方式也存在著很大的局限性,如并發(fā)讀/寫問題,如讀取的數(shù)據(jù)不完整或者讀取的數(shù)據(jù)不是最新的。文件共享適合在對(duì)數(shù)據(jù)同步要求不高的進(jìn)程間通信,并且要妥善處理并發(fā)讀/寫的問題。
4. AIDL
AIDL(Android Interface Definition Language)是一種IDL語言,用于生成可以在Android設(shè)備上兩個(gè)進(jìn)程之間進(jìn)行進(jìn)程間通信(IPC)的代碼。
5. Messenger
Messenger只能傳遞Message對(duì)象,Messenger是一種輕量級(jí)的IPC方案,它的底層實(shí)現(xiàn)是AIDL。
Messenger內(nèi)部消息處理使用Handler實(shí)現(xiàn)的,所以它是以串行的方式處理客服端發(fā)送過來的消息的,如果有大量的消息發(fā)送給服務(wù)器端,服務(wù)器端只能一個(gè)一個(gè)處理,如果并發(fā)量大的話用Messenger就不合適了,而且Messenger的主要作用就是為了傳遞消息,很多時(shí)候我們需要跨進(jìn)程調(diào)用服務(wù)器端的方法,這種需求Messenger就無法做到了。
6. Socket
Socket方法是通過網(wǎng)絡(luò)來進(jìn)行數(shù)據(jù)交換,客戶端和服務(wù)端建立連接之后即可不斷傳輸數(shù)據(jù),比較適合實(shí)時(shí)的數(shù)據(jù)傳。
Binder介紹
上面我對(duì)Android間的通信方式進(jìn)行了簡(jiǎn)單介紹,我們也經(jīng)常會(huì)用到Activity、 Service、Broadcast、ContentProvider四大組件,也會(huì)用到AIDL和Messenger,但是他們內(nèi)部實(shí)現(xiàn)的原理是什么呢?
這些問題的背后都與 Binder 有莫大的關(guān)系,要理解上述原理,理解 Bidner 通信機(jī)制是必須的。
linux已經(jīng)為了提供了包括管道、信號(hào)量、信號(hào)、共享內(nèi)存、套簽字等通信方式,為什么又要?jiǎng)?chuàng)提供Binder
來作為IPC的通道呢,主要基于以下原因:
- 性能
| 通信方式 | 數(shù)據(jù)拷貝次數(shù) | 優(yōu)缺點(diǎn) |
|---|---|---|
| 共享內(nèi)存 | 0 | 共享內(nèi)存雖然無需拷貝,但控制復(fù)雜,難以使用 |
| Socket | 2 | 其傳輸效率低,開銷大,主要用在跨網(wǎng)絡(luò)的進(jìn)程間通信和本機(jī)上進(jìn)程間的低速通信 |
| 消息隊(duì)列和管道 | 2 | 數(shù)據(jù)先從發(fā)送方緩存區(qū)拷貝到內(nèi)核開辟的緩存區(qū)中,然后再從內(nèi)核緩存區(qū)拷貝到接收方緩存區(qū),文件拷貝兩次,效率低 |
| Binder | 1 | 只拷貝一次數(shù)據(jù),性能好 |
安全性
Linux的IPC機(jī)制在本身的實(shí)現(xiàn)中,并沒有安全措施,而Binder機(jī)制的UID/PID是由Binder機(jī)制本身在內(nèi)核空間添加身份標(biāo)識(shí),安全性高;并且Binder可以建立私有通道,這是linux的通信機(jī)制所無法實(shí)現(xiàn)的。穩(wěn)定性
Binder 基于 C/S 架構(gòu),客戶端(Client)有什么需求就丟給服務(wù)端(Server)去完成,架構(gòu)清晰、職責(zé)明確又相互獨(dú)立,自然穩(wěn)定性更好。使用簡(jiǎn)單
client端函數(shù)的名字、參數(shù)和返回值和server的方法一模一樣,取消了client端和server端的隔閡。
Linux進(jìn)程間通信基本概念
為了更好的理解binder機(jī)制,我們對(duì)linux進(jìn)程間通信方式涉及到的概念進(jìn)行下說明。
以下內(nèi)容參考了文章Android跨進(jìn)程通信:圖文詳解 Binder機(jī)制 原理
進(jìn)程隔離
進(jìn)程與進(jìn)程間內(nèi)存是不共享的,進(jìn)程1和進(jìn)程2無法共享內(nèi)存,想要通信必須要使用IPC。
用戶空間和內(nèi)核空間
內(nèi)核空間(Kernel)是系統(tǒng)內(nèi)核運(yùn)行的空間,用戶空間(User Space)是用戶程序運(yùn)行的空間。為了保證安全性,它們之間是隔離的。
用戶態(tài)與內(nèi)核態(tài)
雖然從邏輯上進(jìn)行了用戶空間和內(nèi)核空間的劃分,但不可避免的用戶空間需要訪問內(nèi)核資源,比如文件操作、訪問網(wǎng)絡(luò)等等。
- 當(dāng)一個(gè)任務(wù)(進(jìn)程)執(zhí)行系統(tǒng)調(diào)用而陷入內(nèi)核代碼中執(zhí)行時(shí),稱進(jìn)程處于內(nèi)核運(yùn)行態(tài)(內(nèi)核態(tài))。
- 當(dāng)進(jìn)程在執(zhí)行用戶自己的代碼的時(shí)候,我們稱其處于用戶運(yùn)行態(tài)(用戶態(tài))
系統(tǒng)調(diào)用主要通過下面兩個(gè)方式:
copy_from_user() //將數(shù)據(jù)從用戶空間拷貝到內(nèi)核空間
copy_to_user() //將數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間
內(nèi)存映射
Binder IPC 機(jī)制中涉及到的內(nèi)存映射通過 mmap() 來實(shí)現(xiàn),mmap() 是操作系統(tǒng)中一種內(nèi)存映射的方法。內(nèi)存映射簡(jiǎn)單的講就是將用戶空間的一塊內(nèi)存區(qū)域映射到內(nèi)核空間。映射關(guān)系建立后,用戶對(duì)這塊內(nèi)存區(qū)域的修改可以直接反應(yīng)到內(nèi)核空間;反之內(nèi)核空間對(duì)這段區(qū)域的修改也能直接反應(yīng)到用戶空間。
詳細(xì)了解請(qǐng)參考:[操作系統(tǒng):圖文詳解 內(nèi)存映射](操作系統(tǒng):圖文詳解 內(nèi)存映射 "http://m.itdecent.cn/p/719fc4758813")
Binder的原理
傳統(tǒng)IPC的原理

傳統(tǒng)IPC每次通信要發(fā)生兩次內(nèi)存拷貝,流程如下:
- 發(fā)送數(shù)據(jù)
- 數(shù)據(jù)發(fā)送進(jìn)程調(diào)用copy_from_user將數(shù)據(jù)從用戶空間copy到內(nèi)核緩存
- 系統(tǒng)調(diào)用copy_to_user將數(shù)據(jù)從內(nèi)核空間copy到用戶空間。
- 接受數(shù)據(jù)
Binder的通信原理

- 首先 Binder 驅(qū)動(dòng)在內(nèi)核空間創(chuàng)建一個(gè)數(shù)據(jù)接收緩存區(qū);
- 接著在內(nèi)核空間開辟一塊內(nèi)核緩存區(qū),建立內(nèi)核緩存區(qū)和內(nèi)核中數(shù)據(jù)接收緩存區(qū)之間的映射關(guān)系,以及內(nèi)核中數(shù)據(jù)接收緩存區(qū)和接收進(jìn)程用戶空間地址的映射關(guān)系;
- 發(fā)送方進(jìn)程通過系統(tǒng)調(diào)用 copy_from_user() 將數(shù)據(jù) copy 到內(nèi)核中的內(nèi)核緩存區(qū),由于內(nèi)核緩存區(qū)和接收進(jìn)程的用戶空間存在內(nèi)存映射,因此也就相當(dāng)于把數(shù)據(jù)發(fā)送到了接收進(jìn)程的用戶空間,這樣便完成了一次進(jìn)程間的通信。
Binder的實(shí)現(xiàn)

Binder的實(shí)現(xiàn)主要涉及到如圖的四個(gè)部分Client、Server、Binder Driver、ServiceManager,其中從實(shí)現(xiàn)層面來說,我Client和Server在Android應(yīng)用層,需要開發(fā)者自行實(shí)現(xiàn),ServiceManager、Binder Driver是Android fromwork層實(shí)現(xiàn);從內(nèi)核層面,Client、Server、ServiceManager主要操作在用戶空間執(zhí)行操作,Binder Driver在內(nèi)核空間執(zhí)行操作,Client、Server、ServiceManager分別通過open、mmap 和 ioctl 來訪問設(shè)備文件 /dev/binder,從而實(shí)現(xiàn)操作Binder來跨進(jìn)程通信。
參考文章:[圖文詳解 Binder機(jī)制 原理](圖文詳解 Binder機(jī)制 原理 "https://blog.csdn.net/carson_ho/article/details/73560642")
一次Binder通信主要分為以下步驟:

- 注冊(cè)服務(wù):Server進(jìn)程向Binder Driver發(fā)起注冊(cè)服務(wù)請(qǐng)求,Binder Driver將請(qǐng)求轉(zhuǎn)發(fā)給ServiceManager,ServiceManager添加該Server進(jìn)程管理
- 獲取服務(wù):Client向Binder驅(qū)動(dòng)獲取服務(wù),Binder驅(qū)動(dòng)將請(qǐng)求轉(zhuǎn)發(fā)到ServiceManager并查找Client對(duì)應(yīng)的Server服務(wù)信息,通過Binder驅(qū)動(dòng)將上述信息返回
- 使用服務(wù):
- Binder驅(qū)動(dòng)創(chuàng)建一塊接收緩存區(qū)域,并實(shí)現(xiàn)內(nèi)存映射關(guān)系:包括內(nèi)核緩存區(qū)和Server用戶區(qū)映射到同一個(gè)接受緩存區(qū)。
- Client調(diào)用copy_from_user()發(fā)送到內(nèi)核緩存區(qū)
- 內(nèi)核緩存區(qū)映射到Server用戶空間去,Server進(jìn)行解包,執(zhí)行指定方法
- 將執(zhí)行結(jié)果存到接受區(qū)內(nèi)存緩存區(qū),內(nèi)存映射到內(nèi)核緩存區(qū)
- Client調(diào)用copy_to_user()獲取內(nèi)核緩存區(qū)數(shù)據(jù),完成本次通信
本文對(duì)Binder相關(guān)原理進(jìn)行了梳理,參考了網(wǎng)上的部分資料,相關(guān)部分都有轉(zhuǎn)載引用標(biāo)識(shí),如有侵權(quán),立刻刪除。
參考文章