Android 重學系列 Binder驅(qū)動初始化 Binder的Looper初始化

如果遇到問題請到:http://www.reibang.com/p/2ab3aaf2aeb6

ServiceMananger 的初始化第二步 把進程對象注冊到Binder驅(qū)動中

文件:/frameworks/native/cmds/servicemanager/service_manager.c

    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }

我們看看這個方法具體做了什么后众。
文件:/drivers/staging/android/binder.c

int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

這里又是我們熟悉的ioctl系統(tǒng)調(diào)用。這是初始化之后第一次使用bs對象,binder_state在binder_open中初始化的結(jié)構(gòu)體。此時這個結(jié)構(gòu)體包含著該binder的共享地址。

我們直接看看binder_ioctl中的switch片段

case BINDER_SET_CONTEXT_MGR:
        ret = binder_ioctl_set_ctx_mgr(filp);
        if (ret)
            goto err;
...
        break;
static int binder_ioctl_set_ctx_mgr(struct file *filp)
{
    int ret = 0;
    struct binder_proc *proc = filp->private_data;
...
    binder_context_mgr_node = binder_new_node(proc, 0, 0);
    if (binder_context_mgr_node == NULL) {
        ret = -ENOMEM;
        goto out;
    }
    binder_context_mgr_node->local_weak_refs++;
    binder_context_mgr_node->local_strong_refs++;
    binder_context_mgr_node->has_strong_ref = 1;
    binder_context_mgr_node->has_weak_ref = 1;
out:
    return ret;
}

實際上,我們需要關注的只有這么一小段。我們再一次從文件中獲取私密對象,當前的進程對應的binder_proc對象。此時應用剛剛啟動睦授,因此整個binder驅(qū)動下都是空是复。因此此時我們需要新生成一個binder_node結(jié)構(gòu)體加入到binder的紅黑樹中管理。而這個binder_node代表著在binder驅(qū)動中,一個進程啥繁,工作項蜜另,引用列表等關鍵數(shù)據(jù)的集合。

當我們添加并且生成binder一個新的binder _node對象之后,把它賦值給binder_context_mgr_node這個對象。這個對象是為了快速的尋找service_manager而創(chuàng)建的全局對象。這也因為考慮到Android系統(tǒng)處處使用這個對象。

我們來看看binder_new_node方法殉了。

static struct binder_node *binder_new_node(struct binder_proc *proc,
                       binder_uintptr_t ptr,
                       binder_uintptr_t cookie)
{
    struct rb_node **p = &proc->nodes.rb_node;
    struct rb_node *parent = NULL;
    struct binder_node *node;

    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
            return NULL;
    }

    node = kzalloc(sizeof(*node), GFP_KERNEL);
    if (node == NULL)
        return NULL;
    binder_stats_created(BINDER_STAT_NODE);
    rb_link_node(&node->rb_node, parent, p);
    rb_insert_color(&node->rb_node, &proc->nodes);
    node->debug_id = ++binder_last_id;
    node->proc = proc;
    node->ptr = ptr;
    node->cookie = cookie;
    node->work.type = BINDER_WORK_NODE;
    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創(chuàng)建一個新的Binder 實體如果看過我的紅黑樹文章這里也就輕而易舉了。

此時Binder將會從紅黑樹中根據(jù)node的弱引用的地址作為key尋找node。此時肯定是不會找到的,因此會通過kzmalloc生成一個新的node,并且添加到rb_node這個紅黑樹中管理庸推。并且把binder的本地對象設置到cookie中。此時node的work模式是BINDER_WORK_NODE。

這樣Binder驅(qū)動中第一個代表著service manager的binder實體就創(chuàng)建完成了养葵。

此時的生成模式并沒有有頂層的JavaBBinder咳榜,BpBinder,IPCThreadState等核心初始化Binder類參與進來玄捕。是一個極其特殊的服務的初始化馍迄。所以很多地方?jīng)]有把service manager這個作為一個binder 服務,而是說是binder驅(qū)動的守護進程。然而歸根結(jié)底幌缝,也不過只是注冊在Binder驅(qū)動中的binder對象缘厢。

ServiceMananger 的初始化第三步 service_manager啟動消息等待循環(huán)

文件:/frameworks/native/cmds/servicemanager/service_manager.c

binder_loop(bs, svcmgr_handler);

實際上這里就是啟動Android Service體系中的消息等待初始化间护。
文件:/frameworks/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;

    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) {
...
            break;
        }

        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
        if (res == 0) {
...
            break;
        }
        if (res < 0) {
...
            break;
        }
    }
}

依據(jù)之前學習到的東西,我們大致上可以知道拾积,這個looper做了以下幾個事情玲销。

  • 1.service_manager先往binder驅(qū)動中往binder_write_read寫入BC_ENTER_LOOPER 策吠,告訴binder驅(qū)動進入service的循環(huán)命令。

    1. service_manager 進入阻塞瘩绒,等待binder驅(qū)動往binder_write_read寫入數(shù)據(jù)猴抹。
  • 3.解析從binder驅(qū)動中傳送上來的數(shù)據(jù)。

接下來锁荔,我將分為三點慢慢來聊聊蟀给。

1.binder looper 發(fā)送BC_ENTER_LOOPER 命令

這里有一個很關鍵的結(jié)構(gòu)體 binder_write_read
文件:/bionic/libc/kernel/uapi/linux/android/binder.h

struct binder_write_read {
  binder_size_t write_size;//寫入數(shù)據(jù)的大小
  binder_size_t write_consumed;//寫入的數(shù)據(jù),已經(jīng)寫過了多少位置
  binder_uintptr_t write_buffer;// 寫入數(shù)據(jù)的數(shù)據(jù)緩沖區(qū)

  binder_size_t read_size;//讀取數(shù)據(jù)的大小
  binder_size_t read_consumed;//讀取的數(shù)據(jù)阳堕,已經(jīng)讀取多少數(shù)據(jù)
  binder_uintptr_t read_buffer;//讀取數(shù)據(jù)的數(shù)據(jù)緩沖區(qū)
};

結(jié)構(gòu)體binder_write_read可以分為兩部分跋理,上部分描述了要寫進去的數(shù)據(jù),下部分描述要讀取的數(shù)據(jù)恬总。binder_write_read結(jié)構(gòu)體一般是用來承載framework層數(shù)據(jù)的載體前普,用于傳遞數(shù)據(jù)給binder驅(qū)動。

    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;

    readbuf[0] = BC_ENTER_LOOPER;
    binder_write(bs, readbuf, sizeof(uint32_t));

binder一開始對寫入數(shù)據(jù)進行初始化壹堰。接著BC_ENTER_LOOPER放到readbuf屬性中拭卿,通過binder_write往binder驅(qū)動寫入骡湖。

int binder_write(struct binder_state *bs, void *data, size_t len)
{
    struct binder_write_read bwr;
    int res;

    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;
    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_read 把讀取相關的數(shù)據(jù)都初始化為0,而寫入數(shù)據(jù)相關的屬性峻厚,write_buffer寫入數(shù)據(jù)响蕴,write_size寫入數(shù)據(jù)長度,write_consumed 為0.這樣就告訴了binder驅(qū)動知道數(shù)據(jù)在哪里惠桃,應該從哪里開始讀取浦夷。
文件:/drivers/staging/android/binder.c

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    int ret;
    struct binder_proc *proc = filp->private_data;
    struct binder_thread *thread;
    unsigned int size = _IOC_SIZE(cmd);
    void __user *ubuf = (void __user *)arg;

...
    switch (cmd) {
    case BINDER_WRITE_READ:
        ret = binder_ioctl_write_read(filp, cmd, arg, thread);
        if (ret)
            goto err;
        break;

此時根據(jù)上面?zhèn)飨聛淼臄?shù)據(jù),將會走binder_ioctl_write_read分支刽射。從這里開始就是binder的核心分支之一,binder驅(qū)動在進程間讀寫數(shù)據(jù)的核心就是這個方法剃执。

static int binder_ioctl_write_read(struct file *filp,
                unsigned int cmd, unsigned long arg,
                struct binder_thread *thread)
{
    int ret = 0;
    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;
    }
    if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
        ret = -EFAULT;
        goto out;
    }
...
    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;
        }
    }
    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);
...
        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;
        }
    }
...
    if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
        ret = -EFAULT;
        goto out;
    }
out:
    return ret;
}

binder分為三個步驟進行解析從ioctl傳送下來的數(shù)據(jù)誓禁。

  • 1.把傳遞下來的數(shù)據(jù)轉(zhuǎn)型為內(nèi)核對應的binder_write_read結(jié)構(gòu)體蜘拉。
    1. 當判斷到binder_write_read中write_size大于0昔驱,說明有數(shù)據(jù)寫入,則執(zhí)行binder_thread_write硼婿。
  • 3.當判斷到binder_write_read中read_size大于0怒见,說明有數(shù)據(jù)需要讀取俗慈,則執(zhí)行binder_thread_read。

結(jié)束完之后遣耍,則從內(nèi)核態(tài)的binder_write_read拷貝到用戶態(tài)的binder_write_read數(shù)據(jù)中闺阱。因為此時傳遞下來的ubuf恰好就是用戶空間對應的binder_write_read。因此能夠直接通過copy_to_user把數(shù)據(jù)從內(nèi)核空間拷貝一份到用戶空間舵变。

因此我們可以得出酣溃,binder在處理每個協(xié)議下來的數(shù)據(jù)時候,都是先處理寫的數(shù)據(jù)纪隙,再處理讀的數(shù)據(jù)赊豌。為什么這么做分別看看下面兩個方法就知道了。

binder處理從framework傳下來的寫數(shù)據(jù)

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;
   void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
   void __user *ptr = buffer + *consumed;
   void __user *end = buffer + size;

   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) {
       ...
       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);
           }
           thread->looper |= BINDER_LOOPER_STATE_ENTERED;
           break;
       case BC_EXIT_LOOPER:
           binder_debug(BINDER_DEBUG_THREADS,
                    "%d:%d BC_EXIT_LOOPER\n",
                    proc->pid, thread->pid);
           thread->looper |= BINDER_LOOPER_STATE_EXITED;
           break;

...

       default:
           pr_err("%d:%d unknown command %d\n",
                  proc->pid, thread->pid, cmd);
           return -EINVAL;
       }
       *consumed = ptr - buffer;
   }
   return 0;
}

這里我只挑選出需要關注的分支绵咱。
首先binder在處理寫入數(shù)據(jù)的時候碘饼,由于沒辦法直接通過sizeof直接找到數(shù)據(jù)結(jié)構(gòu)的邊界,因此通過思路上和Parcel相似悲伶,通過下面幾種參數(shù)來控制整個讀寫過程艾恼。

   uint32_t cmd;
   void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
   void __user *ptr = buffer + *consumed;
   void __user *end = buffer + size;
  • 1.cmd 這個縮寫英文我們可以直接望文生義,就是從framework中寫進來的write_buffer中第一個int型麸锉,這個決定了驅(qū)動怎么解析這次命令數(shù)據(jù)蒂萎。
  • 2.buffer 對應這用戶空間的write_buffer 這里面存儲著需要處理的數(shù)據(jù)。
  • 3.ptr 對應著此時binder驅(qū)動已經(jīng)處理了多少數(shù)據(jù)淮椰。
    1. end 確定這一次buffer邊界五慈。
數(shù)據(jù)解析循環(huán)
   while (ptr < end && thread->return_error == BR_OK) {
       if (get_user(cmd, (uint32_t __user *)ptr))
           return -EFAULT;
       ptr += sizeof(uint32_t);

根據(jù)上面的數(shù)據(jù)解析纳寂,因此可以知道此時數(shù)據(jù)解析的循環(huán)結(jié)束條件有兩個,第一 buffer的數(shù)據(jù)區(qū)域循環(huán)到了結(jié)束地址泻拦,第二毙芜,binder_thread 返回BR_OK。

從第一個get_user從用戶空間拷貝方法出來得知争拐,每一次循環(huán)第一個參數(shù)必定是符合條件的cmd腋粥,對應著下面binder分支命令。接著消費指針向前移動一個int的大小架曹,而后面就是我們需要處理的數(shù)據(jù)隘冲。

此時我們從用戶空間下傳下來的命令正是BC_ENTER_LOOPER。

  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);
           }
           thread->looper |= BINDER_LOOPER_STATE_ENTERED;
           break;

此時命令需要的操作很簡單绑雄,就是修改當前binder_proc對應的binder_thread的狀態(tài)展辞。

這樣就完成了service_manager 從用戶空間的寫入操作。還記得上面的對binder_write_read的結(jié)構(gòu)體處理嗎万牺?此時因為read_size被設置為0.因此走不到binder_thread_read罗珍。接著把內(nèi)核空間對應的binder_write_read拷貝回到用戶空間即可。

service_manager 正式進入到binder looper循環(huán)等待消息脚粟。

文件:/frameworks/native/cmds/servicemanager/binder.c

  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) {
...
            break;
        }

可以看到這是一個無限的循環(huán)覆旱,等待著binder驅(qū)動信息返回信息。但是做一個google開發(fā)者怎么可能真的讓循環(huán)不斷進行下去呢核无?看過我啟動的zygote一章節(jié)的讀者扣唱,肯定知道一直在跑無限循環(huán)只會不斷的開銷cpu,因此在這個循環(huán)必定會通過阻塞之類的手段來規(guī)避這種looper的開銷团南。

我們看看循環(huán)的第一段画舌。此時,把讀取的數(shù)據(jù)長度設置為readbuf 一個長度為32的int數(shù)組已慢。接著通過ioctl曲聂,通信到binder驅(qū)動。

文件:/drivers/staging/android/binder.c
此時我們根據(jù)service_manager可以得知佑惠,此時write_size為0朋腋,read_size不為0,將會走binder_ioctl_write_read 讀取數(shù)據(jù)的代碼:

    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);
...
        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;
        }
    }
...
    if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
        ret = -EFAULT;
        goto out;
    }
out:
    return ret;

我們看看binder_thread_read內(nèi)部邏輯。

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;

    if (*consumed == 0) {
        if (put_user(BR_NOOP, (uint32_t __user *)ptr))
            return -EFAULT;
        ptr += sizeof(uint32_t);
    }

retry:
    wait_for_proc_work = thread->transaction_stack == NULL &&
                list_empty(&thread->todo);

    if (thread->return_error != BR_OK && ptr < end) {
        if (thread->return_error2 != BR_OK) {
            if (put_user(thread->return_error2, (uint32_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(uint32_t);
            binder_stat_br(proc, thread, thread->return_error2);
            if (ptr == end)
                goto done;
            thread->return_error2 = BR_OK;
        }
        if (put_user(thread->return_error, (uint32_t __user *)ptr))
            return -EFAULT;
        ptr += sizeof(uint32_t);
        binder_stat_br(proc, thread, thread->return_error);
        thread->return_error = BR_OK;
        goto done;
    }


    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) {
        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);
        }
        binder_set_nice(proc->default_priority);
        if (non_block) {
            if (!binder_has_proc_work(proc, thread))
                ret = -EAGAIN;
        } else
            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_lock(__func__);

    if (wait_for_proc_work)
        proc->ready_threads--;
    thread->looper &= ~BINDER_LOOPER_STATE_WAITING;

    if (ret)
        return ret;

    while (1) {
...
    }

done:

    *consumed = ptr - buffer;
...
    return 0;
}

原理和binder_thread_write相似膜楷。binder_thread_read 做了以下幾件事情旭咽。

  • 1.首先判斷到此時binder 驅(qū)動沒有讀取任何數(shù)據(jù)時候,則會為用戶空間返回的數(shù)據(jù)中赌厅,第一段數(shù)據(jù)加上BR_NOOP穷绵。
    1. wait_for_proc_work 判斷當前進程是否需要等待工作。這個標志位的判斷條件為binder _thread的事務處理棧為空同時binder_thread 的todo list沒有任何需要todo的項特愿。
    1. 設置binder_thread->looper狀態(tài)進入到了BINDER_LOOPER_STATE_WAITING狀態(tài)
  • 4.假如需要等待仲墨,則判斷當前binder初始化的時候是可阻塞工作還是不可阻塞工作勾缭。如果是可阻塞,則會取出binder _thread->wait 等待隊列目养,讓本進程進入到等待當中俩由。還記得我之前寫的等待隊列的本質(zhì)吧。實際上就是把這個時候進程會通過進程調(diào)度癌蚁,把當前進程的需要的cpu資源讓渡出去幻梯。如果是非阻塞,則判斷當前binder_thread中是否還有需要的工作努释,沒有則直接返回碘梢。
    1. 當當前進程的等待隊列被喚醒,則會把 thread->looper 的BINDER_LOOPER_STATE_WAITING關閉伐蒂。
  • 6.進入到while循環(huán)解析數(shù)據(jù)煞躬。

而此時的場景,我們并有任何的需要工作的隊列饿自,因此通過wait_event_freezable把service_manager阻塞起來汰翠。

service_manager binder_parse獲取binder驅(qū)動回復的消息消息龄坪。

int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uintptr_t ptr, size_t size, binder_handler func)
{
    int r = 1;
    uintptr_t end = ptr + (uintptr_t) size;

    while (ptr < end) {
        uint32_t cmd = *(uint32_t *) ptr;
        ptr += sizeof(uint32_t);
#if TRACE
        fprintf(stderr,"%s:\n", cmd_name(cmd));
#endif
        switch(cmd) {
        case BR_NOOP:
            break;
....
        default:
            ALOGE("parse: OOPS %d\n", cmd);
            return -1;
        }
    }

    return r;
}

這里場景模擬昭雌,假如有某個線程喚醒了service_manager,此時ptr實際上就是readbuf這個緩沖區(qū)。我們不管這個數(shù)據(jù)如何健田,第一個返回的參數(shù)必定是BR_NOOP烛卧,告訴著service_manager開始讀取數(shù)據(jù)的開頭標志位。接著不斷的移動指針妓局,讀取處理每一段信息总放。

因此我們可以模擬tcp封包一樣模擬出binder驅(qū)動在通信時候,數(shù)據(jù)是如何封包的好爬。

binder通信封包.png

特殊的當讀取通信信息的時候局雄,封包格式將如下:


binder驅(qū)動讀取之后返回的通信數(shù)據(jù).png

這里就是binder驅(qū)動在Android 系統(tǒng)中service_manager 體系的初始化。當然還有一種aidl的binder初始化存炮,我將會在后面和大家揭曉炬搭。

總結(jié)

這里總結(jié)一副時序圖,為了便于理解穆桂,我省略掉通過軟中斷到內(nèi)核空間的過程宫盔。


binder驅(qū)動在Android service系統(tǒng)初始化.png

從上圖我們大致上可以總結(jié)出Binder驅(qū)動在系統(tǒng)初始化的時候大致上分為以下三步:

    1. binder_open 打開binder驅(qū)動文件,確認版本號享完,并把該進程以及相關信息映射到內(nèi)核中
  • 2.mmap 確認能夠打開binder驅(qū)動之后灼芭,再把當前進程的地址和內(nèi)核映射到一起。
  • 3.把當前的service_manager作為一個binder實體注冊到binder驅(qū)動中般又,作為第一個binder服務彼绷。
  • 4.進入binder_loop巍佑。先通過ioctl 通知binder驅(qū)動此時service_manager進入到了循環(huán)模式。接著調(diào)用讀取數(shù)據(jù)函數(shù)苛预,進入阻塞狀態(tài)句狼。當service_manager被喚醒,則開始解析從binder傳上來的數(shù)據(jù)热某。

到目前位置腻菇,我如下圖已經(jīng)將描紅的部分在Android 服務系統(tǒng)中初始化的闡述完畢。


binder驅(qū)動初始化.png

能夠注意到的是昔馋,此時我們還沒有添加任何的binder 的服務進來筹吐。但是基礎的dns(service_manager)和路由分發(fā)器(Binder驅(qū)動)已經(jīng)準備好了,接下來秘遏,讓我們聊聊client 和 server的初始化丘薛。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市邦危,隨后出現(xiàn)的幾起案子洋侨,更是在濱河造成了極大的恐慌,老刑警劉巖倦蚪,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件希坚,死亡現(xiàn)場離奇詭異,居然都是意外死亡陵且,警方通過查閱死者的電腦和手機裁僧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來慕购,“玉大人聊疲,你說我怎么就攤上這事』Ρ” “怎么了获洲?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長殿如。 經(jīng)常有香客問我贡珊,道長,這世上最難降的妖魔是什么握截? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任飞崖,我火速辦了婚禮,結(jié)果婚禮上谨胞,老公的妹妹穿的比我還像新娘固歪。我一直安慰自己,他們只是感情好牢裳,可當我...
    茶點故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布蒲讯。 她就那樣靜靜地躺著,像睡著了一般判帮。 火紅的嫁衣襯著肌膚如雪晦墙。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天但指,我揣著相機與錄音抗楔,去河邊找鬼。 笑死剩岳,一個胖子當著我的面吹牛反粥,可吹牛的內(nèi)容都是我干的疲迂。 我是一名探鬼主播尤蒿,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼尾组!你這毒婦竟也來了示弓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤跨跨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后忱嘹,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體耕渴,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡橱脸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了椭盏。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吻商。...
    茶點故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡艾帐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出准浴,到底是詐尸還是另有隱情捎稚,我是刑警寧澤,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布葡公,位于F島的核電站条霜,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蒲凶。R本人自食惡果不足惜拆内,卻給世界環(huán)境...
    茶點故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一麸恍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧孩等,春花似錦、人聲如沸肄方。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蝴罪。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間炒瘟,已是汗流浹背疮装。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工受啥, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留居暖,地道東北人糯景。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親脓魏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,654評論 2 354