Service Manager的用處
-
在正式展開敘述之前,我們需要先說明一下為什么要進行Service Manager的啟動和獲取,回到binder-框架認知中的整體框架圖可知:

客戶端想要和服務端進行通信,首先需要和對應服務端建立連接,那么客戶端是怎么知道,并且完成和服務端進行連接的呢?這就需要用到Service Manager服務了,其實對應客戶端和服務端的連接通信都是通過Service Manager來建立的,之所以Service Manager可以實作相關需求,原因如下:
- Service Manager的Binder的handle,固定為0;而這就保證了:不論是Client端還是Service端都可以準確的獲取到Service Manager
- Service Manager中記錄了需要通信的server,這樣就可以通過Service Manager去獲取對應的需要通信的server
Service Manager的啟動
Service Manager啟動相關圖示
-
Service Manager啟動方法呼叫圖

-
Service Manager啟動時序圖

Service Manager啟動代碼決議
- 需要說明以下分析都是基于Android P版本進行分析
1. 啟動Service Manager行程
-
ServiceManager是由init行程通過決議init.rc檔案而創建的,其所對應的可執行程式是ServiceManager,所對應的源檔案是service_manager.c,行程名是servicemanager
檔案路徑:android/system/core/rootdir/init.rc
# Start essential services. # 啟動servicemanager.rc start servicemanager start hwservicemanager start vndservicemanager檔案路徑:android/frameworks/native/cmds/servicemanager.rc
# 啟動service Manager行程 service servicemanager /system/bin/servicemanager class core animation user system group system readproc critical onrestart restart healthd onrestart restart zygote onrestart restart audioserver onrestart restart media onrestart restart surfaceflinger onrestart restart inputflinger onrestart restart drm onrestart restart cameraserver onrestart restart keystore onrestart restart gatekeeperd writepid /dev/cpuset/system-background/tasks shutdown critical
2. 進入 service_manager.c 中的 main()方法
-
啟動servicemanager的入口就是
service_manager.c中的main()方法:int main(int argc, char** argv) { struct binder_state *bs; union selinux_callback cb; char *driver; if (argc > 1) { driver = argv[1]; } else { driver = "/dev/binder"; } //獲取SM的binder驅動相關資訊,并在其中建立了記憶體映射,這里open的Binder驅動檔案是SM行程的,所以下面映射的128K的空間的SM的Binder的大小 bs = binder_open(driver, 128*1024); if (!bs) { #ifdef VENDORSERVICEMANAGER ALOGW("failed to open binder driver %s\n", driver); while (true) { sleep(UINT_MAX); } #else ALOGE("failed to open binder driver %s\n", driver); #endif return -1; } //將該binder注冊成ServiceManager大管家 if (binder_become_context_manager(bs)) { ALOGE("cannot become context manager (%s)\n", strerror(errno)); return -1; } cb.func_audit = audit_callback; selinux_set_callback(SELINUX_CB_AUDIT, cb); cb.func_log = selinux_log_callback; selinux_set_callback(SELINUX_CB_LOG, cb); #ifdef VENDORSERVICEMANAGER sehandle = selinux_android_vendor_service_context_handle(); #else sehandle = selinux_android_service_context_handle(); #endif selinux_status_open(true); if (sehandle == NULL) { ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n"); abort(); } if (getcon(&service_manager_context) != 0) { ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n"); abort(); } //開啟binder loop回圈 binder_loop(bs, svcmgr_handler); return 0; }在
main()方法中,關于SM的啟動,主要的動作是三個:binder_open(): 打開Binder設備檔案binder_become_context_manager(): 告知Binder驅動當前行程是Binder背景關系管理者binder_loop(): 進入訊息回圈,等待Client請求;如果沒有Client請求,那么就進入中斷等待狀態,如果有Client請求,就被喚醒,讀取并處理Client請求
接下來針對這三個方法進行詳細決議,
1. binder_open()方法決議
-
代碼路徑:android/framework/native/cmds/servicemanager/binder.c
//此處引數,driver = /dev/binder,mapsize = 128*1024 struct binder_state *binder_open(const char* driver, size_t mapsize) { struct binder_state *bs; struct binder_version vers; bs = malloc(sizeof(*bs)); if (!bs) { errno = ENOMEM; return NULL; } //打開dev/binder驅動,并回傳驅動檔案句柄,保存在bs->fd中 bs->fd = open(driver, O_RDWR | O_CLOEXEC); if (bs->fd < 0) { fprintf(stderr,"binder: cannot open %s (%s)\n", driver, strerror(errno)); goto fail_open; } //獲取binder驅動版本號 if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) || (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) { fprintf(stderr, "binder: kernel driver version (%d) differs from user space version (%d)\n", vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION); goto fail_open; } bs->mapsize = mapsize; //建立SM行程的binder記憶體映射,mapsize為128K,所以此處映射的空間就是128K,所以Service Manager行程的binder空間就只有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; } //回傳SM的binder相關資訊,包含binder驅動檔案的句柄、記憶體大小以及binder驅動首地址 return bs; fail_map: close(bs->fd); fail_open: free(bs); return NULL; }binder_open()方法的用處就是打開“/dev/binder”節點的Binder驅動設備檔案,然后呼叫mmap()方法進行記憶體映射 -
這里面的
open()、mmap()最后都是呼叫Binder驅動中的binder_open()、binder_mmap(),具體方法決議見binder框架決議(1),此處不多做說明,
2. binder_become_context_manager()方法決議
-
代碼路徑:android/framework/native/cmds/servicemanager/binder.c
int binder_become_context_manager(struct binder_state *bs) { //呼叫到binder驅動層的Binder_ioctl,傳入的引數是BINDER_SET_CONTEXT_MGR return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0); }ioctl()會呼叫到Binder驅動層的binder_ioctl()方法- 代碼路徑:android/kernel/drivers/staging/android/binder.c
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { ... switch (cmd) { ...... //傳入的引數是BINDER_SET_CONTEXT_MGR,命中此處case case BINDER_SET_CONTEXT_MGR: ret = binder_ioctl_set_ctx_mgr(filp); if (ret) goto err; break; ..... } ... }傳入的引數為:
BINDER_SET_CONTEXT_MGR,命中對應case后呼叫方法:binder_ioctl_set_ctx_mgr(filp)static int binder_ioctl_set_ctx_mgr(struct file *filp, struct flat_binder_object *fbo) { int ret = 0; //filp->private_data中記錄的就是對應行程的proc資訊 struct binder_proc *proc = filp->private_data; //拿到對應行程的context物件 struct binder_context *context = proc->context; struct binder_node *new_node; //拿到當前的uid kuid_t curr_euid = current_euid(); mutex_lock(&context->context_mgr_node_lock); //判斷是否已經存在binder_context_mgr_node,如果已經存在,不能再進行二次設定,直接退出,保證只存在一個SM if (context->binder_context_mgr_node) { pr_err("BINDER_SET_CONTEXT_MGR already set\n"); ret = -EBUSY; goto out; } //判斷proc->tsk的合法性,如果proc->tsk不合法直接退出 ret = security_binder_set_context_mgr(proc->tsk); if (ret < 0) goto out; //判斷binder_context_mgr_uid是否合法,這個也是在判斷是否已經進行過SM的設定了,確保只存在一個SM if (uid_valid(context->binder_context_mgr_uid)) { //如果binder_context_mgr_uid合法,即表示已經進行過SM的設定了,那么就uid和當前行程的uid是否相等 if (!uid_eq(context->binder_context_mgr_uid, curr_euid)) { pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n", from_kuid(&init_user_ns, curr_euid), from_kuid(&init_user_ns, context->binder_context_mgr_uid)); ret = -EPERM; goto out; } } else { //如果不存在uid,則將當前執行緒的uid設定進去 context->binder_context_mgr_uid = curr_euid; } //根據proc新建一個binder_node物件,fbo此時為null new_node = binder_new_node(proc, fbo); if (!new_node) { ret = -ENOMEM; goto out; } //加鎖,確保執行緒安全 binder_node_lock(new_node); //增加相關binder參考計數 new_node->local_weak_refs++; new_node->local_strong_refs++; new_node->has_strong_ref = 1; new_node->has_weak_ref = 1; //將新建的binder_node物件使用binder_context_mgr_node保存起來 context->binder_context_mgr_node = new_node; binder_node_unlock(new_node); binder_put_node(new_node); out: mutex_unlock(&context->context_mgr_node_lock); return ret; }縱觀
binder_ioctl_set_ctx_mgr()方法,其實它做的事情就是:在判斷binder_context_mgr_node為空的前提下,通過binder_new_node()方法新建了ServicerManager服務對應的Binder物體,并將其賦值binder_context_mgr_node- 其實每個需要進行IPC通信的server都會通過
binder_new_node()方法去新建一個Binder物體,而SM和其他服務的Binder物體所不同有兩點:binder_new_node(proc, fbo)SM的Binder物體在創建時傳入的引數是特殊的- SM的Binder物體是保存在
binder_context_mgr_node中的,而這就保證了其他的服務都可以通過context->binder_context_mgr_node去獲取SM的Binder物體
接下來我們看一下
binder_new_node()方法的代碼細節:static struct binder_node *binder_new_node(struct binder_proc *proc, struct flat_binder_object *fp) { struct binder_node *node; //創建一個新的binder_node物件 struct binder_node *new_node = kzalloc(sizeof(*node), GFP_KERNEL); if (!new_node) return NULL; binder_inner_proc_lock(proc); //傳入代表當前行程的proc資料、新創建的new_node、fp(在創建SM時,fp為null) node = binder_init_node_ilocked(proc, new_node, fp); binder_inner_proc_unlock(proc); if (node != new_node) /* * The node was already added by another thread */ kfree(new_node); return node; } static struct binder_node *binder_init_node_ilocked( struct binder_proc *proc, struct binder_node *new_node, struct flat_binder_object *fp) { struct rb_node **p = &proc->nodes.rb_node; struct rb_node *parent = NULL; struct binder_node *node; //通過傳入的fp引數是否為空,決定了ptr和cookie是否為0 binder_uintptr_t ptr = fp ? fp->binder : 0; binder_uintptr_t cookie = fp ? fp->cookie : 0; __u32 flags = fp ? fp->flags : 0; s8 priority; assert_spin_locked(&proc->inner_lock); //在proc->nodes.rb_node這顆紅黑樹中,查找是否存在匹配的binder物體(通過ptr成員來判斷) while (*p) { parent = *p; node = rb_entry(parent, struct binder_node, rb_node); if (ptr < node->ptr) p = &(*p)->rb_left; else if (ptr > node->ptr) p = &(*p)->rb_right; else { /* * A matching node is already in * the rb tree. Abandon the init * and return it. */ binder_inc_node_tmpref_ilocked(node); return node; } } //如果在紅黑樹中沒有找到對應相同ptr的binder_node物件,那么就將新創建的new_node賦值給node node = new_node; binder_stats_created(BINDER_STAT_NODE); node->tmp_refs++; //將新創建的node放入node->rb_node中 rb_link_node(&node->rb_node, parent, p); rb_insert_color(&node->rb_node, &proc->nodes); node->debug_id = atomic_inc_return(&binder_last_id); //將當前行程的proc資訊保存到node->proc中 node->proc = proc; //保存下當前行程的ptr、cookie值,在SM中,ptr和cookie都為0,這是SM特殊的地方! node->ptr = ptr; node->cookie = cookie; node->work.type = BINDER_WORK_NODE; priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK; node->sched_policy = (flags & FLAT_BINDER_FLAG_SCHED_POLICY_MASK) >> FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT; node->min_priority = to_kernel_prio(node->sched_policy, priority); node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); node->inherit_rt = !!(flags & FLAT_BINDER_FLAG_INHERIT_RT); node->txn_security_ctx = !!(flags & FLAT_BINDER_FLAG_TXN_SECURITY_CTX); spin_lock_init(&node->lock); INIT_LIST_HEAD(&node->work.entry); INIT_LIST_HEAD(&node->async_todo); binder_debug(BINDER_DEBUG_INTERNAL_REFS, "%d:%d node %d u%016llx c%016llx created\n", proc->pid, current->pid, node->debug_id, (u64)node->ptr, (u64)node->cookie); return node; } -
至此
binder_become_context_manager()就分析完畢了,其所謂的將SM設定為大管家,本質上其實就是兩點:- SM的驅動層Binder物體創建是特殊的,
binder_new_node(proc, 0, 0)中傳入的引數都是0 - SM的驅動層Binder物體用
binder_context_mgr_node進行了保存,而這就保證了所有server都可以通過context->binder_context_mgr_node獲取到SM的Binder物體
- SM的驅動層Binder物體創建是特殊的,
3. binder_loop()方法決議
-
代碼路徑:android/framework/native/cmds/servicemanager/binder.c
void binder_loop(struct binder_state *bs, binder_handler func) { int res; struct binder_write_read bwr; uint32_t readbuf[32]; //首先將寫資料置空 bwr.write_size = 0; bwr.write_consumed = 0; bwr.write_buffer = 0; //放入讀資料為BC_ENTER_LOOPER readbuf[0] = BC_ENTER_LOOPER; //將資料通過ioctl寫入, //這里的作用就是告訴Kernel binder驅動,我(ServiceManager行程)要進入訊息回圈狀態了,請做好相關準備 binder_write(bs, readbuf, sizeof(uint32_t)); //此處是死回圈 for (;;) { //這里的read資料就是BC_ENTER_LOOPER bwr.read_size = sizeof(readbuf); bwr.read_consumed = 0; bwr.read_buffer = (uintptr_t) readbuf; //通過ioctl呼叫kernel層的binder_ioctl,這里首次進來,write_buffer是空的,read_buffer為BC_ENTER_LOOPER //如果沒有訊息需要處理,同步狀態下,會在這里阻塞 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; } } }決議
binder_loop()方法可以看到,其中存在死回圈,也就是說,SM啟動后最侄訓一直在binder_loop()方法中回圈獲取訊息,而在ioctl()方法處,如果不再有訊息,則會阻塞,接下來進一步決議其中的方法,從
binder_write()方法開始看:int binder_write(struct binder_state *bs, void *data, size_t len) { struct binder_write_read bwr; int res; //將傳入的data放到binder_write_read資料結構中的write_buffer中,此處的data為BC_ENTER_LOOPER bwr.write_size = len; bwr.write_consumed = 0; bwr.write_buffer = (uintptr_t) data; bwr.read_size = 0; bwr.read_consumed = 0; bwr.read_buffer = 0; //通過ioctl呼叫kernel層的binder_ioctl將資料寫入 res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); if (res < 0) { fprintf(stderr,"binder_write: ioctl failed (%s)\n", strerror(errno)); } return res; }分析
binder_write()發現,最終也是呼叫了ioctl()方法進到了kernel層進行訊息處理,那么接下來簡單分析一下傳入的資料分別為:write_buffer = BC_ENTER_LOOPER和read_buffer = BC_ENTER_LOOPER時,在kernel層binder_ioctl()中都做了哪些動作吧- 代碼路徑:android/drivers/android/binder.c
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { .... switch (cmd) { ..... //傳入的cmd為BINDER_WRITE_READ,命中case case BINDER_WRITE_READ: ret = binder_ioctl_write_read(filp, cmd, arg, thread); if (ret) goto err; break; ...... } ..... }兩次傳下來的cmd都為
BINDER_WRITE_READ,命中對應case,呼叫方法:binder_ioctl_write_read(filp, cmd, arg, thread)static int binder_ioctl_write_read(struct file *filp, unsigned int cmd, unsigned long arg, struct binder_thread *thread) { int ret = 0; //之前binder_open()方法打開的對應binder驅動檔案背景關系資訊,就是存在filp->private_data中,此時就是通過其獲取到之前打開的binder背景關系 struct binder_proc *proc = filp->private_data; unsigned int size = _IOC_SIZE(cmd); void __user *ubuf = (void __user *)arg; struct binder_write_read bwr; if (size != sizeof(struct binder_write_read)) { ret = -EINVAL; goto out; } //通過copy_from_user將用戶空間的傳輸資料的封裝bwr,拷貝到內核空間中 if (copy_from_user(&bwr, ubuf, sizeof(bwr))) { ret = -EFAULT; goto out; } binder_debug(BINDER_DEBUG_READ_WRITE, "%d:%d write %lld at %016llx, read %lld at %016llx\n", proc->pid, thread->pid, (u64)bwr.write_size, (u64)bwr.write_buffer, (u64)bwr.read_size, (u64)bwr.read_buffer); //第一次進入時,write_size>0,存放的資料是BC_ENTER_LOOPER,命中if;第二次進入時,write_size=0,不滿足條件 if (bwr.write_size > 0) { ret = binder_thread_write(proc, thread, bwr.write_buffer, bwr.write_size, &bwr.write_consumed); trace_binder_write_done(ret); if (ret < 0) { bwr.read_consumed = 0; if (copy_to_user(ubuf, &bwr, sizeof(bwr))) ret = -EFAULT; goto out; } } //第一次進入時read_size=0,不滿足條件;第二次進入時,read_size>0,存放的資料是BC_ENTER_LOOPER,命中if if (bwr.read_size > 0) { ret = binder_thread_read(proc, thread, bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK); trace_binder_read_done(ret); //如果todo佇列不為空,那么就喚醒處于中斷狀態的wait等待執行緒 if (!list_empty(&proc->todo)) wake_up_interruptible(&proc->wait); if (ret < 0) { if (copy_to_user(ubuf, &bwr, sizeof(bwr))) ret = -EFAULT; goto out; } } binder_debug(BINDER_DEBUG_READ_WRITE, "%d:%d wrote %lld of %lld, read return %lld of %lld\n", proc->pid, thread->pid, (u64)bwr.write_consumed, (u64)bwr.write_size, (u64)bwr.read_consumed, (u64)bwr.read_size); //讀寫完畢后,將內核空間的資料的封裝bwr,拷貝回用戶空間 if (copy_to_user(ubuf, &bwr, sizeof(bwr))) { ret = -EFAULT; goto out; } out: return ret; }接下來我們逐一分析
binder_thread_write()和binder_thread_read()方法先看一下
binder_thread_write():static int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, binder_uintptr_t binder_buffer, size_t size, binder_size_t *consumed) { uint32_t cmd; struct binder_context *context = proc->context; void __user *buffer = (void __user *)(uintptr_t)binder_buffer; void __user *ptr = buffer + *consumed; void __user *end = buffer + size; //回圈獲取buffer中的資料 while (ptr < end && thread->return_error == BR_OK) { if (get_user(cmd, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); trace_binder_command(cmd); if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) { binder_stats.bc[_IOC_NR(cmd)]++; proc->stats.bc[_IOC_NR(cmd)]++; thread->stats.bc[_IOC_NR(cmd)]++; } switch (cmd) { //此時buffer中的資料是BC_ENTER_LOOPER,命中case case BC_ENTER_LOOPER: binder_debug(BINDER_DEBUG_THREADS, "%d:%d BC_ENTER_LOOPER\n", proc->pid, thread->pid); if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) { thread->looper |= BINDER_LOOPER_STATE_INVALID; binder_user_error("%d:%d ERROR: BC_ENTER_LOOPER called after BC_REGISTER_LOOPER\n", proc->pid, thread->pid); } //更新looper的值,表明現在進入了loop狀態 thread->looper |= BINDER_LOOPER_STATE_ENTERED; break; ...... } return 0; }到這里,第一個寫入的
BC_ENTER_LOOPER就處理完成了,其實就是置了標志位,表明當下進入了loop狀態,接下來,我們看一下
binder_thread_read()中,對BC_ENTER_LOOPER的處理:static int binder_thread_read(struct binder_proc *proc, struct binder_thread *thread, binder_uintptr_t binder_buffer, size_t size, binder_size_t *consumed, int non_block) { void __user *buffer = (void __user *)(uintptr_t)binder_buffer; void __user *ptr = buffer + *consumed; void __user *end = buffer + size; int ret = 0; int wait_for_proc_work; //第一次進來consumed=0成立,命中if,向用戶空間寫入訊息:BR_NOOP if (*consumed == 0) { if (put_user(BR_NOOP, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); } ........ //設定標志位,BINDER_LOOPER_STATE_WAITING thread->looper |= BINDER_LOOPER_STATE_WAITING; if (wait_for_proc_work) proc->ready_threads++; binder_unlock(__func__); trace_binder_wait_for_work(wait_for_proc_work, !!thread->transaction_stack, !list_empty(&thread->todo)); if (wait_for_proc_work) { //在呼叫binder_thread_write時,我們置入了標志位:BINDER_LOOPER_STATE_ENTERED,所以這里為false if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED))) { binder_user_error("%d:%d ERROR: Thread waiting for process work before calling BC_REGISTER_LOOPER or BC_ENTER_LOOPER (state %x)\n", proc->pid, thread->pid, thread->looper); wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); } //設定優先級,當前執行緒要處理proc的事務,所以設定優先級和proc一樣 binder_set_nice(proc->default_priority); //判斷是否是訊息同步處理機制 //如果是不阻塞式,即異步的處理方式,通過binder_has_proc_work()讀取訊息,如果沒有訊息,則直接回傳 if (non_block) { if (!binder_has_proc_work(proc, thread)) ret = -EAGAIN; } else //如果是阻塞式,即同步的處理方式,同樣使用binder_has_proc_work()讀取訊息,但是如果沒有訊息存在,則在此處阻塞 ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread)); } else { if (non_block) { if (!binder_has_thread_work(thread)) ret = -EAGAIN; } else ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread)); } ...... }至此,
binder_thread_read()也已分析完成,當待處理佇列中沒有需要處理的事務時,那么就會阻塞(針對SM訊息啟動來說),直到有待處理事務寫入,從而喚醒SM的binder_loop(),繼續獲取事務進行處理
Service Manager啟動總結
- 通過對
service_manager.c檔案中的main()方法的決議,我們可以得知:- 對于ServiceManager行程來說:
- 通過
binder_open()方法打開 Binder驅動設備檔案,并且將記憶體映射到ServiceManager的行程空間 - 通過
binder_become_context_manager()設定當前行程為Binder背景關系管理者 - 通過
binder_loop()進入訊息獲取回圈,等待Client的請求
- 通過
- 對于Binder驅動來說:
- 初始化了ServiceManager對應的行程背景關系環境,即
binder_proc變數 - 將內核虛擬記憶體和用戶虛擬記憶體映射到同一塊物理記憶體中,大小是128K
- 新建當前執行緒對應的
binder_thread物件,并將其添加到行程背景關系資訊binder_proc->threads紅黑樹中 - 新建ServiceManager的Binder物體,并將該Binder物體用
context->binder_context_mgr_node保存 - 當沒有訊息事務處理時,進入中斷等待狀態,等待其他行程將其喚醒
- 初始化了ServiceManager對應的行程背景關系環境,即
- 對于ServiceManager行程來說:
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/309540.html
標籤:其他
上一篇:Android異步任務與多執行緒
