1、概述
????????在Android中,系統提供的服務被包裝成一個個系統級service,這些service往往會在設備啟動之時添加進Android系統,當某個應用想要調用系統某個服務的功能時,往往是向系統發出請求,調用該服務的外部接口。在上一節我們了解到,這種外部接口,我們通常稱之為代理接口,也就是我們要拿到目標服務對應的代理對象。
?? ?//TODO
?? ?在Android8.0后,谷歌引入Treble機制,binder機制增加了hwbinder和vndbinder,其中vndbinder的守護進程為vndservicemanager。
?? ?vndservicemanager和service共用同一份代碼,只是傳入的參數和宏控制的流程有部分差異。
?? ?vndservicemanager會傳入參數“/dev/vndbinder”,servicemanager使用默認的“/dev/binder”。
?? ?servicemanager主要做了以下幾件事:
?? ?1、打開binder驅動,申請了128k的內存空間
?? ?2、然后調用binder_become_context_manager()讓自己成為整個系統中唯一的上下文管理器,其實也就是service管理器
?? ?3、調用binder_loop()進入無限循環,不斷監聽并解析binder驅動發來的命令
2、Binder架構
//TODO
3、servicemanager的啟動
//TODO
4、service_manager調用棧:
//TODO
5、源碼分析
? ? 5.1 主程序啟動main()
?????????//TODO
? ? 5.2 binder_open()
? ? ? ? servicemanager啟動后,先通過binder_open()來打開“/dev/binder”,代碼如下:
?? ??? ?
binder_open()的工作也比較簡單,分為以下幾步:
1、通過系統調用open()來打開“/dev/binder”,獲得一個句柄信息,在Binder驅動重對應的是函數binder_open()
2、通過ioctl獲取binder的版本信息,比較binder協議版本是否相同,不同則跳出,在Binder驅動重對應的是函數binder_ioctl()
3、通過mmap內存映射128k的內存空間,即把binder驅動文件的128K字節映射到了內存空間,這128K內存空間的servicemanager使用,在Binder驅動重對應的是函數binder_mmap()。
其他的binder服務進程會映射BINDER_VM_SIZE((1*1024*1024)-sysconf(SC_PAGE_SIZE)*2)的內存空間,SC_PAGE_SIZE表示一個page頁的大小,通常情況下為4K,即(1M-4K*2)=(1M-7K)
這個page的大小,不同廠家有時候也會調整大小,一般有1M,64K,4K,1KB,通常為4K。
ServiceManager進程mmap的內存大小可以通過adb shell命令得出:
其中0x7457b61000 -0x745d41000=0x20000,轉成10進制,即為128K
ARM32內存映射:
虛擬空間的低3GB部分從0-0XBFFFFFFF的虛擬線性地址,用戶態和內核態都可以尋址,這部分也是每個進程的獨立空間。
虛擬空間的高1G部分從0XC00000000到0XFFFFFFFF的虛擬地址,只有內核態的進程才能訪問,這種限制由頁目錄和頁眉描述符的權限標示位決定,通過MML啟動控制
ARM64內存映射:
默認情況下,32位系統默認只能支持4G的內存,在打開PAE后,最大可以擴展到64G的內存,隨著物理硬件的不斷升級,現在的內存越來越大,因此基本上都切換到了64位系統。
理論上講,64位的地址總線可以支持高達16EB(2^64)的內存。
2^64次方太大了,Linux內核只采用了64bits的一部分(開啟CONFIG_ARM64_64K_PAGES時使用42bits,頁大小是4K時使用39bits),該文假設使用的頁大小是4K(VA_BITS=39)
ARM64有足夠的虛擬地址,用戶空間和內核空間可以有各自的2^39=512GB的虛擬地址。
需要注意到,32位應用仍然擁有512GB的內核虛擬地址空間,并且不與內核共享自己的4GB空間,但在ARM32上,32位應用只有3GB的地址空間。
ARM32和ARM64內存地址比較:
?? ?5.3 binder_become_context_manager()
binder_become_context_manager()的作用是讓servicemanager成為整個系統中唯一的上下文管理器,其實也就是service管理器,這樣我們就可以把ServiceManager稱之為守護進程。
對應的binder驅動中操作如下:
從用戶空間拷貝ioctl的參數,調用binder_ioctl_set_ctx_mgr()進行設置
BINDER_SET_CONTEXT_MGR_EXT帶參數,BINDER_SET_CONTEXT_MGR不帶參數
binder_ioctl_set_ctx_mgr()的流程也比較簡單
1、先檢查當前進程是否具有注冊Context Manager的SEAndroid安全權限
2、如果具有SELinux權限,會為整個系統的上下管理器專門生成一個binder_node節點,便該節點的強弱應用加1
3、新創建的binder_node節點,記入context->binder_context_mgr_node,即ServiceManager進程的context binder節點,使之成為serviceManager的binder管理實體
?? ?5.4 binder_loop()
? ? ? ? 下一步進行守護進程的循環處理,binder_loop()會先向binder驅動發出了BC_ENTER_LOOPER命令,告訴binder驅動“本線程要進入循環狀態了”,接著進入一個for循環不斷調用ioctl()讀取發來的數據,接著解析這些數據
其中最重要的一個結構體是binder_write_read,它用來記錄Binder buffer中讀和寫的數據信息結構體如下:
? ? 5.5 binder_parse()
? ? ? ? 在binder_loop()進入for循環之后,核心處理流程就是ioctl和binder_parse(),即不停的從Binder驅動接收讀寫數據,進行binder解析后,進行處理。
在binder_loop()中聲明了一個128字節的棧內存-readbuf,用BINDER_WRITE_READ命令從驅動讀取一些內容,并傳入binder_parse(),binder_parse()根據binder驅動傳來的“BR_XXX”協議碼,進行相關的邏輯處理,最重要的有三個“BR_XXX”協議:
BR_TRANSACTION:事務處理,解析binder_transaction_data的數據,調用回調函數svcmgr_handler()進行服務的注冊,獲取等操作
BR_REPLY:消息回復
BR_DEAD_BINDER:死亡通知
只要binder_parse()解析正常,binder_loop()就會一直執行下去,ServiceManager進程不退出。
binder_parse()解析binder驅動傳來的readbuf的內存,readbuf擁有128字節的棧內存,每次可以只處理一個cmd,也可以有多個cmd,所以存在一個while循環,可以同時解析多個cmd,多個cmd的結構體如下圖所示:
? ? ? ? 5.5.1 BR_XXX 協議碼分析
? ? ? ? BR_XXX碼,也稱為Binder響應碼,這里介紹了ServiceManager處理的一些響應碼的作用:
? ? ? ? 5.5.2 BR_TRANSACTION解析
? ? ? ? 我們這里單獨分析下BR_TRANSACTION的流程,這也是我們常用的一個流程,這是Binder驅動向Server端發送請求數據。
從readbuf中解析出binder_transaction_data的數據,最后對接收和發送數據進行了封裝,傳遞給svcmgr_handler()做詳細處理
從上main的邏輯看,我們重點關注的是binder_transaction_data這個結構,binder_transaction_data說明了transaction到底在傳輸什么語義,而語義碼就記錄在其code成員中,不同語義碼需要攜帶的數據也是不同的,這些數據由data指定。
結構體說明如下:
從上面binder_transaction_data的結構可以看出,data可存入的數據很少,主要采用了數據其實地址和數據偏移量,根據代碼上下文可知,調用了bio_init_from_txn(),從txn.transaction_data解析出binder_io的信息,存入msg
? ? ? ? 5.5.2.1 bio_init_from_txn()
bio_init_from_txn()的作用就是把binder_transaction_data的“數據起始地址”,“偏移量”,“data數據的總大小”和“偏移數組中可用的條目”:
binder_transaction_data和binder_io的關聯
初始化完binder_io的replay,并把transaction_data轉換成了binder_io的msg后,調用回調函數svcmgr_handler()進行最終邏輯處理
? ? 5.6 svcmgr_handler()
? ? ? ? 在BR_TRANSACTION的命令解析后,就把binder_transaction_data_secctx的數據傳給回調函數svcmgr_handler()進行處理。
根據不同的傳輸語義碼(txn->code)來進行相應的操作:查詢服務,注冊服務,以及列舉所服務
源碼如下:
? ? 5.7 ServiceManager是如何管理service信息的?
? ? ? ? //TODO
? ? 5.8 注冊服務
? ? ? ? 根據傳入的code:SVC_MGR_ADD_SERVICE得知,本次binder流程想要進行服務注冊。
步驟:
從binder_io msg中獲取服務名稱和長度
從binder_io msg中獲取handle
檢查該服務是否有注冊的selinx權限
查詢服務列表svclist是否存在該handle,如果有handle,就更新該服務的handle信息,通過這個handle我們最終就能找到遠端的service實體
如果svclist不存在該服務,申請一個svcinfo的空間,把服務名,長度,handle等信息存入其中
把svcinfo進入svclist的鏈表中
再以BC_ACQUIRE命令,handle為目標的信息,通過ioctl發送給binder驅動
最后以BC_REQEST_DEATH_NOTIFICATION命令的信息,通過ioctl發送給binder驅動,主要用于清理內存等收尾工作? ? ? ??
? ? 5.9 查找服務
? ? ? ? //TODO
6、總結
? ? ? ? //TODO