使用ipcs命令的-m(內(nèi)存)和-p(進(jìn)程)選項(xiàng): # ipcs -mp IPC status from /dev/mem as of Wed Jun 26 14:49:19 BEIDT 2002
創(chuàng)新互聯(lián)專注于興山網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠(chéng)為您提供興山營(yíng)銷型網(wǎng)站建設(shè),興山網(wǎng)站制作、興山網(wǎng)頁(yè)設(shè)計(jì)、興山網(wǎng)站官網(wǎng)定制、成都小程序開發(fā)服務(wù),打造興山網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供興山網(wǎng)站排名全網(wǎng)營(yíng)銷落地服務(wù)。
正如上一章所說(shuō), 跨進(jìn)程通信是需要內(nèi)核空間做支持的. 傳統(tǒng)的 IPC 機(jī)制如 管道, Socket, 都是內(nèi)核的一部分, 因此通過內(nèi)核支持來(lái)實(shí)現(xiàn)進(jìn)程間通信自然是沒問題的.
但是 Binder 并不是 Linux 系統(tǒng)內(nèi)核的一部分, 那怎么辦呢, 這得益于 Linux 的動(dòng)態(tài)內(nèi)核可加載模塊 (Loadable Kernel Module, LKM)的機(jī)制
這樣 Android 系統(tǒng)就可以通過動(dòng)態(tài)添加一個(gè)內(nèi)核模塊運(yùn)行在內(nèi)核空間, 用戶進(jìn)程進(jìn)程之間通過這個(gè)內(nèi)核模塊作為橋梁來(lái)實(shí)現(xiàn)通信.
那么在 Android 系統(tǒng)中用戶進(jìn)程之間是如何通過這個(gè)內(nèi)核模塊 (Binder Driver)來(lái)實(shí)現(xiàn)通信的呢? 顯然不是和上一章的傳統(tǒng) IPC 通信一樣,進(jìn)行兩次 copy 了, 不然Binder 也不有在性能方面的優(yōu)勢(shì)了.
Binder IPC 機(jī)制中設(shè)計(jì)到的內(nèi)存映射通過 mmap() 來(lái)實(shí)現(xiàn), mmap() 是操作系統(tǒng)中一種內(nèi)存映射的方法.
內(nèi)存映射能減少數(shù)據(jù) copy 的次數(shù), 實(shí)現(xiàn)用戶空間和內(nèi)核空間的高效互動(dòng). 兩個(gè)空間各自的修改也能直接反應(yīng)在映射的內(nèi)存區(qū)域, 從而被對(duì)方空間及時(shí)感知. 也正因?yàn)槿绱? 內(nèi)存映射能夠提供對(duì)進(jìn)程間通信的支持.
Binder IPC 正是基于內(nèi)存映射( mmap() ) 來(lái)實(shí)現(xiàn)的, 但是 mmap() 通常是用在有物理介質(zhì)的文件系統(tǒng)上的.
比如進(jìn)程中的用戶區(qū)域是不能直接和物理設(shè)備打交道的, 如果想要把磁盤上的數(shù)據(jù)讀取到進(jìn)程的用戶區(qū)域, 需要兩次 copy (磁盤 - 內(nèi)核空間 - 用戶空間). 通常在這種場(chǎng)景下 mmap() 就能發(fā)揮作用, 通過在物理介質(zhì)和用戶空間之間建立映射, 減少數(shù)據(jù)的 copy 次數(shù), 用內(nèi)存讀寫代替 I/O 讀寫, 提高文件讀取效率.
而 Binder 并不存在物理介質(zhì), 因此 Binder 驅(qū)動(dòng)使用 mmap() 并不是為了在物理介質(zhì)和用戶空間之間映射, 而是用來(lái)在內(nèi)核空間創(chuàng)建數(shù)據(jù)接收的緩存空間.
一次完整的 Binder IPC 通信過程通常是這樣:
這樣就完成了一次進(jìn)程間通信
如下圖:
介紹完 Binder IPC 的底層通信原理, 接下來(lái)我們看看實(shí)現(xiàn)層面是如何設(shè)計(jì)的
一次完成的進(jìn)程間通信必然至少包含兩個(gè)進(jìn)程, 通常我們稱通信的雙方分別為客戶端進(jìn)程(Client) 和服務(wù)端進(jìn)程(Server), 由于進(jìn)程隔離機(jī)制的存在, 通信雙方必然需要借助 Binder 來(lái)實(shí)現(xiàn).
BInder 是基于 C/S 架構(gòu). 是由一些列組件組成. 包括 Client, Server, ServiceManager, Binder 驅(qū)動(dòng).
Binder 驅(qū)動(dòng)就如如同路由器一樣, 是整個(gè)通信的核心. 驅(qū)動(dòng)負(fù)責(zé)進(jìn)程之間 Binder 通信的建立 / 傳遞, Binder 引用計(jì)數(shù)管理, 數(shù)據(jù)包在進(jìn)程之間的傳遞和交互等一系列底層支持.
ServiceManager 作用是將字符形式的 Binder 名字轉(zhuǎn)化成 Client 中對(duì)該 Binder 的引用, 使得 Client 能夠通過 Binder 的名字獲得對(duì) Binder 實(shí)體的引用.
注冊(cè)了名字的 Binder 叫實(shí)名 Binder, 就像網(wǎng)站一樣除了 IP 地址以外還有自己的網(wǎng)址.
Server 創(chuàng)建了 Binder, 并為它起一個(gè)字符形式, 可讀易記的名字, 將這個(gè) BInder 實(shí)體連同名字一起以數(shù)據(jù)包的形式通過 Binder 驅(qū)動(dòng) 發(fā)送給 ServiceManager, 通知 ServiceManager 注冊(cè)一個(gè)名字為 "張三"的 Binder, 它位于某個(gè) Server 中, 驅(qū)動(dòng)為這個(gè)穿越進(jìn)程邊界的 BInder 創(chuàng)建位于內(nèi)核中的實(shí)體節(jié)點(diǎn)以及 ServiceManager 對(duì)實(shí)體的引用, 將名字以及新建的引用打包傳給 ServiceManager, ServiceManager 收到數(shù)據(jù)后從中取出名字和引用填入查找表.
ServiceManager 是一個(gè)進(jìn)程, Server 又是一個(gè)另外的進(jìn)程, Server 向 ServiceManager 中注冊(cè) BInder 必然涉及到進(jìn)程間通信. 當(dāng)實(shí)現(xiàn)進(jìn)程間通信又要用到進(jìn)程間通信, 這就好像蛋可以孵出雞的前提確實(shí)要先找只雞下蛋! Binder 的實(shí)現(xiàn)比較巧妙, 就是預(yù)先創(chuàng)造一只雞來(lái)下蛋. ServiceManager 和其他進(jìn)程同樣采用 Binder 通信, ServiceManager 是 Server 端, 有自己的 Binder 實(shí)體, 其他進(jìn)程都是 Client, 需要通過這個(gè) Binder 的引用來(lái)實(shí)現(xiàn) Binder 的注冊(cè), 查詢和獲取. ServiceManager 提供的 Binder 比較特殊, 它沒有名字也不需要注冊(cè). 當(dāng)一個(gè)進(jìn)程使用 BINDERSETCONTEXT_MGR 命令將自己注冊(cè)成 ServiceManager 時(shí) Binder 驅(qū)動(dòng)會(huì)自動(dòng)為它創(chuàng)建 Binder 實(shí)體(這就是那只預(yù)先造好的那只雞). 其實(shí)這個(gè) Binder 實(shí)體的引用在所有 Client 中都固定為 0 , 而無(wú)需通過其他手段獲得. 也就是說(shuō), 一個(gè) Server 想要向 ServiceManager 注冊(cè)自己的 Binder 就必須通過這個(gè) 0 號(hào)引用和 ServiceManager 的 Binder 通信. 這里說(shuō)的 Client 是相對(duì)于 ServiceManager 而言的, 一個(gè)進(jìn)程或者應(yīng)用程序可能是提供服務(wù)的 Server, 但是對(duì)于 ServiceManager 來(lái)說(shuō)它仍然是個(gè) Client.
Server 向 ServiceManager 中注冊(cè)了 Binder 以后, Client 就能通過名字獲得 Binder 的引用. Client 也利用保留的 0 號(hào)引用向 ServiceManager 請(qǐng)求訪問某個(gè) Binder. 比如,Client 申請(qǐng)?jiān)L問名字叫"張三"的 Binder 引用. ServiceManager 收到這個(gè)請(qǐng)求后從請(qǐng)求數(shù)據(jù)包中取出 Binder 名稱, 在查找表里找到對(duì)應(yīng)的條目, 取出對(duì)應(yīng)的 Binder 引用, 作為回復(fù)發(fā)送給發(fā)起請(qǐng)求的 Client. 從面相對(duì)象的角度看, Server 中的 Binder 實(shí)體現(xiàn)在有兩個(gè)引用: 一個(gè)位于 ServiceManager 中, 一個(gè)位于發(fā)起請(qǐng)求的 Client 中. 如果后面會(huì)有更多的 Client 請(qǐng)求該 Binder, 系統(tǒng)中就會(huì)有更多的引用指向這個(gè) Binder, 就像 Java 中一個(gè)對(duì)象有多個(gè)引用一樣.
我們已經(jīng)解釋清楚 Client, Server 借助 Binder 驅(qū)動(dòng)完成跨進(jìn)程通信的實(shí)現(xiàn)機(jī)制了, 但是還有個(gè)問題需要弄清楚, 比如 A 進(jìn)程想要 B 進(jìn)程中的某個(gè)對(duì)象(object) 是如何實(shí)現(xiàn)的呢, 畢竟它們屬于不同的進(jìn)程, A 進(jìn)程沒辦法直接使用 B 進(jìn)程中的 object.
前面我們說(shuō)過跨進(jìn)程通信的過程都有 Binder 驅(qū)動(dòng)的參與, 因此在數(shù)據(jù)流經(jīng) Binder 驅(qū)動(dòng)的時(shí)候 Binder 驅(qū)動(dòng)會(huì)對(duì)數(shù)據(jù)做一層轉(zhuǎn)換.
我們?cè)?Client端,向 ServiceManager 獲取具體的 Server 端的 Binder 引用的時(shí)候,會(huì)首先進(jìn)過 Binder 驅(qū)動(dòng),Binder 驅(qū)動(dòng)它并不會(huì)把真正的 Server 的 Binder 引用返回給 Client 端,而是返回一個(gè)代理的 java 對(duì)象,該對(duì)象具有跟 Server 端的 Binder 引用相同的方法簽名,這個(gè)對(duì)象為 ProxyObject,他具有跟 Server 的 Binder 實(shí)例一樣的方法,只是這些方法并沒有 Server 端的能力,這些方法只需要把請(qǐng)求參數(shù)交給 Binder 驅(qū)動(dòng)即可. 對(duì)于 Client 端來(lái)說(shuō)和直接調(diào)用 Server 中的方法是一樣的.
了解了上面之后, 我們大致可以推算出 Binder 的通信過程
1. 注冊(cè) ServiceManager
2. 注冊(cè) Server
3. Client 獲取 Server 的 Binder 引用
4. Client 與 Server 通信
使用ipcs命令查看,參數(shù)不同系統(tǒng)會(huì)有不同 Linux : ipcs -m Aix : ipcs -am 具體參數(shù)請(qǐng)參看操作系統(tǒng)幫助