一.概述
1.1 media服務注冊
media入口函數是main_mediaserver.cpp中的main()方法,代碼如下:
過程說明:
獲取ServiceManager: 講解了defaultServiceManager()返回的是BpServiceManager對象, 用于跟servicemanager進程通信;
理解Binder線程池的管理, 講解了startThreadPool和joinThreadPool過程.
本文的重點就是講解Native層服務注冊的過程.
1.2 類圖
在Native層的服務以media服務為例,來說一說服務注冊過程,先來看看media的整個的類關系圖
圖解:
藍色代表的是注冊MediaPlayerService服務所涉及的類
綠色代表的是Binder架構中與Binder驅動通信過程中的最為核心的兩個類;
紫色代表的是注冊服務和獲取服務的公共接口/父類;
1.3 時序圖
先通過一幅圖來說說,media服務啟動過程是如何向servicemanager注冊服務的。
二. ProcessState
2.1 ProcessState::self
[-> ProcessState.cpp]
獲得ProcessState對象: 這也是單例模式,從而保證每一個進程只有一個ProcessState對象。其中gProcess和gProcessMutex是保存在Static.cpp類的全局變量。
2.2 ProcessState初始化
[-> ProcessState.cpp]
ProcessState的單例模式的惟一性,因此一個進程只打開binder設備一次,其中ProcessState的成員變量mDriverFD記錄binder驅動的fd,用于訪問binder設備。
BINDER_VM_SIZE = (1*1024*1024) - (4096 *2), binder分配的默認內存大小為1M-8k。
DEFAULT_MAX_BINDER_THREADS = 15,binder默認的最大可并發訪問的線程數為16。
2.3 open_driver
[-> ProcessState.cpp]
open_driver作用是打開/dev/binder設備,設定binder支持的最大線程數。關于binder驅動的相應方法,見文章Binder Driver初探。
ProcessState采用單例模式,保證每一個進程都只打開一次Binder Driver。
2.4 mmap
參數說明:
addr: 代表映射到進程地址空間的起始地址,當值等于0則由內核選擇合適地址,此處為0;
size: 代表需要映射的內存地址空間的大小,此處為1M-8K;
prot: 代表內存映射區的讀寫等屬性值,此處為PROT_READ(可讀取);
flags: 標志位,此處為MAP_PRIVATE(私有映射,多進程間不共享內容的改變)和 MAP_NORESERVE(不保留交換空間)
fd: 代表mmap所關聯的文件描述符,此處為mDriverFD;
offset:偏移量,此處為0。
mmap()經過系統調用,執行binder_mmap過程。
三. 服務注冊
3.1 instantiate
[-> MediaPlayerService.cpp]
注冊服務MediaPlayerService:由defaultServiceManager()返回的是BpServiceManager,同時會創建ProcessState對象和BpBinder對象。 故此處等價于調用BpServiceManager->addService。其中MediaPlayerService位于libmediaplayerservice庫.
3.2 BpSM.addService
[-> IServiceManager.cpp ::BpServiceManager]
服務注冊過程:向ServiceManager注冊服務MediaPlayerService,服務名為”media.player”;
3.2.1 writeStrongBinder
[-> parcel.cpp]
3.2.2 flatten_binder
[-> parcel.cpp]
將Binder對象扁平化,轉換成flat_binder_object對象。
對于Binder實體,則cookie記錄Binder實體的指針;
對于Binder代理,則用handle記錄Binder代理的句柄;
關于localBinder,代碼見Binder.cpp。
3.2.3 finish_flatten_binder
3.2.3 finish_flatten_binder
將flat_binder_object寫入out。
3.3 BpBinder::transact
[-> BpBinder.cpp]
Binder代理類調用transact()方法,真正工作還是交給IPCThreadState來進行transact工作。先來 看看IPCThreadState::self的過程。
3.3.1 IPCThreadState::self
[-> IPCThreadState.cpp]
\TLS是指Thread local storage(線程本地儲存空間),每個線程都擁有自己的TLS,并且是私有空間,線程之間不會共享。通過pthread_getspecific/pthread_setspecific函數可以獲取/設置這些空間中的內容。從線程本地存儲空間中獲得保存在其中的IPCThreadState對象。
3.3.2 IPCThreadState初始化
[-> IPCThreadState.cpp]
每個線程都有一個IPCThreadState,每個IPCThreadState中都有一個mIn、一個mOut。成員變量mProcess保存了ProcessState變量(每個進程只有一個)。
mIn 用來接收來自Binder設備的數據,默認大小為256字節;
mOut用來存儲發往Binder設備的數據,默認大小為256字節。
3.4 IPC::transact
[-> IPCThreadState.cpp]
IPCThreadState進行transact事務處理分3部分:
errorCheck() //數據錯誤檢查
writeTransactionData() // 傳輸數據
waitForResponse() //f等待響應
3.5 IPC.writeTransactionData
[-> IPCThreadState.cpp]
其中handle的值用來標識目的端,注冊服務過程的目的端為service manager,此處handle=0所對應的是binder_context_mgr_node對象,正是service manager所對應的binder實體對象。binder_transaction_data結構體是binder驅動通信的數據結構,該過程最終是把Binder請求碼BC_TRANSACTION和binder_transaction_data結構體寫入到mOut。
transact過程,先寫完binder_transaction_data數據,其中Parcel data的重要成員變量:
mDataSize:保存再data_size,binder_transaction的數據大小;
mData: 保存在ptr.buffer, binder_transaction的數據的起始地址;
mObjectsSize:保存在ptr.offsets_size,記錄著flat_binder_object結構體的個數;
mObjects: 保存在offsets, 記錄著flat_binder_object結構體在數據偏移量;
接下來執行waitForResponse()方法。
3.6 IPC.waitForResponse
[-> IPCThreadState.cpp]
在waitForResponse過程, 首先執行BR_TRANSACTION_COMPLETE;另外,目標進程收到事務后,處理BR_TRANSACTION事務。 然后發送給當前進程,再執行BR_REPLY命令。
3.7 IPC.talkWithDriver
[-> IPCThreadState.cpp]
binder_write_read結構體用來與Binder設備交換數據的結構, 通過ioctl與mDriverFD通信,是真正與Binder驅動進行數據讀寫交互的過程。 主要是操作mOut和mIn變量。
ioctl()經過系統調用后進入Binder Driver.
四. Binder Driver
ioctl -> binder_ioctl -> binder_ioctl_write_read
4.1 binder_ioctl_write_read
[-> binder.c]
4.2 binder_thread_write
4.3 binder_transaction
注冊服務的過程,傳遞的是BBinder對象,故[小節3.2.1]的writeStrongBinder()過程中localBinder不為空, 從而flat_binder_object.type等于BINDER_TYPE_BINDER。
服務注冊過程是在服務所在進程創建binder_node,在servicemanager進程創建binder_ref。 對于同一個binder_node,每個進程只會創建一個binder_ref對象。
向servicemanager的binder_proc->todo添加BINDER_WORK_TRANSACTION事務,接下來進入ServiceManager進程。
4.3.1 binder_get_node
從binder_proc來根據binder指針ptr值,查詢相應的binder_node。
4.3.2 binder_new_node
4.3.3 binder_get_ref_for_node
handle值計算方法規律:
每個進程binder_proc所記錄的binder_ref的handle值是從1開始遞增的;
所有進程binder_proc所記錄的handle=0的binder_ref都指向service manager;
同一個服務的binder_node在不同進程的binder_ref的handle值可以不同;
五. ServiceManager
由Binder系列3—啟動ServiceManager已介紹其原理,循環在binder_loop()過程, 會調用binder_parse()方法。
5.1 binder_parse
[-> servicemanager/binder.c]
5.2 svcmgr_handler
[-> service_manager.c]
5.3 do_add_service
[-> service_manager.c]
\svcinfo記錄著服務名和handle信息,保存到svclist列表。
5.4 binder_send_reply
[-> servicemanager/binder.c]
binder_write進入binder驅動后,將BC_FREE_BUFFER和BC_REPLY命令協議發送給Binder驅動, 向client端發送reply.
六. 總結
服務注冊過程(addService)核心功能:在服務所在進程創建binder_node,在servicemanager進程創建binder_ref。 其中binder_ref的desc再同一個進程內是唯一的:
每個進程binder_proc所記錄的binder_ref的handle值是從1開始遞增的;
所有進程binder_proc所記錄的handle=0的binder_ref都指向service manager;
同一個服務的binder_node在不同進程的binder_ref的handle值可以不同;
Media服務注冊的過程涉及到MediaPlayerService(作為Client進程)和Service Manager(作為Service進程),通信流程圖如下所示:
過程分析:
MediaPlayerService進程調用ioctl()向Binder驅動發送IPC數據,該過程可以理解成一個事務binder_transaction(記為T1),執行當前操作的線程binder_thread(記為thread1),則T1->from_parent=NULL,T1->from =?thread1,thread1->transaction_stack=T1。其中IPC數據內容包含:
Binder協議為BC_TRANSACTION;
Handle等于0;
RPC代碼為ADD_SERVICE;
RPC數據為”media.player”。
Binder驅動收到該Binder請求,生成BR_TRANSACTION命令,選擇目標處理該請求的線程,即ServiceManager的binder線程(記為thread2),則 T1->to_parent = NULL,T1->to_thread =?thread2。并將整個binder_transaction數據(記為T2)插入到目標線程的todo隊列;
Service Manager的線程thread2收到T2后,調用服務注冊函數將服務”media.player”注冊到服務目錄中。當服務注冊完成后,生成IPC應答數據(BC_REPLY),T2->form_parent = T1,T2->from = thread2, thread2->transaction_stack = T2。
Binder驅動收到該Binder應答請求,生成BR_REPLY命令,T2->to_parent = T1,T2->to_thread = thread1, thread1->transaction_stack = T2。 在MediaPlayerService收到該命令后,知道服務注冊完成便可以正常使用。
整個過程中,BC_TRANSACTION和BR_TRANSACTION過程是一個完整的事務過程;BC_REPLY和BR_REPLY是一個完整的事務過程。 到此,其他進行便可以獲取該服務,使用服務提供的方法,下一篇文章將會講述如何獲取服務。