在了解 Binder 跨進(jìn)程通信原理之前, 我們先了解一下 Linux 傳統(tǒng)的進(jìn)程間通信的概念和基本原理, 這樣有助于我們更好的理解 Binder 的通信原理. 這個(gè)部分基本都是理論, 基礎(chǔ)不是很好的同學(xué), 還是要耐著性子看完. 最起碼,我看完后受益匪淺.
(如有侵權(quán), 請(qǐng)聯(lián)系刪除)
1.基本概念
Linux 中跨進(jìn)程通信有一些幾個(gè)基本的概念
- 進(jìn)程隔離
- 進(jìn)程空間劃分: 用戶空間(User Space) & 內(nèi)核空間(Kernel Space)
- 系統(tǒng)調(diào)用: 用戶態(tài) & 內(nèi)核態(tài)
1.1 進(jìn)程隔離
- 簡單的說就是操作系統(tǒng)中, 進(jìn)程與進(jìn)程間的內(nèi)存是不共享的. 兩個(gè)進(jìn)程就像兩個(gè)平行的世界, A進(jìn)程沒辦法直接訪問 B 進(jìn)程的數(shù)據(jù).這就是進(jìn)程隔離的通俗解釋. A 進(jìn)程和 B 進(jìn)程之間要進(jìn)行數(shù)據(jù)交互就得采用特殊的通信機(jī)制: 進(jìn)程間通信(IPC)
1.2 進(jìn)程空間劃分
- 現(xiàn)在操作系統(tǒng)都是采用的虛擬存儲(chǔ)器, 對(duì) 32 位系統(tǒng)而言, 它的尋址空間 (虛擬存儲(chǔ)空間) 就是 2 的 32 吃飯,也就是 4GB. 操作系統(tǒng)的核心是內(nèi)核,獨(dú)立于普通的應(yīng)用程序, 可以訪問受保護(hù)的內(nèi)存空間, 也可以訪問底層硬件設(shè)備的權(quán)限. 為了保護(hù)用戶進(jìn)程不能直接操作內(nèi)核, 保證內(nèi)核的安全, 操作系統(tǒng)從邏輯上將虛擬空間劃分為用戶空間(User Space) 和內(nèi)核空間(Kernel). 針對(duì) Linux 操作系統(tǒng)而言, 將最高的 1GB 直接供內(nèi)核使用. 稱為內(nèi)核空間. 較低的 3GB 直接供各進(jìn)程使用,稱為用戶空間. 簡單來說就是, 內(nèi)核空間是系統(tǒng)內(nèi)核運(yùn)行的空間, 用戶空間是用戶程序運(yùn)行的空間. 為了保證安全性, 他們之間是隔離的.
1.3 系統(tǒng)調(diào)用: 用戶態(tài) & 內(nèi)核態(tài)
Linux 使用兩級(jí)保護(hù)機(jī)制: 0 級(jí)供系統(tǒng)內(nèi)核使用, 3 級(jí)供用戶程序使用
- 當(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)). 此時(shí)處理器處于特權(quán)級(jí)最高的(0 級(jí))內(nèi)核代碼中執(zhí)行. 當(dāng)進(jìn)程處于內(nèi)核態(tài)時(shí), 執(zhí)行的內(nèi)核代碼會(huì)使用當(dāng)前進(jìn)程的內(nèi)核棧. 每個(gè)進(jìn)程都有自己的內(nèi)核棧.
- 當(dāng)進(jìn)程在執(zhí)行用戶自己的代碼的時(shí)候, 我們稱其處于用戶運(yùn)行態(tài)(用戶態(tài)). 此時(shí)處理器在特權(quán)級(jí)最低的(3 級(jí)) 用戶代碼中運(yùn)行.
- 系統(tǒng)調(diào)用, 主要通過如下兩個(gè)函數(shù)來實(shí)現(xiàn).
-
copy_from_user()
//將數(shù)據(jù)從用戶空間 copy 到內(nèi)核空間 -
copy_to_user()
//將數(shù)據(jù)從內(nèi)核空間 copy 到用戶空間
-
2. 傳統(tǒng) IPC 通信的基本原理.
通常做法是將消息發(fā)送方要發(fā)送的數(shù)據(jù)放在內(nèi)存緩存區(qū)中, 通過系統(tǒng)圖調(diào)用進(jìn)入內(nèi)核態(tài), 然后內(nèi)核程序在內(nèi)核空間分配內(nèi)存, 開辟一塊內(nèi)核緩存區(qū), 調(diào)用 copy_from_user()
函數(shù)將數(shù)據(jù)從用戶空間的內(nèi)存緩存區(qū) copy 到內(nèi)核空間的內(nèi)核緩存區(qū)中. 同樣的接收方進(jìn)程在接收數(shù)據(jù)時(shí)在自己的用戶空間開辟一塊內(nèi)存緩存去, 然后內(nèi)核程序調(diào)用 copy_to_user()
函數(shù)將數(shù)據(jù)從內(nèi)核緩存區(qū) copy 到接收進(jìn)程的內(nèi)存緩存區(qū). 這樣數(shù)據(jù)發(fā)送方進(jìn)程和數(shù)據(jù)接收方進(jìn)程就完成了一次數(shù)據(jù)傳輸. 也就是完成了一次進(jìn)程間通信.
傳統(tǒng)的 IPC 通信方式有兩個(gè)問題
- 性能低下, 一次數(shù)據(jù)傳遞需要經(jīng)歷: 內(nèi)存緩存區(qū)(發(fā)送方) -> 內(nèi)核緩存區(qū) -> 內(nèi)存緩存區(qū)(接收方). 兩次的數(shù)據(jù) copy
- 接收數(shù)據(jù)的緩存區(qū)由數(shù)據(jù)接收進(jìn)程提供, 但是接收進(jìn)程并不知道需要多大的空間來存放將要傳遞過來的數(shù)據(jù), 因此只能開辟盡可能大的內(nèi)存控件或者先調(diào)用 API 接收消息頭來獲取消息體的大小, 但是這兩種做法不是浪費(fèi)空間就是浪費(fèi)時(shí)間.