Binder是一個相似於C/S架構的通訊框架,有時候客戶端可能想知道服務端的狀態,好比服務端若是掛了,客戶端但願能及時的被通知到,而不是等到再起請求服務端的時候才知道,這種場景其實在互爲C/S的時候最經常使用,好比AMS與APP,當APP端進程異常退出的時候,AMS但願能及時知道,不單單是清理APP端在AMS中的一些信息,好比ActivityRecord,ServiceRecord等,有時候可能還須要及時恢復一些自啓動的Service。Binder實現了一套」死亡訃告」的功能,即:服務端掛了,或者正常退出,Binder驅動會向客戶端發送一份訃告,告訴客戶端Binder服務掛了。javascript
這個「訃告」到底是如何實現的呢?其做用又是什麼呢?對於Android而言,Binder「訃告」有點採用了相似觀察者模式,所以,首先須要將Observer註冊到目標對象中,其實就是將Client註冊到Binder驅動,未來Binder服務掛掉時候,就能經過驅動去發送。Binder「訃告」發送的入口只有一個:在釋放binder設備的時候,在在操做系統中,不管進程是正常退出仍是異常退出,進程所申請的全部資源都會被回收,包括打開的一些設備文件,如Binder字符設備等。在釋放的時候,就會調用相應的release函數,「訃告」也就是在這個時候去發送的。所以Binder訃告其實就僅僅包括兩部分:註冊與通知。java
這裏拿bindService爲例子進行分析,其餘場景相似,bindService會首先請求AMS去啓動Service,Server端進程在啓動時,會調用函數open來打開設備文件/dev/binder,同時將Binder服務實體回傳給AMS,AMS再將Binder實體的引用句柄經過Binder通訊傳遞給Client,也就是在AMS回傳給Client的時候,會向Binder驅動註冊。其實這也比較好理解,得到了服務端的代理,就應該關心服務端的死活 。當AMS利用IServiceConnection這條binder通訊線路爲Client回傳Binder服務實體的時候,InnerConnection就會間接的將死亡回調註冊到內核:node
private static class InnerConnection extends IServiceConnection.Stub {
final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;
public void connected(ComponentName name, IBinder service) throws RemoteException {
LoadedApk.ServiceDispatcher sd = mDispatcher.get();
if (sd != null) {
sd.connected(name, service);
}
}
}複製代碼
ServiceDispatcher函數進一步調用 doConnectedcookie
public void doConnected(ComponentName name, IBinder service) {
ServiceDispatcher.ConnectionInfo old;
ServiceDispatcher.ConnectionInfo info;
synchronized (this) {
if (service != null) {
mDied = false;
info = new ConnectionInfo();
info.binder = service;
info.deathMonitor = new DeathMonitor(name, service);
try {
<!-- 關鍵點點1-->
service.linkToDeath(info.deathMonitor, 0);
}
}複製代碼
看關鍵點點1 ,這裏的IBinder service實際上是AMS回傳的服務代理BinderProxy,linkToDeath是一個Native函數,會進一步調用BpBinde的linkToDeath:架構
status_t BpBinder::linkToDeath(
const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags){
<!--關鍵點1-->
IPCThreadState* self = IPCThreadState::self();
self->requestDeathNotification(mHandle, this);
self->flushCommands();
}複製代碼
最終調用IPCThreadState的requestDeathNotification(mHandle, this)向內核發送BC_REQUEST_DEATH_NOTIFICATION請求:框架
status_t IPCThreadState::requestDeathNotification(int32_t handle, BpBinder* proxy)
{
mOut.writeInt32(BC_REQUEST_DEATH_NOTIFICATION);
mOut.writeInt32((int32_t)handle);
mOut.writeInt32((int32_t)proxy);
return NO_ERROR;
}複製代碼
最後來看一下在內核中,是怎麼登記註冊的:函數
int
binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
void __user *buffer, int size, signed long *consumed)
{
...
case BC_REQUEST_DEATH_NOTIFICATION:
case BC_CLEAR_DEATH_NOTIFICATION: {
...
ref = binder_get_ref(proc, target);
if (cmd == BC_REQUEST_DEATH_NOTIFICATION) {
...關鍵點1
death = kzalloc(sizeof(*death), GFP_KERNEL);
binder_stats.obj_created[BINDER_STAT_DEATH]++;
INIT_LIST_HEAD(&death->work.entry);
death->cookie = cookie;
ref->death = death;
if (ref->node->proc == NULL) {
ref->death->work.type = BINDER_WORK_DEAD_BINDER;
if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {
list_add_tail(&ref->death->work.entry, &thread->todo);
} else {
list_add_tail(&ref->death->work.entry, &proc->todo);
wake_up_interruptible(&proc->wait);
}
}
}
}複製代碼
看關鍵點1 ,其實就是爲Client新建binder_ref_death對象,並賦值給binder_ref。在binder驅動中,binder_node節點會記錄全部binder_ref,當binder_node所在的進程掛掉後,驅動就能根據這個全局binder_ref列表找到全部Client的binder_ref,並對於設置了死亡回調的Client發送「訃告」,這是由於在binder_get_ref_for_node向Client插入binder_ref的時候,也會插入binder_node的binder_ref列表。oop
static struct binder_ref *
binder_get_ref_for_node(struct binder_proc *proc, struct binder_node *node)
{
struct rb_node *n;
struct rb_node **p = &proc->refs_by_node.rb_node;
struct rb_node *parent = NULL;
struct binder_ref *ref, *new_ref;
if (node) {
hlist_add_head(&new_ref->node_entry, &node->refs);
}
return new_ref;
}複製代碼
如此,死亡回調入口就被註冊到binder內核驅動,以後,等到進程結束要釋放binder的時候,就會觸發死亡回調。post
在調用binder_realease函數來釋放相應資源的時候,最終會調用binder_deferred_release函數。該函數會遍歷該binder_proc內全部的binder_node節點,並向註冊了死亡回調的Client發送訃告,ui
static void binder_deferred_release(struct binder_proc *proc)
{ ....
if (ref->death) {
death++;
if (list_empty(&ref->death->work.entry)) {
ref->death->work.type = BINDER_WORK_DEAD_BINDER;
list_add_tail(&ref->death->work.entry, &ref->proc->todo);
// 插入到binder_ref請求進程的binder線程等待隊列????? 自然支持binder通訊嗎?
// 何時,須要死亡回調,本身也是binder服務?
wake_up_interruptible(&ref->proc->wait);
}
...
}複製代碼
死亡訃告被直接發送到Client端的binder進程todo隊列上,這裏彷佛也只對於互爲C/S通訊的場景有用,當Client的binder線程被喚醒後,就會針對「訃告」作一些清理及善後工做:
static int
binder_thread_read(struct binder_proc *proc, struct binder_thread *thread,
void __user *buffer, int size, signed long *consumed, int non_block)
{
case BINDER_WORK_DEAD_BINDER:
case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {
struct binder_ref_death *death = container_of(w, struct binder_ref_death, work);
uint32_t cmd;
if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION)
cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE;
else
cmd = BR_DEAD_BINDER;
...
}複製代碼
這裏會向用戶空間寫入一個BR_DEAD_BINDER命令,並返回talkWithDriver函數,返回後,IPCThreadState會繼續執行executeCommand,
status_t IPCThreadState::executeCommand(int32_t cmd)
{
// 死亡訃告
case BR_DEAD_BINDER:
{
BpBinder *proxy = (BpBinder*)mIn.readInt32();
<!--關鍵點1 --> proxy->sendObituary(); mOut.writeInt32(BC_DEAD_BINDER_DONE); mOut.writeInt32((int32_t)proxy); } break;複製代碼
}
看關鍵點1,Obituary直譯過來就是訃告,其實就是利用BpBinder發送訃告,待訃告處理結束後,再向Binder驅動發送確認通知。
void BpBinder::sendObituary()
{
ALOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n",
this, mHandle, mObitsSent ? "true" : "false");
mAlive = 0;
if (mObitsSent) return;
mLock.lock();
Vector<Obituary>* obits = mObituaries;
if(obits != NULL) {
<!--關鍵點1-->
IPCThreadState* self = IPCThreadState::self();
self->clearDeathNotification(mHandle, this);
self->flushCommands();
mObituaries = NULL;
}
mObitsSent = 1;
mLock.unlock();
if (obits != NULL) {
const size_t N = obits->size();
for (size_t i=0; i<N; i++) {
reportOneDeath(obits->itemAt(i));
}
delete obits;
}
}複製代碼
看關鍵點1,這裏跟註冊相對應,將本身從觀察者列表中清除,以後再上報
void BpBinder::reportOneDeath(const Obituary& obit)
{
sp<DeathRecipient> recipient = obit.recipient.promote();
ALOGV("Reporting death to recipient: %p\n", recipient.get());
if (recipient == NULL) return;
recipient->binderDied(this);
}複製代碼
進而調用上層DeathRecipient的回調,作一些清理之類的邏輯。以AMS爲例,其binderDied函數就挺複雜,包括了一些數據的清理,甚至還有進程的重建等,不作討論。
Android 後臺殺死系列之一:FragmentActivity 及 PhoneWindow 後臺殺死處理機制
Android後臺殺死系列之二:ActivityManagerService與App現場恢復機制 Android後臺殺死系列之三:後臺殺死原理LowMemoryKiller(4.3-6.0)
Android後臺殺死系列之四:Binder訃告原理
Android後臺殺死系列之五:Android進程保活-自「裁」或者耍流氓