Binder系列5—注冊服務(addService)

一.概述

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是一個完整的事務過程。 到此,其他進行便可以獲取該服務,使用服務提供的方法,下一篇文章將會講述如何獲取服務

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容