Binder機制(3)

StartThreadPool和joinThreadPool分析:


startThreadPool()的實現如下所示:


上面spawnpooledThread()函數的實現如下所示:


PoolThread是在IPCThreadState中定義的一個Thread子類,它的實現如下所示:


下面看看IPCThreadState的joinThreadPool的實現,因爲新創建的線程也會調用這個函數,具體代碼如下所示:



我們的兩個夥計在talkWithDriver,他們希望能從binder設備那裏找到點可做的事情。


到底有多少個線程在爲Service服務呢?目前看來是兩個。

1. startThreadPool中新啓動的線程通過joinThreadPool讀取binder設備,查看是否有請求。

2. 主線程也調用joinThradPool讀取binder設備,查看是否有請求。看來binder設備是支持多線程操作的,啓動一定是做了同步方面的工作

MediaServer這個進程一共註冊了4個服務,繁忙的時候,兩個線程會不會顯得有點少呢?另外,如果實現的服務負擔不是很重,完全可以不調用startThreadPool創建新的線程,使用主線程即可勝任。


Binder通信層和業務層的關係:


服務總管ServiceManager

ServiceManager的原理:

前面說過,defaultServiceManager返回的是一個BpServiceManager,通過它可以把命令請求發送給handle值爲0的目的端。按照之前的IServiceManager“家譜”來看,無論如何也應該有一個類從BnServiceanager派生出來並處理這些來自遠方的請求把?

很可惜,源碼中沒有這樣一個類存在。而是由ServceManager來完成了BnServiceManager未盡的工作。代碼在Service_manager.c中。


ServiceManager的入口函數:



打開binder設備,實現如下:



成爲老大:

怎麼才成爲系統中獨一無二的manager了呢?manager的實現如下:



binder_loop是一個很盡責的函數,它老是圍繞者binder設備轉悠,實現如下:



集中處理:

往binder_loop中傳的函數指針是svcmgr_handler,它的代碼如下:





服務的註冊:

上面提到的switch/case語句,將實現IServiceManager中定義的各個業務函數,下面重點看do_add_service這個函數,它最終完成了對addService請求的處理,代碼如下:


先將上面的函數暫時放一下,先介紹svc_can_register函數

1. 不是什麼都可以註冊的

do_add_service函數中的svc_can_register,是用來判斷註冊服務的進程是否有權限的,代碼如下所示:


allowed結構數組控制那些權限達不到root和system的進程,它的定義如下所示:



所以,如果Server進程權限不夠root和system,那麼就要記住allowed中添加線應的項。


添加服務項。

再回到我們的do_add_service中,如下所示:



到此爲止,服務註冊分析完畢。可以看到,ServiceManager不過就是保存了一些服務的信息。這樣做有什麼意義?

1. ServiceManager能集中管理系統內的所有服務,它能施加權限控制,並不是任何進程都能註冊服務的。

2. ServiceManager支持通過字符串名稱來查看對應的Service。這個功能很像DNS。

3. 由於各種原因的影響,Server進程可能生死無常。如果讓每個Client都去檢測,壓力太大了。現在有了統一的管理機構,Client只需要查詢ServiceManager,就能把握動向,得到最新信息。這可能正是ServiceManager存在的最大意義把。


MediaPlayerService和它的Client

前面一直在討論ServiceManager和它的Client,現在以MediaPlayerService的Client來進行分析。由於ServiceManager不是從BnServiceManager中派生的,所以之前沒有講請求數據是如何從通信層傳遞到業務層並進行處理的。

我們以MediaPlayerService和它的Client作爲分析對象,試着解決這些遺留問題。


查詢ServiceManager

前面分析過ServiceManager的作用,一個Client想要得到某個Service的信息,就必須線和ServiceManager打交道,通過調用getService函數來獲取對應的Service的信息。下面看下源於IMediaDeathNotifier.cpp中的例子getMediaPlayerService(),它的代碼如下所示:



有了BpMediaPlayerService,就能夠使用任何IMediaPlayerService提供的業務邏輯函數了。例如createMediaRecorder和createMetadataRetriever等

顯而易見的是,調用的這些函數都將把請求數據打包發送給Binder驅動,並由BpBinder中的handle值找到對應端的處理者來處理。這中間的過程爲:

1. 通信層接受到請求。

2. 遞交給業務層處理。


下面進一步瞭解這中間的過程:

子承父業:

根據前面的分析可知,MediaPlayerService駐留在MediaServer進程中,這個進程有兩個線程在talkWithDriver。假設其中有意個線程收到了請求信息,它最終會通過executeCommand調用來處理這個請求,實現代碼如下所示:




BBinder和業務層有什麼關係?我們以MediaPlayerService爲例,來梳理一下其派生關係,如下所示:


BnMediaPlayerService實現了onTransact函數,它將根據消息碼調用對應的業務邏輯函數,這些業務函數由MediaPlayerService來實現,這一路的歷程,如下面的代碼所示:



下面看看onTransact的實現:



Binder和線程的關係:

以MS爲例,如果現在程序運行正常,此時MS則:

1. 通過startThreadPool啓動一個線程,這個線程在talkWithDriver

2. 主線程通過joinThreadPool也在talkWithDriver。

至此我們直到,有兩個線程在和BInder設備打交道。這時在業務邏輯上需要與ServiceManager交互,比如要調用listServices打印所有服務的名字,假設這是MS中的第三個線程。按照之前的分析,它最終會調用IPCThradState的transact函數,這個函數會talkWithDriver並把請求發到ServiceManager進程,然後等待來自Binder設備的回覆。那麼現在一共有三個線程都在talkWithDriver。

ServiceManager處理完了listServices,把回覆結果寫回Binder驅動,那麼MS中哪個線程會收到回覆呢?


顯而易見,當然是調用listServices的那個線程會得到結果。爲什麼?因爲如果不這麼做,則會導致下面的情況發生:

1. 如果是沒有調用listServices的線程1或線程2得到回覆,那麼它們應該喚醒調用listServices的線程3.因爲這時已經有了結果,線程3應該從listServices函數調用中返回

2. 這其中的線程等待,喚醒,切換會浪費不少寶貴的時間片,而且代碼邏輯會及其複雜。

看來,。Binder設備把發起請求的線程牢牢地栓住了,必須收到回覆纔會放它離開。這種一一對應的方式極大簡化了代碼層的處理邏輯。


在socket編程中,如果一個socket關閉了,我們會無比希望另一端的select/poll/epoll/WaitForXXX有相應的返回,以示通知:



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章