Android IPC 通訊機制原始碼分析 一
Android IPC 通訊機制原始碼分析
----Albertchen
Binder通訊簡介:
Linux系統中程序間通訊的方式有:socket, named pipe,message queque, signal,share memory。Java系統中的程序間通訊方式有socket, named pipe等,android應用程式理所當然可以應用JAVA的IPC機制實現程序間的通訊,但我檢視android的原始碼,在同一終端上的應用軟體的通訊幾乎看不到這些IPC通訊方式,取而代之的是Binder通訊。Google為什麼要採用這種方式呢,這取決於Binder通訊方式的高效率。 Binder通訊是通過linux的binder driver來實現的,Binder通訊操作類似執行緒遷移(thread migration),兩個程序間IPC看起來就象是一個程序進入另一個程序執行程式碼然後帶著執行的結果返回。Binder的使用者空間為每一個程序維護著一個可用的執行緒池,執行緒池用於處理到來的IPC以及執行程序本地訊息,Binder通訊是同步而不是非同步。
Android中的Binder通訊是基於Service與Client的,所有需要IBinder通訊的程序都必須建立一個IBinder介面,系統中有一個程序管理所有的system service,Android不允許使用者新增非授權的System service,當然現在原始碼開發了,我們可以修改一些程式碼來實現新增底層system Service的目的。對使用者程式來說,我們也要建立server,或者Service用於程序間通訊,這裡有一個ActivityManagerService管理JAVA應用層所有的service建立與連線(connect),disconnect,所有的Activity也是通過這個service來啟動,載入的。ActivityManagerService也是載入在Systems Servcie中的。
Android虛擬機器啟動之前系統會先啟動service Manager程序,service Manager開啟binder驅動,並通知binder kernel驅動程式這個程序將作為System Service Manager,然後該程序將進入一個迴圈,等待處理來自其他程序的資料。使用者建立一個System service後,通過defaultServiceManager得到一個遠端ServiceManager的介面,通過這個介面我們可以呼叫addService函式將System service新增到Service Manager程序中,然後client可以通過getService獲取到需要連線的目的Service的IBinder物件,這個IBinder是Service的BBinder在binder kernel的一個參考,所以service IBinder 在binder kernel中不會存在相同的兩個IBinder物件,每一個Client程序同樣需要開啟Binder驅動程式。對使用者程式而言,我們獲得這個物件就可以通過binder kernel訪問service物件中的方法。Client與Service在不同的程序中,通過這種方式實現了類似執行緒間的遷移的通訊方式,對使用者程式而言當呼叫Service返回的IBinder介面後,訪問Service中的方法就如同呼叫自己的函式。
下圖為client與Service建立連線的示意圖
首先從ServiceManager註冊過程來逐步分析上述過程是如何實現的。
ServiceMananger程序註冊過程原始碼分析:
Service Manager Process(Service_manager.c):
Service_manager為其他程序的Service提供管理,這個服務程式必須在Android Runtime起來之前執行,否則Android JAVA Vm ActivityManagerService無法註冊。
int main(int argc, char **argv)
{
struct binder_state *bs;
void *svcmgr = BINDER_SERVICE_MANAGER;
bs = binder_open(128*1024); //開啟/dev/binder 驅動
if (binder_become_context_manager(bs)) {//註冊為service manager in binder kernel
LOGE("cannot become context manager (%s)/n", strerror(errno));
return -1;
}
svcmgr_handle = svcmgr;
binder_loop(bs, svcmgr_handler);
return 0;
}
首先開啟binder的驅動程式然後通過binder_become_context_manager函式呼叫ioctl告訴Binder Kernel驅動程式這是一個服務管理程序,然後呼叫binder_loop等待來自其他程序的資料。BINDER_SERVICE_MANAGER是服務管理程序的控制代碼,它的定義是:
/* the one magic object */
#define BINDER_SERVICE_MANAGER ((void*) 0)
如果客戶端程序獲取Service時所使用的控制代碼與此不符,Service Manager將不接受Client的請求。客戶端如何設定這個控制代碼在下面會介紹。
CameraSerivce服務的註冊(Main_mediaservice.c)
int main(int argc, char** argv)
{
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
LOGI("ServiceManager: %p", sm.get());
AudioFlinger::instantiate(); //Audio 服務
MediaPlayerService::instantiate(); //mediaPlayer服務
CameraService::instantiate(); //Camera 服務
ProcessState::self()->startThreadPool(); //為程序開啟緩衝池
IPCThreadState::self()->joinThreadPool(); //將程序加入到緩衝池
}
CameraService.cpp
void CameraService::instantiate() {
defaultServiceManager()->addService(
String16("media.camera"), new CameraService());
}
建立CameraService服務物件並新增到ServiceManager程序中。
client獲取remote IServiceManager IBinder介面:
sp<IServiceManager> defaultServiceManager()
{
if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
{
AutoMutex _l(gDefaultServiceManagerLock);
if (gDefaultServiceManager == NULL) {
gDefaultServiceManager = interface_cast<IServiceManager>(
ProcessState::self()->getContextObject(NULL));
}
}
return gDefaultServiceManager;
}
任何一個程序在第一次呼叫defaultServiceManager的時候gDefaultServiceManager值為Null,所以該程序會通過ProcessState::self得到ProcessState例項。ProcessState將開啟Binder驅動。
ProcessState.cpp
sp<ProcessState> ProcessState::self()
{
if (gProcess != NULL) return gProcess;
AutoMutex _l(gProcessMutex);
if (gProcess == NULL) gProcess = new ProcessState;
return gProcess;
}
ProcessState::ProcessState()
: mDriverFD(open_driver()) //開啟/dev/binder驅動
...........................
{
}
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& caller)
{
if (supportsProcesses()) {
return getStrongProxyForHandle(0);
} else {
return getContextObject(String16("default"), caller);
}
}
Android是支援Binder驅動的所以程式會呼叫getStrongProxyForHandle。這裡handle為0,正好與Service_manager中的BINDER_SERVICE_MANAGER一致。
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp<IBinder> result;
AutoMutex _l(mLock);
handle_entry* e = lookupHandleLocked(handle);
if (e != NULL) {
// We need to create a new BpBinder if there isn't currently one, OR we
// are unable to acquire a weak reference on this current one. See comment
// in getWeakProxyForHandle() for more info about this.
IBinder* b = e->binder; //第一次呼叫該函式b為Null
if (b == NULL || !e->refs->attemptIncWeak(this)) {
b = new BpBinder(handle);
e->binder = b;
if (b) e->refs = b->getWeakRefs();
result = b;
} else {
// This little bit of nastyness is to allow us to add a primary
// reference to the remote proxy when this team doesn't have one
// but another team is sending the handle to us.
result.force_set(b);
e->refs->decWeak(this);
}
}
return result;
}
第一次呼叫的時候b為Null所以會為b生成一BpBinder物件:
BpBinder::BpBinder(int32_t handle)
: mHandle(handle)
, mAlive(1)
, mObitsSent(0)
, mObituaries(NULL)
{
LOGV("Creating BpBinder %p handle %d/n", this, mHandle);
extendObjectLifetime(OBJECT_LIFETIME_WEAK);
IPCThreadState::self()->incWeakHandle(handle);
}
void IPCThreadState::incWeakHandle(int32_t handle)
{
LOG_REMOTEREFS("IPCThreadState::incWeakHandle(%d)/n", handle);
mOut.writeInt32(BC_INCREFS);
mOut.writeInt32(handle);
}
getContextObject返回了一個BpBinder物件。
interface_cast<IServiceManager>(
ProcessState::self()->getContextObject(NULL));
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
return INTERFACE::asInterface(obj);
}
將這個巨集擴充套件後最終得到的是:
sp<IServiceManager> IServiceManager::asInterface(const sp<IBinder>& obj)
{
sp<IServiceManager> intr;
if (obj != NULL) {
intr = static_cast<IServiceManager*>(
obj->queryLocalInterface(
IServiceManager::descriptor).get());
if (intr == NULL) {
intr = new BpServiceManager(obj);
}
}
return intr;
}
返回一個BpServiceManager物件,這裡obj就是前面我們建立的BpBInder物件。
client獲取Service的遠端IBinder介面
以CameraService為例(camera.cpp):
const sp<ICameraService>& Camera::getCameraService()
{
Mutex::Autolock _l(mLock);
if (mCameraService.get() == 0) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
binder = sm->getService(String16("media.camera"));
if (binder != 0)
break;
LOGW("CameraService not published, waiting...");
usleep(500000); // 0.5 s
} while(true);
if (mDeathNotifier == NULL) {
mDeathNotifier = new DeathNotifier();
}
binder->linkToDeath(mDeathNotifier);
mCameraService = interface_cast<ICameraService>(binder);
}
LOGE_IF(mCameraService==0, "no CameraService!?");
return mCameraService;
}
由前面的分析可知sm是BpCameraService物件://應該為BpServiceManager物件
virtual sp<IBinder> getService(const String16& name) const
{
unsigned n;
for (n = 0; n < 5; n++){
sp<IBinder> svc = checkService(name);
if (svc != NULL) return svc;
LOGI("Waiting for sevice %s.../n", String8(name).string());
sleep(1);
}
return NULL;
}
virtual sp<IBinder> checkService( const String16& name) const
{
Parcel data, reply;
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
return reply.readStrongBinder();
}
這裡的remote就是我們前面得到BpBinder物件。所以checkService將呼叫BpBinder中的transact函式:
status_t BpBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
// Once a binder has died, it will never come back to life.
if (mAlive) {
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}
mHandle為0,BpBinder繼續往下呼叫IPCThreadState:transact函式將資料發給與mHandle相關聯的Service Manager Process。
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
............................................................
if (err == NO_ERROR) {
LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
(flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
}
if (err != NO_ERROR) {
if (reply) reply->setError(err);
return (mLastError = err);
}
if ((flags & TF_ONE_WAY) == 0) {
if (reply) {
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
..............................
return err;
}
通過writeTransactionData構造要傳送的資料
status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
binder_transaction_data tr;
tr.target.handle = handle; //這個handle將傳遞到service_manager
tr.code = code;
tr.flags = bindrFlags;
。。。。。。。。。。。。。。
}
waitForResponse將呼叫talkWithDriver與對Binder kernel進行讀寫操作。當Binder kernel接收到資料後,service_mananger執行緒的ThreadPool就會啟動,service_manager查詢到CameraService服務後呼叫binder_send_reply,將返回的資料寫入Binder kernel,Binder kernel。
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
int32_t cmd;
int32_t err;
while (1) {
if ((err=talkWithDriver()) < NO_ERROR) break;
..............................................
}
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
............................................
#if defined(HAVE_ANDROID_OS)
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
#else
err = INVALID_OPERATION;
#endif
...................................................
}
通過上面的ioctl系統函式中BINDER_WRITE_READ對binder kernel進行讀寫。
相關推薦
Android IPC 通訊機制原始碼分析 一
Android IPC 通訊機制原始碼分析----Albertchen Binder通訊簡介: Linux系統中程序間通訊的方式有:socket, named pipe,message queque, signal,share memory。Java系統中的程序間通訊方式有socket, named
Android事件分發機制原始碼分析之Activity篇
在之前的事件分發分析中,曾提及到View的事件是由ViewGroup分發的,然而ViewGroup的事件我們只是稍微帶過是由Activity分發的。而我們知道,事件產生於使用者按下螢幕的一瞬間,事件生成後,經過一系列的過程來到我們的Activity層,那麼事件是怎樣從Activity傳遞
以太坊p2p網路(五):P2P模組TCP連線池網路通訊機制原始碼分析
上節中通過設定靜態節點BootstrapNodes節點來發現更多全網的其他節點,這部分只是發現節點並找出其中可以ping通的節點,但是還沒有進行使用,還沒建立TCP連線進行資料傳輸,協議處理等。 這裡主要分析P2P系統的TCP連線池的建立,以及是怎麼跟其他節點通
Android事件分發機制原始碼分析下----ViewGroup事件分發分析
ViewGroup事件分發機制 上篇文章從原始碼的角度對View的事件分發進行了分析,這篇文章繼續對事件分發進行介紹,從原始碼的角度分析ViewGroup的事件分發,從繼承關係看ViewGroup也屬於View的一種,但它的內部可以放置View,簡單的結論我
Mybatis工作機制原始碼分析—一次insert請求處理流程
本文從原始碼分析的角度分析Mybatis一次insert請求處理流程。 insert整體處理流程 時序圖 相關原始碼 /** SqlSessionTemplate.java */ public int insert(String statement, Obj
Android觸控式螢幕事件派發機制詳解與原始碼分析一(View篇)
【工匠若水 http://blog.csdn.net/yanbober】 Notice:閱讀完該篇之後如果想繼續深入閱讀Android觸控式螢幕事件派發機制詳解與原始碼分析下一篇請點選《Android觸控式螢幕事件派發機制詳解與原始碼分析二(ViewGroup篇)》檢視。 1
Android 8.0系統原始碼分析--Binder程序間通訊(一)
開始我們的沉澱之路,老羅的書中第二章講的是Android HAL層的知識,而且直接自己實現了一個虛擬的freg驅動程式,後面的幾節是分別從native、java層如何訪問這個虛擬的驅動程式介面,我這裡沒有這樣的環境,所以就不分析這節了,第三章的智慧指標我對比8.0系統原
Android IPC通訊之Binder機制分析
優勢: 與Linux中的Pipe管道、訊號Signal、訊息佇列Message、共享記憶體Share Memory、Socket插口等相比較,Binder在程序間傳輸資料,只需要執行一次拷貝操作。因此它不僅提高了效率,而且節省了記憶體空間。 角色: Ser
React Native 4 for Android原始碼分析 一《JNI智慧指標之介紹篇》
導讀 React Native 釋出以來將近一年多了,也被抄的火爆到不行,包括RN的中文網和各種資料也很多,加之SE5,Se6語法升級,學習成本並不在RN環境搭建和入門,關鍵還是對JS的掌握入門,不管你是用Native開發,h5開發,還是React
Android App啟動時Apk資源載入機制原始碼分析
在Andorid開發中我們要設定文字或圖片顯示,都直接通過Api一步呼叫就完成了,不僅是我們工程下res資源以及系統自帶的framwork資源也可以,那這些資源打包成Apk之後是如何被系統載入從而顯示出來的呢。 這裡我要從Apk安裝之後啟動流程開始講起,在桌面
Android Apk資源載入機制原始碼分析以及資源動態載入實現系列文章
Android系統中執行Apk時是如何對包內的資源進行載入以及我們開發中設定相關資源後又是如何被加載出來,這個系列我們可以學習系統載入資源的機制原理,然後我們再巧妙的利用學習系統載入技巧來打造我們自己的動態資源載入機制實現。 這個系列主要分為如下3部分內容來講
Android 8.0 RIL原始碼分析(一)
1.去電流程三中跟蹤到最後的時候可以看到其呼叫了RIL的dail方法 這裡繼續以此分析其從RIL到Modem的流程 @Override public void dial(String address, int clirMode, UUSInfo
Android非同步訊息處理機制原始碼分析
宣告:本文是參考了以下幾位大神的文章,自己按照自己的思維習慣整理的筆記,並新增一些相關的內容。如有不正確的地方歡迎留言指出,謝謝! 郭霖部落格 鴻洋部落格 任玉剛《Android開發藝術探索》 一. Andoid訊息機制概述
Android 訊息機制原始碼分析
我們知道,當應用啟動的時候,android首先會開啟一個主執行緒,主執行緒管理ui控制元件,進行事件分發,當我們要做一個耗時的操作時,如聯網讀取資料,獲取讀取本地較大的檔案的時候,你應該在子執行緒中操作,因為有ui的更新,android主執行緒是執行緒不安全的,如果將更新介
Android Handler機制原始碼分析
1)Looper: 一個執行緒可以產生一個Looper物件,由它來管理此執行緒裡的MessageQueue(訊息佇列)。 2)Handler: 你可以構造Handler物件來與Looper溝通,以便push新訊息到MessageQueue裡;或者接收Looper從Messa
Android Input系統原始碼分析一(啟動與初始化)
一. Input系統的啟動Android Framework是由一系列的Service所構建起來的,其中與Input相關的主要是InputManagerService(IMS)。我們看看IMS的啟動流程。IMS是在SystemServer.startOtherService中
Android中ViewGroup、View事件分發機制原始碼分析總結(雷驚風)
1.概述 很長時間沒有回想Android中的事件分發機制了,開啟目前的原始碼發現與兩三年前的實現程式碼已經不一樣了,5.0以後發生了變化,更加複雜了,但是萬變不離其宗,實現原理還是一樣的,在這裡將5.0以前的時間分發機制做一下原始碼剖析及總結。會涉及到幾個方
NSQ原始碼分析(一)——nsqd的初始化及啟動流程
nsq原始碼地址:https://github.com/nsqio/nsq 版本1.1.0 NSQ原始碼分析系列是我通過閱讀nsq的原始碼及結合網上的相關文章整理而成,由於在網上沒有找到很詳細和完整的文章,故自己親自整理了一份。如果有錯誤的地方,還請指正,希望這系列的文章給您帶來
Android IntentService用法和原始碼分析
關於IntentService的介紹,我個人覺得還是先看官方描述比較好: IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) o
CTS 原始碼分析(一) --CTS概況
1、什麼是CTS? Compatibility Test Suite 相容性測試套件 說白了,就是一套工具。一套軟體組成的測試工具。 2、哪裡有這套工具? &nb