Binder解析(1)

1. Binder是干什么的?

簡單來說binder就是用于進程間通訊的踪区。但從不同角度對binder可以有不同的理解昆烁。引用《Android開發(fā)藝術探索》中的解釋:

  • Binder是Android中一個實現(xiàn)了IBinder接口的類
  • 是一種跨進程通訊的方式
  • 是一種虛擬的物理設備,驅動為/dev/binder
  • 在Android中是ServiceManager連接各種Manager極其Service的橋梁
  • 是Android客戶端與服務端的媒介

估計在看完這些概念時缎岗,感覺是一臉懵静尼。不著急先繼續(xù)往下看。

在深入了解binder前传泊,我們有必要了解一些相關概念來循序漸進:

1.1 App為什么要多進程茅郎?

通常我們在日常開發(fā)中特定場景下會使用多進程,比如接入webview或渤、視頻音樂播放、大圖瀏覽奕扣、推送等功能時薪鹦,會開啟多進程。開啟多進程的方法很簡單惯豆,在對應的組件manifest文件節(jié)點下面指定process屬性即可棒卷。

為什么要開啟多進程另萤?

  • 系統(tǒng)分配的dalvik虛擬機內(nèi)存空間有限,當使用過程中內(nèi)存超出時會oom。一個進程代表一個虛擬機锉矢,代表一塊內(nèi)存,開啟多進程相當于向系統(tǒng)多申請了內(nèi)存空間肤舞。
  • 使用多進程可以實現(xiàn)風險隔離图仓。當一個進程掛掉了,不會影響另外一個進程揭厚。

ps:

查看系統(tǒng)給App分配的內(nèi)存空間使用命令:

getprop dalvik.vm.heapsize

1.2 了解到多進程的作用后却特,那么Linux進程間通訊的方式有哪些?

管道筛圆、信號量裂明、socket、共享內(nèi)存太援、binder等

那Android中為什么要使用binder來進行通訊闽晦?

2. Biner通訊特點

binder與共享內(nèi)存、socket對比

從表中可以看出

binder相比共享內(nèi)存提岔,在傳遞數(shù)據(jù)時會多一次數(shù)據(jù)拷貝仙蛉,而比傳統(tǒng)的通訊方式socket少一次數(shù)據(jù)拷貝。

那又什么是數(shù)據(jù)拷貝碱蒙?

說到這里就不得不先說下Android的內(nèi)存劃分了捅儒,在我們的Android系統(tǒng)中,內(nèi)存被操作系統(tǒng)劃分為兩塊:內(nèi)核空間和用戶空間:內(nèi)核空間是共享的,是內(nèi)核代碼運行的地方巧还,可以調(diào)用系統(tǒng)的一切資源鞭莽。用戶空間是每個進程獨有的,是用戶程序代碼運行的地方麸祷,從安全角度考慮澎怒,它不能直接調(diào)用系統(tǒng)的相關資源,只能通過系統(tǒng)接口(system call)來向內(nèi)核空間發(fā)出指令作出相關操作阶牍,比如訪問網(wǎng)絡喷面,讀寫文件等,都是通過內(nèi)核空間中轉了一次的走孽。兩者相互隔離惧辈,即當用戶的程序崩潰了,內(nèi)核也不會受影響磕瓷。

二者關系如下圖

例如當應用需要像系統(tǒng)中寫入一個文件時盒齿,它會分成以下幾步

  1. 調(diào)用wirte,告訴內(nèi)核需要寫入數(shù)據(jù)的開始地址與長度
  1. 內(nèi)核將數(shù)據(jù)拷貝到內(nèi)核緩存
  1. 由操作系統(tǒng)調(diào)用,將數(shù)據(jù)拷貝到磁盤困食,完成寫入

BInder方式為什么高效呢边翁?下面來對比下傳統(tǒng)IPC方式與Binder方式進程1需要向進程2寫入數(shù)據(jù)時的不同

傳統(tǒng)IPC方式

Binder方式

用binder來進行進程間通訊時,在從內(nèi)核空間拷貝數(shù)據(jù)到進程2這一步硕盹,會通過mmap()內(nèi)存映射關系符匾,直接與數(shù)據(jù)接收方共享同一塊物理內(nèi)存,從而減少一次數(shù)據(jù)拷貝瘩例。所以使用binder比傳統(tǒng)IPC方式更高效啊胶。那么有人會問,那寫入的時候也使用mmap不是更高效嗎垛贤?直接不用拷貝了创淡。其實這就是共享內(nèi)存。就是上面表中的0次拷貝南吮。但共享內(nèi)存由于安全關系琳彩,同步控制復雜,反而沒有binder高效部凑。

那什么是mmap呢露乏?

Linux通過將一個虛擬內(nèi)存區(qū)域與一個磁盤上的對象關聯(lián)起來,以初始化這個虛擬內(nèi)存區(qū)域的內(nèi)容涂邀,這個過程稱為內(nèi)存映射(memory mapping)

用戶空間與內(nèi)核空間上內(nèi)存概念都是邏輯地址瘟仿,而對應到真正的物理內(nèi)存地址,背后存在著地址一對一的映射比勉。通過mmap實現(xiàn)這樣的映射關系后劳较,就可以采用指針的方式讀寫操作某一段內(nèi)存驹止,而系統(tǒng)會自動回寫到對應的文件磁盤上。

前面只是大概的描述了下兩個進程間的通訊過程观蜗。我們知道Android 上層都是用 Java 寫的臊恋,但是 Binder 驅動是 C 實現(xiàn),他們之間數(shù)據(jù)具體是如何傳遞的呢墓捻?

3. Binder執(zhí)行流程

下面再給出一張binder的框架圖抖仅,和涉及到的類圖

binder框架圖

binder涉及到的類圖

簡單來說就是Fromwork(java)層與native(c)層通過JNI搭建一個橋梁,注冊各種方法砖第,然后到native層又通過系統(tǒng)提供的接口(system call)與內(nèi)核層通訊撤卢,來進行一些操作,比如注冊服務與獲取服務等梧兼。

下面再從源碼的角度來看看放吩,這一步一步是怎么實現(xiàn)的

4. Binder的JNI方法注冊

4.1 Android系統(tǒng)開機啟動啟動流程

首先來回顧下Android系統(tǒng)的開機啟動流程


① 電源啟動

② 引導芯片代碼加載引導程序BootLoader到RAM中去執(zhí)行。BootLoader是Android操作系統(tǒng)開始運行前的一個小程序羽杰,它負責把操作OS系統(tǒng)拉起來并運行渡紫。

③ Linux內(nèi)核系統(tǒng)啟動后開始系統(tǒng)設置,并在系統(tǒng)文件中找到init.rc文件忽洛,啟動init進程

④ init進程啟動后,會做一些初始化及啟動屬性服務的工作环肘,并且啟動zygote進程

⑤ zygote進程啟動后會創(chuàng)建java虛擬機并為虛擬機注冊jni方法欲虚,創(chuàng)建服務端socket,啟動SystemServer進程

⑥ SystemServer會啟動Binder線程池及SystemServiceManager,并啟動各種Service(AMS、WMS等)

⑦ AMS會啟動Launcher,Launcher啟動后會把已安裝的應用圖標顯示到手機桌面上

4.2 zygote進程啟動注冊jni

在上述的第四步中悔雹,init進程通過解析init.zygote.rc文件复哆,創(chuàng)建zygote進程。zygote進程所對應的可執(zhí)行程序是app_process,所對應的源文件是app_main.cpp腌零。該文件位于Android系統(tǒng)源碼的/system/core/rootdir/init.zygote32.rc目錄下

4.3 源碼跟蹤

framworks/base/cmds/app_process/app_main.cpp


//執(zhí)行main方法

//將zygote標志位置為true


//運行AndroidRuntime.cpp的start方法

framworks/base/core/jni/AndroidRuntime.cpp


//執(zhí)行start方法

//執(zhí)行startReg()

//注冊jni方法

//循環(huán)注冊jni方法

//里面包含binder

framworks/base/core/jni/android_util_Binder.cpp


//具體執(zhí)行注冊

以第一個為例梯找,下面兩個方法注冊都差不多就不再一 一分析。

//這里可以理解為相當于做個一個反射益涧,將java層的方法一一對應起來锈锤,建立關聯(lián)。

通過findClassOrDie方法闲询,查找文件kBinderPathName="android/os/Binder"返回對應的class對象久免;通過gBinderOffsets結構體,保存Java層Binder類的信息扭弧,為JNI層訪問Java層提供通道阎姥;通過RegisterMethodsOrDie為gBinderMethods數(shù)組完成映射關系,從而為Java層訪問JNI層提供通道


經(jīng)過上面的源碼流程鸽捻,把java層到native層JNI注冊的流程摸清楚了呼巴,下面再介紹native層到內(nèi)核層systemCall是怎么進行的

5. Binder的驅動啟動

5.1 示意圖

在Linux中泽腮,一切皆文件。對binder的驅動初始化也是從binder.c 開始的衣赶。主要涉及到binder_init()诊赊、binder_open()、binder_mmap()屑埋、binder_ioctl()這幾個函數(shù)豪筝。

5.2 源碼追蹤

#kernel/drivers/staging/android/binder.c

注意該文件不在Android源碼里面,位于Linux源碼里面摘能。與Android源碼framwork文件夾里那個不是一回事续崖。


在binder_init函數(shù)中調(diào)用了init_binder_device(const char *name)函數(shù),在這里主要干了三件事

  1. 為binder設備開辟內(nèi)存空間
  1. 初始化驅動設備
  1. 將hlist節(jié)點添加到binder_devices為表頭的設備鏈表

其中在第二步初始化時拿到了binder_fops結構體指針团搞,在后續(xù)過程中严望,會調(diào)用到binder_fops.mmop、binder_fops.open逻恐、binder_ioctl

先來看binder_open函數(shù)中

在這里主要干了四件事:

  1. 分配內(nèi)存像吻,創(chuàng)建binder_proc
  1. 初始化binder_proc(可以理解為把java層的binder相關信息保存到proc)
  1. 將proc托管到file中
  1. 把proc添加到鏈表中procs保管起來

再來看binder_mmap 函數(shù)


android6.0已經(jīng)把binder_buffer有關的操作和binder.c分開了,實現(xiàn)在binder_alloc.c文件里面复隆,這里不再詳述拨匆。

感興趣可查看

https://github.com/torvalds/linux/blob/master/drivers/android/binder_alloc.c

在這里主要干了三件事

  1. 通過用戶空間的虛擬內(nèi)存大小分配一塊內(nèi)核的虛擬內(nèi)存,二者大小相等挽拂,且不超過4M
  1. 分配一塊物理內(nèi)存--4KB (先分配1頁one page)等需要用的時候再擴大惭每,免費內(nèi)存浪費
  1. 計算到的用戶空間和內(nèi)核空間地址偏移量,并把這塊物理內(nèi)存分別映射到用戶空間的虛擬內(nèi)存和內(nèi)核的虛擬內(nèi)存

注意mmap所映射的內(nèi)容大小最大為4M亏栈,是由sz_4M屬性決定的台腥。

緊接著來到binder_ioctl函數(shù)中

它會根據(jù)傳遞過來的命令做出相應的操作,比如讀寫操作

找了這么就終于來到我們前面提到的copy_from_user()和copy_to_user()中了绒北,這兩個函數(shù)分別把用戶空間數(shù)據(jù)ubuf拷貝到內(nèi)核空間數(shù)據(jù)bwr及反之黎侈。

注意這里拷貝的是數(shù)據(jù)頭,不是有效數(shù)據(jù)

5.4 常見面試題

順便提一句闷游,在面試的時候經(jīng)常會被問到intent最大一次能攜帶多少數(shù)據(jù)峻汉?答案:1M-8k(8k是兩個pagesize,一個pagesize是申請物理內(nèi)存的最小單元)

原因是如果一個進程使用ProcessState這個類來初始化Binder服務脐往,這個進程的Binder內(nèi)核內(nèi)存上限就是BINDER_VM_SIZE俱济,也就是1MB-8KB。

frameworks/native/libs/binder/ProcessState.cpp

初始化在zygote進程初始化binder服務時調(diào)用的


?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末钙勃,一起剝皮案震驚了整個濱河市蛛碌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辖源,老刑警劉巖蔚携,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件希太,死亡現(xiàn)場離奇詭異,居然都是意外死亡酝蜒,警方通過查閱死者的電腦和手機誊辉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來亡脑,“玉大人堕澄,你說我怎么就攤上這事∶棺桑” “怎么了蛙紫?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長途戒。 經(jīng)常有香客問我坑傅,道長,這世上最難降的妖魔是什么喷斋? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任唁毒,我火速辦了婚禮,結果婚禮上星爪,老公的妹妹穿的比我還像新娘浆西。我一直安慰自己,他們只是感情好顽腾,可當我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布近零。 她就那樣靜靜地躺著,像睡著了一般崔泵。 火紅的嫁衣襯著肌膚如雪秒赤。 梳的紋絲不亂的頭發(fā)上猪瞬,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天憎瘸,我揣著相機與錄音,去河邊找鬼陈瘦。 笑死幌甘,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的痊项。 我是一名探鬼主播锅风,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鞍泉!你這毒婦竟也來了皱埠?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤咖驮,失蹤者是張志新(化名)和其女友劉穎边器,沒想到半個月后训枢,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡忘巧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年恒界,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片砚嘴。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡十酣,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出际长,到底是詐尸還是另有隱情耸采,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布也颤,位于F島的核電站洋幻,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏翅娶。R本人自食惡果不足惜文留,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望竭沫。 院中可真熱鬧燥翅,春花似錦、人聲如沸蜕提。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谎势。三九已至凛膏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間脏榆,已是汗流浹背猖毫。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留须喂,地道東北人吁断。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像坞生,于是被迫代替她去往敵國和親仔役。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,515評論 2 359