進程間通信——Binder
概述
Binder是Android中使用最廣泛的IPC機制。如果統觀Binder中的各個組成元素,就會驚奇地發現它和TCP/IP網絡有很多相似之處:
.Binder驅動——路由器
.Service Manager——DNS
.Binder Client——客戶端
.Binder Server——服務器
Service Manager在Binder通信過程中的唯一標誌永遠都是0.
Binder驅動與協議
Android系統是基於Linux內核的,因而它所依賴的Binder驅動也必須是一個標準的Linux驅動。具體而言,Binder Driver會將自己註冊成
一個misc device,並向上提供一個/dev/binder節點——值得一提的是,Binder節點並不對應真實的硬件設備。Binder驅動運行於內核態,
可以提供open(),ioctl(),mmap()等常用的文件操作。
Binder驅動源碼在Kernel工程的drivers/staging/android目錄中。
Android系統爲什麼把Binder註冊成misc device類型的驅動呢?
這種”雜項”驅動的主設備號統一爲10,次設備號則是每種設備獨有的。驅動程序也可以通過設置MISC_DYNAMIC_MINOR來由系統動態分配次設備號。Linux中的字符設備通常要經過alloc_chrdev_region(),cdev_init()等一系列操作才能在內核中註冊自己。而misc類型驅動則相對簡單,只需要調用misc_register()就可輕鬆解決。
Binder驅動總共爲上層應用程序提供了6個接口:
(1)binder_poll(2)binder_ioctl(3)binder_mmap(4)binder_open(5)binder_flush(6)binder_release.其中使用最多的binder_ioctl,binder_mmap
和binder_open。而一般文件操作需要用到的read()和write()則沒有出現,這是因爲它們的功能完全可以用ioctl()和mmap()來代替;而且後兩者還更加靈活。
binder_open:上層進程在訪問Binder驅動時,首先就需要開/dev/binder節點,這個操作最終的實現是 在binder_open()中。在binder_open函數中,Binder驅動會爲用戶創建一個它自己的 binder_proc實體。即Binder驅動會在/proc系統目錄下生成各種管理信息(比如/proc/binder/proc,
/proc/binder/state,/proc/binder/stats等),binder_proc就是管理數據的記錄體(每個進程都有 獨立記錄),用戶對Binder設備的操作就是以binder_proc對象爲基礎的。
binder_mmap:該函數主要是對內存的管理,主要是對某塊物理內存的共享,以便實現兩個進程間的數據交互。
binder_ioctl:這是Binder接口函數中工作量最大的一個,它承擔了Binder驅動的大部分業務。Binder並不提供read() 和write()等常規文件操作,因爲binder_ioctl就可以完全替代它們。
binder_ioctl支持的命令:
1.BINDER_WRITE_READ:讀寫操作,可以用此命令向Binder讀取或寫入數據.
2.BINDER_SET_MAX_THREADS:設置支持的最大線程數,因爲客戶端可以併發向服務器端發送請求,如果Binder驅動發現當 前的線程數量已經超過設定值,就會告知Binder Server停止啓動新的線程。
3.BINDER_SET_CONTEXT_MGR:Service Manager專用,將自己設置爲”Binder大管家”。系統中只能有一個SM存在。
4.BINDER_THREAD_EXIT:通知Binder線程退出。每個線程在退出時都應該告知Binder驅動,這樣才能釋放相關資源,否則可能會造成內存泄露。
5.BINDER_VERSION:獲取Binder版本號。
Binder驅動並沒有脫離Linux的典型驅動模型,提供了多個文件操作接口。其中binder_ioctl實現了應用進程與Binder驅動之間的命令交互,可以說承載了Binder驅動中的大部分業務。
ServiceManager(Binder Server)
ServiceManager(以下簡稱SM)的功能可以類比爲互聯網中的”DNS”服務器,”IP地址”爲0.另外,和DNS本身也是服務器一樣,SM也是一個標準的Binder Server,Binder驅動中提供了專門爲SM服務的相關命令。
ServiceManager的啓動
ServiceManager是在init程序解析init.rc時啓動的,另外需要注意的是:一旦ServiceManager發生問題後重啓,其他系統服務zygote,media,surfaceflinger和drm 也會被重新加載。這個servicemanager是用c/c++編寫的,源碼路徑在工程的
/frameworks/native/cmds/servicemanager目錄中,如下所示:
要特別提醒的是,servicemanager所對應的c文件是service_manager.c和binder.c。源碼工程中還有其他諸如ServiceManager.cpp的文件存在,但並不屬於SM程序。
ServiceManager的構建
在Android系統中,Binder Server的另一個常見稱謂是”XXX Service”,如Media Service,ActivityService等。
我們看下SM啓動後都做了哪些工作,找到main函數.
//http://androidxref.com/5.1.0_r1/xref/frameworks/native/cmds/servicemanager/service_manager.c
int main(int argc, char **argv)
{
struct binder_state *bs;
bs = binder_open(128*1024);//打開Binder設備
if (!bs) {
ALOGE("failed to open binder driver\n");
return -1;
}
//將自己設置爲Binder“大管家”,整個Android系統只允許一個ServiceManager存在,因而如果後面還
//有人調用這個函數就會失敗
if (binder_become_context_manager(bs)) {
ALOGE("cannot become context manager (%s)\n", strerror(errno));
return -1;
}
.................................................//代碼省略
svcmgr_handle = BINDER_SERVICE_MANAGER;
binder_loop(bs, svcmgr_handler);//進入循環,等待客戶請求
return 0;
}
我們可以看到main函數裏主要做了以下幾件事:
1.打開Binder設備,做好初始化
2.將自己設置爲Binder大管家
3.進入主循環
下面我們再深入一些看一下這幾件事:
//http://androidxref.com/5.1.0_r1/xref/frameworks/native/cmds/servicemanager/binder.c
struct binder_state *binder_open(size_t mapsize)
{
struct binder_state *bs;//這個結構體記錄了SM中關於Binder的所有信息,如fd,map的大小等
struct binder_version vers;
bs = malloc(sizeof(*bs));
if (!bs) {
errno = ENOMEM;
return NULL;
}
bs->fd = open("/dev/binder", O_RDWR);//打開Binder驅動節點
if (bs->fd < 0) {
fprintf(stderr,"binder: cannot open device (%s)\n",
strerror(errno));
goto fail_open;
}
if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
(vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
fprintf(stderr, "binder: driver version differs from user space\n");
goto fail_open;
}
bs->mapsize = mapsize;//mapsize是SM自己設的,爲128*1024,即128k
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
if (bs->mapped == MAP_FAILED) {
fprintf(stderr,"binder: cannot map device (%s)\n",
strerror(errno));
goto fail_map;
}
return bs;
fail_map:
close(bs->fd);//關閉file
fail_open:
free(bs);
return NULL;
}
/*
關於open和mmap最終都是調用的驅動層的binder_open和binder_mmap。
根據上面代碼段中的參數設置可知:
1.由Binder驅動決定被映射到進程空間中的內存起始地址。
2.映射區塊大小爲128k
3.映射區只讀
4.映射區的改變是私有的,不需要保存文件。
5.從文件的起始地址開始映射。
*/
接着我們來看第二件事,即將servicemanager註冊成Binder機制的”大管家”.
//http://androidxref.com/5.1.0_r1/xref/frameworks/native/cmds/servicemanager/binder.c
int binder_become_context_manager(struct binder_state *bs)
{
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
/*正如前面驅動部分的命令描述所陳述的那樣,只要向Binder Driver發送BINDER_SET_CONTEXT_MGR的ioctl命令即可
。因爲servicemanager啓動得很早,能保證它是系統中的第一個向Binder驅動註冊成"管家"的程序*/
第三件事就是看看SM是如何處理請求的:
//http://androidxref.com/5.1.0_r1/xref/frameworks/native/cmds/servicemanager/binder.c
void binder_loop(struct binder_state *bs, binder_handler func)
{
int res;
struct binder_write_read bwr;//這是執行BINDER_WRITE_READ命令所需的數據格式
uint32_t readbuf[32];//一次讀取容量
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
readbuf[0] = BC_ENTER_LOOPER;//命令
binder_write(bs, readbuf, sizeof(uint32_t));
for (;;) {
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);//讀取消息
if (res < 0) {
ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
break;
}
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);//處理讀取到的消息
if (res == 0) {
ALOGE("binder_loop: unexpected reply?!\n");
break;
}
if (res < 0) {
ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
break;
}
}
}
/*由此可見,SM遵循以下幾個步驟:
1.從Binder驅動讀取消息:通過發送BINDER_WRITE_READ命令實現——z這個命令既可讀取也可寫入,具體
要看bwr.write_size和bwr.read_size。因爲這裏write_size的初始化值爲
0,而read_size爲sizeof(readbuf),所以Binder驅動只執行讀取操作。
2.處理消息:調用binder_parse來解析消息。
3.不斷循環,而且永遠不會主動退出(除非出現致命錯誤)
*/
在binder_parse的處理過程中,需要重點關注下BR_TRANSACTION。
BR_TRANSACTION
對BR_TRANSACTION命令的處理主要由func來完成,然後將結果返回給Binder驅動。因爲ServiceManager是爲了完成”Binder Server Name”(域名)和”Server Handle”(IP地址)間對應關係的查詢而存在的,所以可推測出它提供的服務應該至少包括以下幾種:(1)註冊:當一個Binder Server創建後,它們要將自己的名稱,Binder句柄對應的關係告知SM進行備案。(2)查詢:即應用程序可以向SM發起查詢請求,以獲知某個Binder Server所對應的句柄(3)其他信息查詢:比如SM版本號,當前的狀態等。當然,這一部分不是必需的,可以不實現。
注意在這裏func函數指向svcmgr_handler函數。所以我們這裏需要來看一下svcmgr_handler的實現:
//http://androidxref.com/5.1.0_r1/xref/frameworks/native/cmds/servicemanager/service_manager.c
int svcmgr_handler(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply)
{
struct svcinfo *si;
uint16_t *s;
size_t len;
uint32_t handle;
uint32_t strict_policy;
int allow_isolated;
//ALOGI("target=%x code=%d pid=%d uid=%d\n",
// txn->target.handle, txn->code, txn->sender_pid, txn->sender_euid);
if (txn->target.handle != svcmgr_handle)
return -1;
if (txn->code == PING_TRANSACTION)
return 0;
// Equivalent to Parcel::enforceInterface(), reading the RPC
// header with the strict mode policy mask and the interface name.
// Note that we ignore the strict_policy and don't propagate it
// further (since we do no outbound RPCs anyway).
strict_policy = bio_get_uint32(msg);
s = bio_get_string16(msg, &len);//bio_XX系列函數爲取出各種類型的數據提供了便利。
if (s == NULL) {
return -1;
}
if ((len != (sizeof(svcmgr_id) / 2)) ||
memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {
fprintf(stderr,"invalid id %s\n", str8(s, len));
return -1;
}
if (sehandle && selinux_status_updated() > 0) {
struct selabel_handle *tmp_sehandle = selinux_android_service_context_handle();
if (tmp_sehandle) {
selabel_close(sehandle);
sehandle = tmp_sehandle;
}
}
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE://根據server名稱查找它的handle值。
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
//do_find_service這個函數執行查找操作。SM中維護有一個全局的svclist變量,用於保存所有server的註冊信息。
handle = do_find_service(bs, s, len, txn->sender_euid, txn->sender_pid);
if (!handle)
break;
bio_put_ref(reply, handle);//保存查詢結果,以返回給客戶端
return 0;
case SVC_MGR_ADD_SERVICE://用於註冊一個Binder Server
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
handle = bio_get_ref(msg);
allow_isolated = bio_get_uint32(msg) ? 1 : 0;
//這個函數具體執行添加操作
if (do_add_service(bs, s, len, handle, txn->sender_euid,
allow_isolated, txn->sender_pid))
return -1;
break;
case SVC_MGR_LIST_SERVICES: {//獲取列表中對應Server
uint32_t n = bio_get_uint32(msg);
if (!svc_can_list(txn->sender_pid)) {
ALOGE("list_service() uid=%d - PERMISSION DENIED\n",
txn->sender_euid);
return -1;
}
si = svclist;//所有的Server信息都保存於此。
while ((n-- > 0) && si)
si = si->next;
if (si) {
bio_put_string16(reply, si->name);
return 0;
}
return -1;
}
default:
ALOGE("unknown code %d\n", txn->code);
return -1;
}
bio_put_uint32(reply, 0);
return 0;
}
ServiceManager的功能架構比較簡潔——其內部維護着一個svclist列表,用於存儲所有Server相關信息(以svcinfo爲數據結構),查詢和註冊都是基於這個表展開的。函數svcmgr_handler處理完成後,binder_parse會進一步通過binder_send_reply來將執行結果回覆給底層Binder驅動,進而傳遞給客戶端。然後binder_parse會進入下一輪的while循環,知道ptr < end爲False——此時說明Service Manager上一次從驅動層讀取的消息都已處理完成,因而它還會繼續向Binder Driver發送BINDER_WRITE_READ以查詢有沒有消息。如果有的話就處理,否則會進入休眠等待。
我們來回憶一下順序:init—>init.rc—>ServiceManager start->service_manager.c/main->binder.c/binder_open
->binder.c/binder_become_context_manager->binder.c/binder_loop->binder.c/binder_parse.
獲取ServiceManager服務
所有Binder Client或者Binder Server都是圍繞Binder驅動展開的。每個進程只允許打開一次Binder設備,且只做一次內存映射,所有需要使用Binder驅動的線程共享這一資源。與Binder驅動進行實際命令通信的是IPCThreadState.
ServiceManagerProxy
在Java層ServiceManagerProxy的封裝類是ServiceManager.java.該類的部分代碼如下:
//http://androidxref.com/5.1.0_r1/xref/frameworks/base/core/java/android/os/ServiceManager.java
public final class ServiceManager {
private static final String TAG = "ServiceManager";
private static IServiceManager sServiceManager;
//用於記錄getService的歷史查詢結果,以加快查詢速度。
private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
// Find the service manager
sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
return sServiceManager;
}
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return getIServiceManager().getService(name);
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
public static void addService(String name, IBinder service) {
try {
getIServiceManager().addService(name, service, false);
} catch (RemoteException e) {
Log.e(TAG, "error in addService", e);
}
}
/*
*/
.........................//代碼省略
}
通過上面的這部分代碼我們可以發現,getService與addService的最終實現依賴IServiceManager的 某個實現類,我們看到sServiceManager的創建是依賴於 ServiceManagerNative.asInterface這方法.
下面我們繼續查看這個函數的代碼
//http://androidxref.com/5.1.0_r1/xref/frameworks/base/core/java/android/os/ServiceManagerNative.java
// Cast a Binder object into a service manager interface, generating a proxy if needed.
static public IServiceManager asInterface(IBinder obj)
{
if (obj == null) {
return null;
}
IServiceManager in =
(IServiceManager)obj.queryLocalInterface(descriptor);//查詢本地是否已經有IServiceManager存在
if (in != null) {
return in;
}
//如果沒有查詢到,則新建一個ServiceManagerProxy.
return new ServiceManagerProxy(obj);
}
哈哈,看SM的代理ServiceManagerProxy終於出現了,可想而知ServiceManagerProxy必定是要與Binder驅動通信的,因而它的構造函數中傳入了IBinder對象。我們看一下這個代理類的代碼:
//http://androidxref.com/5.1.0_r1/xref/frameworks/base/core/java/android/os/ServiceManagerNative.java
class ServiceManagerProxy implements IServiceManager {
public ServiceManagerProxy(IBinder remote) {
mRemote = remote;
}
public IBinder asBinder() {
return mRemote;
}
public IBinder getService(String name) throws RemoteException {
//準備打包數據
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();//保存通訊結果
data.writeInterfaceToken(IServiceManager.descriptor);
data.writeString(name)
//打包數據結束
//利用IBinder的transact將請求發送出去,而不用理會Binder驅動的open,mmap以及一大堆具體
//的Binder協議中的命令。所以這個IBinder一定會在內部使用ProcessState和IPCThreadState來
//與Binder驅動進行通信,
mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
IBinder binder = reply.readStrongBinder();
reply.recycle();
data.recycle();
return binder;
}
public IBinder checkService(String name) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IServiceManager.descriptor);
data.writeString(name);
mRemote.transact(CHECK_SERVICE_TRANSACTION, data, reply, 0);
IBinder binder = reply.readStrongBinder();
reply.recycle();
data.recycle();
return binder;
}
public void addService(String name, IBinder service, boolean allowIsolated)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IServiceManager.descriptor);
data.writeString(name);
data.writeStrongBinder(service);
data.writeInt(allowIsolated ? 1 : 0);
mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);
reply.recycle();
data.recycle();
}
..........................................................................//代碼省略
private IBinder mRemote;
/*
以上代碼中的getService,checkService,addService這些函數的函數體的重要區別點在於命令的不同。
*/
}
IBinder和BpBinder
在創建ServiceManagerProxy時,傳入了一個IBinder對象,然後藉助於它的transact方法,可以方便地與Binder驅動進行通信。
那麼我們的這個IBinder對象怎麼來的呢?
我們可以看到在ServiceManager類中有這麼一句ServiceManagerNative.asInterface(BinderInternal.getContextObject());然後再根據asInterface的代碼我們可以肯定的是,這個IBinder對象是由BinderInternal.getContextObject()產生的。
//http://androidxref.com/5.1.0_r1/xref/frameworks/base/core/java/com/android/internal/os/BinderInternal.java
/**
* Return the global "context object" of the system. This is usually
* an implementation of IServiceManager, which you can use to find
* other services.
*/
public static final native IBinder getContextObject();//這個方法是native,所以我們得找到對應的jni實現
//http://androidxref.com/5.1.0_r1/xref/frameworks/base/core/jni/android_util_Binder.cpp
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
sp<IBinder> b = ProcessState::self()->getContextObject(NULL);//這裏我們引出了ProcessState
return javaObjectForIBinder(env, b);//將native層創建的對象轉換爲Java層的IBinder對象。即BpBinder->BinderProxy
/*
這裏有人可能會有疑問爲什麼取名爲ContextObject呢?
Context即"背景,上下文",這裏可以理解爲運行時態的上下文。換句話說,是因爲IPC和整個Process或者
Thread都是有緊密關聯的。更進一步,基本上每個Process都需要IPC通信。既然如此,IPC就可以作爲進程
的基礎配置存在。作爲IPC中的基礎元素之一,Binder就是這個Context中的重要一環,因爲稱爲ContexObject。
*/
}
IBinder只是一個接口類,顯然還會有具體的實現類繼承於它。在Native層,這就是BpBinder(BpBinder.cpp);而在Java層,則是Binder.java中的BinderProxy。事實上,ProcessState::self()->getContextObject(NULL)返回的就是一個BpBinder對象。BinderProxy和BpBinder分別繼承自Java和Native層的IBinder接口。其中BpBinder是由ProcessState創建的,而BinderProxy是由javaObjectForIBinder()函數通過JNI的NewObject()創建的。
ProcessState和IPCThreadState
實現ProcessState的關鍵點在於:
1.保證同一個進程中只有一個ProcessState實例存在,而且只有在ProcessState對象創建時纔打開Binder設備以及做內存映射。
2.向上層提供IPC服務
3.與IPCThreadState分工合作,各司其職。
我們來看一下ProcessState的代碼:
//http://androidxref.com/5.1.0_r1/xref/frameworks/native/libs/binder/ProcessState.cpp
//這個函數我們在之前的獲取ContextObject的時候用到過,訪問ProcessState需要使用ProcessStata::Self()。
//如果當前已經有實例(gProcess不爲空)存在,就直接返回這個對象;否則,新建一個ProcessState。
sp<ProcessState> ProcessState::self()
{
Mutex::Autolock _l(gProcessMutex);
if (gProcess != NULL) {
return gProcess;
}
gProcess = new ProcessState;//創建對象
return gProcess;
}
ProcessState::ProcessState()
: mDriverFD(open_driver())//注意這裏打開驅動
, mVMStart(MAP_FAILED)
, mManagesContexts(false)
, mBinderContextCheckFunc(NULL)
, mBinderContextUserData(NULL)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1)
{
if (mDriverFD >= 0) {//成功打開/dev/binder
// XXX Ideally, there should be a specific define for whether we
// have mmap (or whether we could possibly have the kernel module
// availabla).
#if !defined(HAVE_WIN32_IPC)
// 開始執行mmap,內存塊大小爲:BINDER_VM_SIZE ((1*1024*1024) - (4096 *2)),即接近1M的空間
mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
if (mVMStart == MAP_FAILED) {
// *sigh*
ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
close(mDriverFD);
mDriverFD = -1;
}
#else
mDriverFD = -1;
#endif
}
LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened. Terminating.");
}
void ProcessState::setContextObject(const sp<IBinder>& object)
{
setContextObject(object, String16("default"));
}
<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
return getStrongProxyForHandle(0);//傳入0,代表Service Manager
}
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp<IBinder> result;//需要返回的IBinder
AutoMutex _l(mLock);
/**查找一個Vector表mHandleToObject,這裏保存了這個進程已經建立的Binder相關信息。
如果沒有找到相應節點,會自動添加一個。所以返回值正常情況下爲非空。
mHandleToObject記錄所有與Binder對象相關的信息,而每個表項是一個handle_entry,包含了
如下數據:
struct handle_entry{IBinder * binder;RefBase::weakref_type * refs;};
其中的binder屬性實際上是一個BpBinder,而且程序只有在以下兩種情況下才會生成一個BpBinder:
1.在列表中沒有找到對應的BpBinder。
2.雖然從列表中查找到了對應的節點,但是沒有辦法順利對這個BpBinder增加weakreference.
**/
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;
if (b == NULL || !e->refs->attemptIncWeak(this)) {
if (handle == 0) {
Parcel data;
status_t status = IPCThreadState::self()->transact(
0, IBinder::PING_TRANSACTION, data, NULL, 0);
if (status == DEAD_OBJECT)
return NULL;
}
b = new BpBinder(handle);//BpBinder出現了。
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;
}
ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle)
{
const size_t N=mHandleToObject.size();
if (N <= (size_t)handle) {
handle_entry e;
e.binder = NULL;
e.refs = NULL;
status_t err = mHandleToObject.insertAt(e, N, handle+1-N);
if (err < NO_ERROR) return NULL;
}
return &mHandleToObject.editItemAt(handle);
}
static int open_driver()
{
int fd = open("/dev/binder", O_RDWR);
if (fd >= 0) {
fcntl(fd, F_SETFD, FD_CLOEXEC);
int vers = 0;
status_t result = ioctl(fd, BINDER_VERSION, &vers);
if (result == -1) {
ALOGE("Binder ioctl to obtain version failed: %s", strerror(errno));
close(fd);
fd = -1;
}
if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
ALOGE("Binder driver protocol does not match user space protocol!");
close(fd);
fd = -1;
}
size_t maxThreads = 15;
result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
if (result == -1) {
ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
}
} else {
ALOGW("Opening '/dev/binder' failed: %s\n", strerror(errno));
}
return fd;
}
...........................................................//代碼省略
上面的代碼註釋中講到了什麼情況下才會生成一個BpBinder,那我們看一下BpBinder的構造函數
//http://androidxref.com/5.1.0_r1/xref/frameworks/native/libs/binder/BpBinder.cpp
BpBinder::BpBinder(int32_t handle)
: mHandle(handle)//如果是SM,handle爲0
, mAlive(1)
, mObitsSent(0)
, mObituaries(NULL)
{
ALOGV("Creating BpBinder %p handle %d\n", this, mHandle);
extendObjectLifetime(OBJECT_LIFETIME_WEAK);
IPCThreadState::self()->incWeakHandle(handle);//IPCThreadState出現了
}
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);//在ServiceManagerProxy中的mRemote.transact最終調用的是IPCThreadState:self()->transact
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}
我們前面說過IPCThreadState負責與Binder驅動進行具體的命令交互(ProcessState只是負責打開了Binder節點並做mmap),下面我們看看IPCThreadState的代碼:
//http://androidxref.com/5.1.0_r1/xref/frameworks/native/libs/binder/IPCThreadState.cpp
IPCThreadState* IPCThreadState::self()
{
/*當第一次被調用時,gHaveTLS爲false,因而不會進入第一個if判斷中,也不會創建IPCThreadState.但是隨後
它就會進入第二個if判斷並啓動TLS,然後返回restart處新建一個IPCThreadState。當以後再被調用時,gHaveTLS
爲true,如果本線程已經創建過IPCThreadState,那麼pthread_getspecific就不爲空;否則返回一個新建的IPCThreadState
*/
if (gHaveTLS) {//這個變量初始值是false
restart://當啓用TLS後,重新回到這
const pthread_key_t k = gTLS;
IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
if (st) return st;
return new IPCThreadState;
}
if (gShutdown) return NULL;
pthread_mutex_lock(&gTLSMutex);
if (!gHaveTLS) {
if (pthread_key_create(&gTLS, threadDestructor) != 0) {
pthread_mutex_unlock(&gTLSMutex);
return NULL;
}
gHaveTLS = true;//以後都是TLS了
}
pthread_mutex_unlock(&gTLSMutex);
goto restart;//返回restart接着執行
}
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()),//ProcessState整個進程只有一個
mMyThreadId(androidGetTid()),//當前的線程id
mStrictModePolicy(0),
mLastTransactionBinderFlags(0)
{
pthread_setspecific(gTLS, this);
clearCaller();
mIn.setDataCapacity(256);//mIn是一個Parcel,從名稱可以看出來,它是用於接收Binder發過來的數據的,最大容量爲256
mOut.setDataCapacity(256);//mOut也是一個Parcel,它是用於存儲要發送給Binder的命令數據的,最大容量爲256
}
void IPCThreadState::incWeakHandle(int32_t handle)
{
LOG_REMOTEREFS("IPCThreadState::incWeakHandle(%d)\n", handle);
mOut.writeInt32(BC_INCREFS);
mOut.writeInt32(handle);
}
void IPCThreadState::decWeakHandle(int32_t handle)
{
LOG_REMOTEREFS("IPCThreadState::decWeakHandle(%d)\n", handle);
mOut.writeInt32(BC_DECREFS);
mOut.writeInt32(handle);
}
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
status_t err = data.errorCheck();
flags |= TF_ACCEPT_FDS;
IF_LOG_TRANSACTIONS() {
TextOutput::Bundle _b(alog);
alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand "
<< handle << " / code " << TypeCode(code) << ": "
<< indent << data << dedent << endl;
}
if (err == NO_ERROR) {
LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
(flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
/*將數據打包成Binder驅動協議規定的格式,這個過程由writeTransactionData來完成(注意:
這個函數只是整理數據,並把結果存入mOut中。而在talkWithDriver()方法中才會將命令真正發
送給Binder驅動)*/
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 0
if (code == 4) { // relayout
ALOGI(">>>>>> CALLING transaction 4");
} else {
ALOGI(">>>>>> CALLING transaction %d", code);
}
#endif
if (reply) {
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
#if 0
if (code == 4) { // relayout
ALOGI("<<<<<< RETURNING transaction 4");
} else {
ALOGI("<<<<<< RETURNING transaction %d", code);
}
#endif
IF_LOG_TRANSACTIONS() {
TextOutput::Bundle _b(alog);
alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand "
<< handle << ": ";
if (reply) alog << indent << *reply << dedent << endl;
else alog << "(none requested)" << endl;
}
} else {
err = waitForResponse(NULL, NULL);
}
return err;
}
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
int32_t cmd;
int32_t err;
while (1) {
if ((err=talkWithDriver()) < NO_ERROR) break;//將已有數據進行必要的包裝,然後發送給Binder驅動
err = mIn.errorCheck();//錯誤檢查
if (err < NO_ERROR) break;//數據有錯誤
if (mIn.dataAvail() == 0) continue;//如果沒有可用數據,進入下一輪循環
cmd = mIn.readInt32();//讀取cmd
IF_LOG_COMMANDS() {
alog << "Processing waitForResponse Command: "
<< getReturnString(cmd) << endl;
}
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
if (!reply && !acquireResult) goto finish;
break;
case BR_DEAD_REPLY:
err = DEAD_OBJECT;
goto finish;
case BR_FAILED_REPLY:
err = FAILED_TRANSACTION;
goto finish;
case BR_ACQUIRE_RESULT:
{
ALOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT");
const int32_t result = mIn.readInt32();
if (!acquireResult) continue;
*acquireResult = result ? NO_ERROR : INVALID_OPERATION;
}
goto finish;
case BR_REPLY:
{
binder_transaction_data tr;
err = mIn.read(&tr, sizeof(tr));
ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
if (err != NO_ERROR) goto finish;
if (reply) {
if ((tr.flags & TF_STATUS_CODE) == 0) {
reply->ipcSetDataReference(
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(binder_size_t),
freeBuffer, this);
} else {
err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);
freeBuffer(NULL,
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(binder_size_t), this);
}
} else {
freeBuffer(NULL,
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(binder_size_t), this);
continue;
}
}
goto finish;
default:
err = executeCommand(cmd);
if (err != NO_ERROR) goto finish;
break;
}
}
finish:
if (err != NO_ERROR) {
if (acquireResult) *acquireResult = err;
if (reply) reply->setError(err);
mLastError = err;
}
return err;
}
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
if (mProcess->mDriverFD <= 0) {//Binder驅動設備還沒打開
return -EBADF;
}
binder_write_read bwr;//讀寫都使用這個數據結構
// Is the read buffer empty?
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
// We don't want to write anything if we are still reading
// from data left in the input buffer and the caller
// has requested to read the next data.
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
bwr.write_size = outAvail;
bwr.write_buffer = (uintptr_t)mOut.data();
// This is what we'll read.
if (doReceive && needRead) {
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (uintptr_t)mIn.data();
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
}
IF_LOG_COMMANDS() {
TextOutput::Bundle _b(alog);
if (outAvail != 0) {
alog << "Sending commands to driver: " << indent;
const void* cmds = (const void*)bwr.write_buffer;
const void* end = ((const uint8_t*)cmds)+bwr.write_size;
alog << HexDump(cmds, bwr.write_size) << endl;
while (cmds < end) cmds = printCommand(alog, cmds);
alog << dedent;
}
alog << "Size of receive buffer: " << bwr.read_size
<< ", needRead: " << needRead << ", doReceive: " << doReceive << endl;
}
// Return immediately if there is nothing to do.
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
bwr.write_consumed = 0;//>0說明Binder驅動消耗了mOut中的數據
bwr.read_consumed = 0;//>0說明Binder驅動已經成功幫我們讀取到了數據,並寫入了mIn.data().
status_t err;
do {
IF_LOG_COMMANDS() {
alog << "About to read/write, write size = " << mOut.dataSize() << endl;
}
#if defined(HAVE_ANDROID_OS)
/*執行完ioctl後,通過bwr.write_consumed和bwr.read_consumed可以知道Binder驅動對
我們請求的BINDER_WRITE_READ命令的處理情況,然後對mIn和mOut做善後處理*/
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
#else
err = INVALID_OPERATION;
#endif
if (mProcess->mDriverFD <= 0) {
err = -EBADF;
}
IF_LOG_COMMANDS() {
alog << "Finished read/write, write size = " << mOut.dataSize() << endl;
}
} while (err == -EINTR);
IF_LOG_COMMANDS() {
alog << "Our err: " << (void*)(intptr_t)err << ", write consumed: "
<< bwr.write_consumed << " (of " << mOut.dataSize()
<< "), read consumed: " << bwr.read_consumed << endl;
}
if (err >= NO_ERROR) {
if (bwr.write_consumed > 0) {
if (bwr.write_consumed < mOut.dataSize())
mOut.remove(0, bwr.write_consumed);
else
mOut.setDataSize(0);
}
if (bwr.read_consumed > 0) {
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
IF_LOG_COMMANDS() {
TextOutput::Bundle _b(alog);
alog << "Remaining data size: " << mOut.dataSize() << endl;
alog << "Received commands from driver: " << indent;
const void* cmds = mIn.data();
const void* end = mIn.data() + mIn.dataSize();
alog << HexDump(cmds, mIn.dataSize()) << endl;
while (cmds < end) cmds = printReturnCommand(alog, cmds);
alog << dedent;
}
return NO_ERROR;
}
return err;
}
注意不管是讀取還是寫入,Binder驅動只是發揮中間人的作用,真正處理請求的還是Binder Client和Binder Server通信雙方。
下面我們簡單的來總結一下:
1.ServiceManagerProxy
當某個Binder Server在啓動時,會把自己的名稱name與對應的Binder句柄值保存在ServiceManager中。調用者通常只知道Binder Server的名稱,所以必須先向Service Manager發起查詢請求,就是getService(name)。任何Binder Client都可以直接通過
0這個Binder句柄創建一個BpBinder,再通過Binder驅動去獲取Service Manager的服務,具體而言,就是調用BinderInternal的 getContextObject()來獲得Service Manager的BpBinder.Android系統同時支持Java與C/C++層的Binder機制,因而很多對象 都必須有”雙重身份”,如BpBinder在Java層以IBinder來表示。對於Service Manager而言,IBinder的真正持有者與使用者是ServiceManagerProxy.
2.ProcessState和IPCThreadState
大多數程序都有IPC的需要,而進程間通信本身又是非常煩瑣的,因而Android系統特別爲程序進程使用Binder機制封裝了兩個實現類,即ProcessState和IPCThreadState。從名稱上可以看出,前者是進程相關的,而後者是線程相關的。ProcessState負責打開Binder驅動設備,進行mmap()等準備工作,而如何與Binder驅動進行具體的命令通信則由IPCThreadState來完成。在getService()這個場景中,調用者是從Java層的IBinder.transact()開始,層層往下調用到IPCThreadState.transact(),然後通過waitForResponse進入主循環,直至收到Service Manager的回覆後才跳出循環,並將結果再次層層回傳到應用層。
注意:真正與Binder驅動打交道的地方是talkWithDriver中的ioctl(),整個流程中多次調用了這個函數。
3.Binder驅動
Binder驅動通過巧妙的機制來使數據的傳遞更加高效,即只需要一次複製就可以把數據從一個進程複製到另一個進程。Binder中還保存着大量的全局以及進程相關的變量,用於管理每個進程/線程的狀態,內存申請和待辦事項等一系列複雜的數據信息。正是這些變量的有效協作,才使得整個Binder通信真正”動”了起來。
4.Service Manager的實現
ServiceManager在Android系統啓動之後就運行起來了,並通過BINDER_SET_CONTEXT_MGR把自己註冊成Binder”“大管家”.它在做完一系列初始
化後,在最後一次ioctl的read操作中會進入等待,直到有Binder Client發起服務請求而被Binder驅動喚醒。Service Manager喚醒後,程序分爲兩條主線索。其一,Service Manager端將接着執行read操作,把調用者的具體請求讀取出來,然後利用binder_parse解析,再根據實際情況填寫transaction信息,最後把結果BR_REPLY命令(也是ioctl)返回Binder驅動。其二,發起getService請求的Binder Client在等待Service Manager回覆的過程中會進入休眠,直到被Binder驅動再次喚醒——它和Service Manager一樣也是在read中睡眠的,因而醒來後繼續執行讀取操作。這一次得到的就是Service Manager對請求的執行結果。程序先把結果填充到reply這個Parcel中,然後通過層層返回到ServiceManagerProxy,再利用Parcel.readStrongBinder生成一個BpBinder,最終經過類型轉換爲IBinder對象後傳給調用者。
Binder客戶端——Binder Client
Binder的最大”“消費者”是Java層的應用程序。關於Binder Client,我們只需要知道的是Binder的Client端和Server端需要繼承同樣的公共接口類。Binder Client提供了與Binder Sever一樣的函數原型,使用戶感覺不出Server是運行在本地還是遠端。
Android接口描述語言——AIDL
AIDL是Android Interface Description Language 的簡寫。從名稱上看它是一種語言,而且是專門用於描述接口的語言。準確地說,它是用於定義客戶端/服務端通信接口的一種描述語言。
這裏我們先看一下構建一個Binder Server所需的工作是怎樣的,主要考慮的因素如下:
1.啓動的時機:主要考慮什麼時候啓動,比如SM是開機的時候通過init.rc文件啓動的,這就保證了它是系統中第一個註冊成”“服務大管家”的Service。
2.提供一致的服務接口:顯然,一個Binder Server應該向公衆暴露它所能提供的服務;而且客戶端使用的服務接口和服務端實現的服務接口必須是完全 一致的。注意這裏有個不一樣的地方,SM中的服務接口是IServiceManager。客戶端本地進程中的ServiceManagerProxy及 ServiceManagerNative都繼承自這個接口。而它的服務端是直接用C語言實現的(Service_mananger.c)。不過通過分析其內部 binder_parse函數可知,它還是保持了與客戶端接口的一致。
3.與Binder驅動的交互方式:一個Binder Server需要與Binder Driver做哪些交互呢?除去一系列必要的初始化以外(open,mmap等),就是要通過不斷地ioctl來循環讀寫數據。比如SM就是通過binder_loop函數中的一個for死循環來完成這一工作的。
4.外界如何能訪問到這個Server的服務:訪問Server主要有兩種方法:
(1)Server在ServiceManager中註冊:這種方法普遍存在於Android系統服務中,如ActivityManagerService,WindowManagerService等都在ServiceManager中做了註冊。調用者只要通過ServiceManager.getService(SERVICE_NAME)就可以獲取到Binder Server的本地代理,然後與之通信
(2)通過其他Server作爲中介:這是一種”“匿名”的方法。換句話說,這種類型的Binder Server不需要在ServiceManager中註冊,通過一個”“第三方”的”實名”Sever——因爲是實名的,調用者可以通過ServiceManager來首先訪問到
它,然後由它提供的接口獲取”匿名”者的Binder句柄。
關於匿名Binder Server與實名Server的差異主要就在於後者是通過ServiceManager來獲得對它的引用,而前者則是以其他實名Server爲中介來傳遞這一引用信息。
注:關於Android系統的深入知識也是之前一年的主要學習內容,由於沒有去記錄,導致了看過的許多東西都忘記了,感覺就是沒有學習一樣,最近的一段時間會陸續的整理出一些東西,Android系統的深入知識,主要參考資料爲博客與《深入理解Android內核設計思想》等,當然還有源碼。另:需要說明一點的是,文章標題以說明爲筆記,所以大部分內容爲摘抄,主要來源書籍。
轉載請註明出處:http://blog.csdn.net/android_jiangjun/article/details/76691860