AndroidシステムのBinderサブシステム(下)


binder駆動のフレームワークと、サービスをどのように登録し、サービスを取得し、サービスを使用するかを分析した後、binder transaction stackメカニズムを見てみましょう.
プロセスAがプロセスBにデータを相互に送信することについても前述したが,プロセスAがBC_を先に送信するモードである.TRANSACTIONはプロセスBに、プロセスBが受け取った後にプロセスAにBR_を返信するTRANSACTIONは、データが受信されたことを示します.そして、プロセスBは、プロセスAにBC_を送信するREPLYは、プロセスBがプロセスAに返信する必要がある関連データを送信するために使用され、プロセスAがデータを受信した後、プロセスBにBR_を送信するREPLYは、返信されたデータが受信されたことを示します.これらは駆動によってデータのインタラクションを行い、この4つのモードだけが2つのプロセスに関連し、他のモードはappと駆動のインタラクションにすぎず、ステータスを変更/レポートするために使用されます.
では、2つの問題があります.誰に送る必要がありますか?誰に返しますか.この2つの質問に答えるにはtestを見てみましょうClient(A)とtest_server(B)はどのように働いていますか.私たちの前のコード分析によると、svcmgr_publish登録サービスシーケンス呼び出しbinder_call関数、binder_call関数でパラメータを構築する場合cmdはBC_TRANSACTION.ここからtest_クライアントはまずBCを送信しますTRANSACTION給test_server.そしてwrite関連のデータを構築し、最後にioctlを呼び出してBINDER_を送信するWRITE_READ.ではbinderに入りますioctl関数で見てみましょう.BINDERを見つけたWRITE_READコマンドは、先の構造のwriteに関するデータから、binder_thread_write関数です.binder_thread_write関数でBC_が見つかりましたTRANSACTIONコマンドは、binderを作ったのを見ました.Transactionの動作.
私たちはbinderに入っています.Transaction関数では、どのように完了したかを見てみましょう.
a.最初は「双方向伝送」ではなかった.だから、データはtest_に置かれます.serverのbinder_proc.todoチェーンテーブル;
b.入Aスタック、test_client.binder_thread.transaction_stack中:.from = test_client、.to_proc = test_server、.to_thread= test_server;
c.データをtest_に入れるserver.binder_proc.todoチェーンテーブルでtest_を起動server.binder_proc.wait上のスレッド.
test_の場合serverはBRを受け取ったTRANSACTIONならbinder_thread_read関数では、次のことを行います.
a.test_からserver.binder_proc.todoチェーンテーブルからデータを取り出し、処理する.
b.Bスタックに入る、test_server.binder_thread.transaction_stack中:.from = test_client、.to_proc = test_server、.to_thread= test_server;
同じbinder_についてTransactionではfrom_parentは送信者のスタックに入れる、通過する.to_parentは受信者のスタックに入れます.
そしてBはAにBC_を送信するREPLYもbinder_Transaction関数で完了したもの:
a.スタックからtest_を取り出すserver.binder_thread.transaction_stackの関連データ(.from、.to_proc、.to_thread)は、.from可知返信test_client;
b.test_server、すなわちtest_server.binder_thread.transaction_stack = NULL;
c.データcopy_from_userからtest_client;
d.スタック(test_client)、すなわちtest_client.binder_thread.transaction_stack = NULL;
e.todoチェーンテーブルを入れ、起動します.
最後のプロセスAは、データを受け取った後、プロセスBにBR_を送信するREPLY.ユーザースペースに戻ると、スタックの操作には触れません.
これで、2つのプロセスのインタラクティブなプロセスが完了しました.
関連コードは次のとおりです.
int svcmgr_publish(struct binder_state *bs, uint32_t target, const char *name, void *ptr)
{
    int status;
    unsigned iodata[512/4];
    struct binder_io msg, reply;

    bio_init(&msg, iodata, sizeof(iodata), 4);
    bio_put_uint32(&msg, 0);  // strict mode header
    bio_put_string16_x(&msg, SVC_MGR_NAME);
    bio_put_string16_x(&msg, name);
    bio_put_obj(&msg, ptr);

    if (binder_call(bs, &msg, &reply, target, SVC_MGR_ADD_SERVICE))    //     
        return -1;

    status = bio_get_uint32(&reply);

    binder_done(bs, &msg, &reply);

    return status;
}

binder_call関数は次のとおりです.
int binder_call(struct binder_state *bs,
                struct binder_io *msg, struct binder_io *reply,
                uint32_t target, uint32_t code)
{
    int res;
    struct binder_write_read bwr;
    struct {
        uint32_t cmd;
        struct binder_transaction_data txn;
    } __attribute__((packed)) writebuf;
    unsigned readbuf[32];

    if (msg->flags & BIO_F_OVERFLOW) {
        fprintf(stderr,"binder: txn buffer overflow
");         goto fail;     }     //      writebuf.cmd = BC_TRANSACTION;     writebuf.txn.target.handle = target;     writebuf.txn.code = code;     writebuf.txn.flags = 0;     writebuf.txn.data_size = msg->data - msg->data0;     writebuf.txn.offsets_size = ((char*) msg->offs) - ((char*) msg->offs0);     writebuf.txn.data.ptr.buffer = (uintptr_t)msg->data0;     writebuf.txn.data.ptr.offsets = (uintptr_t)msg->offs0;     bwr.write_size = sizeof(writebuf);     bwr.write_consumed = 0;     bwr.write_buffer = (uintptr_t) &writebuf;     hexdump(msg->data0, msg->data - msg->data0);     for (;;) {         bwr.read_size = sizeof(readbuf);         bwr.read_consumed = 0;         bwr.read_buffer = (uintptr_t) readbuf;         res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);    //   ioctl          if (res flags |= BIO_F_IOERROR;     return -1; }

binder_Transaction関数は次のとおりです.
static void binder_transaction(struct binder_proc *proc,
                   struct binder_thread *thread,
                   struct binder_transaction_data *tr, int reply)
{
    struct binder_transaction *t;
    struct binder_work *tcomplete;
    size_t *offp, *off_end;
    size_t off_min;
    struct binder_proc *target_proc;
    struct binder_thread *target_thread = NULL;
    struct binder_node *target_node = NULL;
    struct list_head *target_list;
    wait_queue_head_t *target_wait;
    struct binder_transaction *in_reply_to = NULL;
    struct binder_transaction_log_entry *e;
    uint32_t return_error = BR_OK;

    e = binder_transaction_log_add(&binder_transaction_log);
    e->call_type = reply ? 2 : !!(tr->flags & TF_ONE_WAY);
    e->from_proc = proc->pid;
    e->from_thread = thread->pid;
    e->target_handle = tr->target.handle;
    e->data_size = tr->data_size;
    e->offsets_size = tr->offsets_size;

    if (reply) {
        in_reply_to = thread->transaction_stack;
        if (in_reply_to == NULL) {
            binder_user_error("binder: %d:%d got reply transaction "
                      "with no transaction stack
",                       proc->pid, thread->pid);             return_error = BR_FAILED_REPLY;             goto err_empty_call_stack;         }         binder_set_nice(in_reply_to->saved_priority);         if (in_reply_to->to_thread != thread) {             binder_user_error("binder: %d:%d got reply transaction "                 "with bad transaction stack,"                 " transaction %d has target %d:%d
",                 proc->pid, thread->pid, in_reply_to->debug_id,                 in_reply_to->to_proc ?                 in_reply_to->to_proc->pid : 0,                 in_reply_to->to_thread ?                 in_reply_to->to_thread->pid : 0);             return_error = BR_FAILED_REPLY;             in_reply_to = NULL;             goto err_bad_call_stack;         }         thread->transaction_stack = in_reply_to->to_parent;         target_thread = in_reply_to->from;         if (target_thread == NULL) {             return_error = BR_DEAD_REPLY;             goto err_dead_binder;         }         if (target_thread->transaction_stack != in_reply_to) {             binder_user_error("binder: %d:%d got reply transaction "                 "with bad target transaction stack %d, "                 "expected %d
",                 proc->pid, thread->pid,                 target_thread->transaction_stack ?                 target_thread->transaction_stack->debug_id : 0,                 in_reply_to->debug_id);             return_error = BR_FAILED_REPLY;             in_reply_to = NULL;             target_thread = NULL;             goto err_dead_binder;         }         target_proc = target_thread->proc;     } else {         if (tr->target.handle) {             struct binder_ref *ref;             ref = binder_get_ref(proc, tr->target.handle);             if (ref == NULL) {                 binder_user_error("binder: %d:%d got "                     "transaction to invalid handle
",                     proc->pid, thread->pid);                 return_error = BR_FAILED_REPLY;                 goto err_invalid_target_handle;             }             target_node = ref->node;         } else {             target_node = binder_context_mgr_node;             if (target_node == NULL) {                 return_error = BR_DEAD_REPLY;                 goto err_no_context_mgr_node;             }         }         e->to_node = target_node->debug_id;         target_proc = target_node->proc;         if (target_proc == NULL) {             return_error = BR_DEAD_REPLY;             goto err_dead_binder;         }         if (security_binder_transaction(proc->tsk, target_proc->tsk) flags & TF_ONE_WAY) && thread->transaction_stack) {             struct binder_transaction *tmp;             tmp = thread->transaction_stack;             if (tmp->to_thread != thread) {                 binder_user_error("binder: %d:%d got new "                     "transaction with bad transaction stack"                     ", transaction %d has target %d:%d
",                     proc->pid, thread->pid, tmp->debug_id,                     tmp->to_proc ? tmp->to_proc->pid : 0,                     tmp->to_thread ?                     tmp->to_thread->pid : 0);                 return_error = BR_FAILED_REPLY;                 goto err_bad_call_stack;             }             while (tmp) {                 if (tmp->from && tmp->from->proc == target_proc)                     target_thread = tmp->from;                 tmp = tmp->from_parent;             }         }     }     if (target_thread) {         e->to_thread = target_thread->pid;         target_list = &target_thread->todo;         target_wait = &target_thread->wait;     } else {         target_list = &target_proc->todo;         target_wait = &target_proc->wait;     }     e->to_proc = target_proc->pid;     /* TODO: reuse incoming transaction for reply */     t = kzalloc(sizeof(*t), GFP_KERNEL);     if (t == NULL) {         return_error = BR_FAILED_REPLY;         goto err_alloc_t_failed;     }     binder_stats_created(BINDER_STAT_TRANSACTION);     tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);     if (tcomplete == NULL) {         return_error = BR_FAILED_REPLY;         goto err_alloc_tcomplete_failed;     }     binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE);     t->debug_id = ++binder_last_id;     e->debug_id = t->debug_id;     if (reply)         binder_debug(BINDER_DEBUG_TRANSACTION,                  "binder: %d:%d BC_REPLY %d -> %d:%d, "                  "data %p-%p size %zd-%zd
",                  proc->pid, thread->pid, t->debug_id,                  target_proc->pid, target_thread->pid,                  tr->data.ptr.buffer, tr->data.ptr.offsets,                  tr->data_size, tr->offsets_size);     else         binder_debug(BINDER_DEBUG_TRANSACTION,                  "binder: %d:%d BC_TRANSACTION %d -> "                  "%d - node %d, data %p-%p size %zd-%zd
",                  proc->pid, thread->pid, t->debug_id,                  target_proc->pid, target_node->debug_id,                  tr->data.ptr.buffer, tr->data.ptr.offsets,                  tr->data_size, tr->offsets_size);     if (!reply && !(tr->flags & TF_ONE_WAY))         t->from = thread;     else         t->from = NULL; #if defined(CONFIG_MACH_P4NOTE) || defined(CONFIG_MACH_SP7160LTE) || defined(CONFIG_MACH_TAB3) || defined(CONFIG_MACH_KONA)     /* workaround code for invalid binder proc */     if (!proc->tsk) {         binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,                  "binder: %d:%d invalid proc
",                  proc->pid, thread->pid);         return_error = BR_FAILED_REPLY;         goto err_binder_alloc_buf_failed;     } #endif     t->sender_euid = proc->tsk->cred->euid;     t->to_proc = target_proc;     t->to_thread = target_thread;     t->code = tr->code;     t->flags = tr->flags;     t->priority = task_nice(current);     t->buffer = binder_alloc_buf(target_proc, tr->data_size,         tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));     if (t->buffer == NULL) {         return_error = BR_FAILED_REPLY;         goto err_binder_alloc_buf_failed;     }     t->buffer->allow_user_free = 0;     t->buffer->debug_id = t->debug_id;     t->buffer->transaction = t;     t->buffer->target_node = target_node;     if (target_node)         binder_inc_node(target_node, 1, 0, NULL);     offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));     if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) {         binder_user_error("binder: %d:%d got transaction with invalid "             "data ptr
", proc->pid, thread->pid);         return_error = BR_FAILED_REPLY;         goto err_copy_data_failed;     }     if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) {         binder_user_error("binder: %d:%d got transaction with invalid "             "offsets ptr
", proc->pid, thread->pid);         return_error = BR_FAILED_REPLY;         goto err_copy_data_failed;     }     if (!IS_ALIGNED(tr->offsets_size, sizeof(size_t))) {         binder_user_error("binder: %d:%d got transaction with "             "invalid offsets size, %zd
",             proc->pid, thread->pid, tr->offsets_size);         return_error = BR_FAILED_REPLY;         goto err_bad_offset;     }     off_end = (void *)offp + tr->offsets_size;     off_min = 0;     for (; offp  t->buffer->data_size - sizeof(*fp) ||             *offp buffer->data_size pid, thread->pid, *offp, off_min,                                (t->buffer->data_size - sizeof(*fp)));             return_error = BR_FAILED_REPLY;             goto err_bad_offset;         }         fp = (struct flat_binder_object *)(t->buffer->data + *offp);         off_min = *offp + sizeof(struct flat_binder_object);         switch (fp->type) {         case BINDER_TYPE_BINDER:         case BINDER_TYPE_WEAK_BINDER: {             struct binder_ref *ref;             struct binder_node *node = binder_get_node(proc, fp->binder);             if (node == NULL) {                 node = binder_new_node(proc, fp->binder, fp->cookie);                 if (node == NULL) {                     return_error = BR_FAILED_REPLY;                     goto err_binder_new_node_failed;                 }                 node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;                 node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);             }             if (fp->cookie != node->cookie) {                 binder_user_error("binder: %d:%d sending u%p "                     "node %d, cookie mismatch %p != %p
",                     proc->pid, thread->pid,                     fp->binder, node->debug_id,                     fp->cookie, node->cookie);                 goto err_binder_get_ref_for_node_failed;             }             if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {                 return_error = BR_FAILED_REPLY;                 goto err_binder_get_ref_for_node_failed;             }             ref = binder_get_ref_for_node(target_proc, node);             if (ref == NULL) {                 return_error = BR_FAILED_REPLY;                 goto err_binder_get_ref_for_node_failed;             }             if (fp->type == BINDER_TYPE_BINDER)                 fp->type = BINDER_TYPE_HANDLE;             else                 fp->type = BINDER_TYPE_WEAK_HANDLE;             fp->handle = ref->desc;             binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,                        &thread->todo);             binder_debug(BINDER_DEBUG_TRANSACTION,                      "        node %d u%p -> ref %d desc %d
",                      node->debug_id, node->ptr, ref->debug_id,                      ref->desc);         } break;         case BINDER_TYPE_HANDLE:         case BINDER_TYPE_WEAK_HANDLE: {             struct binder_ref *ref = binder_get_ref(proc, fp->handle);             if (ref == NULL) {                 binder_user_error("binder: %d:%d got "                     "transaction with invalid "                     "handle, %ld
", proc->pid,                     thread->pid, fp->handle);                 return_error = BR_FAILED_REPLY;                 goto err_binder_get_ref_failed;             }             if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {                 return_error = BR_FAILED_REPLY;                 goto err_binder_get_ref_failed;             }             if (ref->node->proc == target_proc) {                 if (fp->type == BINDER_TYPE_HANDLE)                     fp->type = BINDER_TYPE_BINDER;                 else                     fp->type = BINDER_TYPE_WEAK_BINDER;                 fp->binder = ref->node->ptr;                 fp->cookie = ref->node->cookie;                 binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL);                 binder_debug(BINDER_DEBUG_TRANSACTION,                          "        ref %d desc %d -> node %d u%p
",                          ref->debug_id, ref->desc, ref->node->debug_id,                          ref->node->ptr);             } else {                 struct binder_ref *new_ref;                 new_ref = binder_get_ref_for_node(target_proc, ref->node);                 if (new_ref == NULL) {                     return_error = BR_FAILED_REPLY;                     goto err_binder_get_ref_for_node_failed;                 }                 fp->handle = new_ref->desc;                 binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL);                 binder_debug(BINDER_DEBUG_TRANSACTION,                          "        ref %d desc %d -> ref %d desc %d (node %d)
",                          ref->debug_id, ref->desc, new_ref->debug_id,                          new_ref->desc, ref->node->debug_id);             }         } break;         case BINDER_TYPE_FD: {             int target_fd;             struct file *file;             if (reply) {                 if (!(in_reply_to->flags & TF_ACCEPT_FDS)) {                     binder_user_error("binder: %d:%d got reply with fd, %ld, but target does not allow fds
",                         proc->pid, thread->pid, fp->handle);                     return_error = BR_FAILED_REPLY;                     goto err_fd_not_allowed;                 }             } else if (!target_node->accept_fds) {                 binder_user_error("binder: %d:%d got transaction with fd, %ld, but target does not allow fds
",                     proc->pid, thread->pid, fp->handle);                 return_error = BR_FAILED_REPLY;                 goto err_fd_not_allowed;             }             file = fget(fp->handle);             if (file == NULL) {                 binder_user_error("binder: %d:%d got transaction with invalid fd, %ld
",                     proc->pid, thread->pid, fp->handle);                 return_error = BR_FAILED_REPLY;                 goto err_fget_failed;             }             if (security_binder_transfer_file(proc->tsk, target_proc->tsk, file)  %d
", fp->handle, target_fd);             /* TODO: fput? */             fp->handle = target_fd;         } break;         default:             binder_user_error("binder: %d:%d got transactio"                 "n with invalid object type, %lx
",                 proc->pid, thread->pid, fp->type);             return_error = BR_FAILED_REPLY;             goto err_bad_object_type;         }     }     if (reply) {         BUG_ON(t->buffer->async_transaction != 0);         binder_pop_transaction(target_thread, in_reply_to);     } else if (!(t->flags & TF_ONE_WAY)) {         BUG_ON(t->buffer->async_transaction != 0);         t->need_reply = 1;         t->from_parent = thread->transaction_stack;         thread->transaction_stack = t;     } else {         BUG_ON(target_node == NULL);         BUG_ON(t->buffer->async_transaction != 1);         if (target_node->has_async_transaction) {             target_list = &target_node->async_todo;             target_wait = NULL;         } else             target_node->has_async_transaction = 1;     }     t->work.type = BINDER_WORK_TRANSACTION;     list_add_tail(&t->work.entry, target_list);     tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;     list_add_tail(&tcomplete->entry, &thread->todo);     if (target_wait)         wake_up_interruptible(target_wait);     return; err_get_unused_fd_failed: err_fget_failed: err_fd_not_allowed: err_binder_get_ref_for_node_failed: err_binder_get_ref_failed: err_binder_new_node_failed: err_bad_object_type: err_bad_offset: err_copy_data_failed:     binder_transaction_buffer_release(target_proc, t->buffer, offp);     t->buffer->transaction = NULL;     binder_free_buf(target_proc, t->buffer); err_binder_alloc_buf_failed:     kfree(tcomplete);     binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE); err_alloc_tcomplete_failed:     kfree(t);     binder_stats_deleted(BINDER_STAT_TRANSACTION); err_alloc_t_failed: err_bad_call_stack: err_empty_call_stack: err_dead_binder: err_invalid_target_handle: err_no_context_mgr_node:     binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,              "binder: %d:%d transaction failed %d, size %zd-%zd
",              proc->pid, thread->pid, return_error,              tr->data_size, tr->offsets_size);     {         struct binder_transaction_log_entry *fe;         fe = binder_transaction_log_add(&binder_transaction_log_failed);         *fe = *e;     }     BUG_ON(thread->return_error != BR_OK);     if (in_reply_to) {         thread->return_error = BR_TRANSACTION_COMPLETE;         binder_send_failed_reply(in_reply_to, return_error);     } else         thread->return_error = return_error; }

binder_thread_write関数は次のとおりです.
int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
            void __user *buffer, int size, signed long *consumed)
{
    uint32_t cmd;
    void __user *ptr = buffer + *consumed;
    void __user *end = buffer + size;

    while (ptr return_error == BR_OK) {
        if (get_user(cmd, (uint32_t __user *)ptr))
            return -EFAULT;
        ptr += sizeof(uint32_t);
        if (_IOC_NR(cmd) stats.bc[_IOC_NR(cmd)]++;
            thread->stats.bc[_IOC_NR(cmd)]++;
        }
        switch (cmd) {
        /*        */
        case BC_TRANSACTION:
        case BC_REPLY: {
            struct binder_transaction_data tr;

            if (copy_from_user(&tr, ptr, sizeof(tr)))
                return -EFAULT;
            ptr += sizeof(tr);
            binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
            break;
        }
        /*        */
        default:
            printk(KERN_ERR "binder: %d:%d unknown command %d
",                    proc->pid, thread->pid, cmd);             return -EINVAL;         }         *consumed = ptr - buffer;     }     return 0; }

binder_thread_read関数は次のとおりです.
static int binder_thread_read(struct binder_proc *proc,
                  struct binder_thread *thread,
                  void  __user *buffer, int size,
                  signed long *consumed, int non_block)
{
    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 return_error2 != BR_OK) {
            if (put_user(thread->return_error2, (uint32_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(uint32_t);
            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);
        thread->return_error = BR_OK;
        goto done;
    }


    thread->looper |= BINDER_LOOPER_STATE_WAITING;
    if (wait_for_proc_work)
        proc->ready_threads++;

    binder_unlock(__func__);

    if (wait_for_proc_work) {
        if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
                    BINDER_LOOPER_STATE_ENTERED))) {
            binder_user_error("binder: %d:%d ERROR: Thread waiting "
                "for process work before calling BC_REGISTER_"
                "LOOPER or BC_ENTER_LOOPER (state %x)
",                 proc->pid, thread->pid, thread->looper);             wait_event_interruptible(binder_user_error_wait,                          binder_stop_on_user_error default_priority);         if (non_block) {             if (!binder_has_proc_work(proc, thread))                 ret = -EAGAIN;         } else             ret = wait_event_interruptible_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_interruptible(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) {         uint32_t cmd;         struct binder_transaction_data tr;         struct binder_work *w;         struct binder_transaction *t = NULL;         if (!list_empty(&thread->todo))             w = list_first_entry(&thread->todo, struct binder_work, entry);         else if (!list_empty(&proc->todo) && wait_for_proc_work)             w = list_first_entry(&proc->todo, struct binder_work, entry);         else {             if (ptr - buffer == 4 && !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) /* no data added */                 goto retry;             break;         }         if (end - ptr type) {         case BINDER_WORK_TRANSACTION: {             t = container_of(w, struct binder_transaction, work);         } break;         case BINDER_WORK_TRANSACTION_COMPLETE: {             cmd = BR_TRANSACTION_COMPLETE;             if (put_user(cmd, (uint32_t __user *)ptr))                 return -EFAULT;             ptr += sizeof(uint32_t);             binder_stat_br(proc, thread, cmd);             binder_debug(BINDER_DEBUG_TRANSACTION_COMPLETE,                      "binder: %d:%d BR_TRANSACTION_COMPLETE
",                      proc->pid, thread->pid);             list_del(&w->entry);             kfree(w);             binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);         } break;         case BINDER_WORK_NODE: {             struct binder_node *node = container_of(w, struct binder_node, work);             uint32_t cmd = BR_NOOP;             const char *cmd_name;             int strong = node->internal_strong_refs || node->local_strong_refs;             int weak = !hlist_empty(&node->refs) || node->local_weak_refs || strong;             if (weak && !node->has_weak_ref) {                 cmd = BR_INCREFS;                 cmd_name = "BR_INCREFS";                 node->has_weak_ref = 1;                 node->pending_weak_ref = 1;                 node->local_weak_refs++;             } else if (strong && !node->has_strong_ref) {                 cmd = BR_ACQUIRE;                 cmd_name = "BR_ACQUIRE";                 node->has_strong_ref = 1;                 node->pending_strong_ref = 1;                 node->local_strong_refs++;             } else if (!strong && node->has_strong_ref) {                 cmd = BR_RELEASE;                 cmd_name = "BR_RELEASE";                 node->has_strong_ref = 0;             } else if (!weak && node->has_weak_ref) {                 cmd = BR_DECREFS;                 cmd_name = "BR_DECREFS";                 node->has_weak_ref = 0;             }             if (cmd != BR_NOOP) {                 if (put_user(cmd, (uint32_t __user *)ptr))                     return -EFAULT;                 ptr += sizeof(uint32_t);                 if (put_user(node->ptr, (void * __user *)ptr))                     return -EFAULT;                 ptr += sizeof(void *);                 if (put_user(node->cookie, (void * __user *)ptr))                     return -EFAULT;                 ptr += sizeof(void *);                 binder_stat_br(proc, thread, cmd);                 binder_debug(BINDER_DEBUG_USER_REFS,                          "binder: %d:%d %s %d u%p c%p
",                          proc->pid, thread->pid, cmd_name, node->debug_id, node->ptr, node->cookie);             } else {                 list_del_init(&w->entry);                 if (!weak && !strong) {                     binder_debug(BINDER_DEBUG_INTERNAL_REFS,                              "binder: %d:%d node %d u%p c%p deleted
",                              proc->pid, thread->pid, node->debug_id,                              node->ptr, node->cookie);                     rb_erase(&node->rb_node, &proc->nodes);                     kfree(node);                     binder_stats_deleted(BINDER_STAT_NODE);                 } else {                     binder_debug(BINDER_DEBUG_INTERNAL_REFS,                              "binder: %d:%d node %d u%p c%p state unchanged
",                              proc->pid, thread->pid, node->debug_id, node->ptr,                              node->cookie);                 }             }         } break;         case BINDER_WORK_DEAD_BINDER:         case BINDER_WORK_DEAD_BINDER_AND_CLEAR:         case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {             struct binder_ref_death *death;             uint32_t cmd;             death = container_of(w, struct binder_ref_death, work);             if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION)                 cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE;             else                 cmd = BR_DEAD_BINDER;             if (put_user(cmd, (uint32_t __user *)ptr))                 return -EFAULT;             ptr += sizeof(uint32_t);             if (put_user(death->cookie, (void * __user *)ptr))                 return -EFAULT;             ptr += sizeof(void *);             binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION,                      "binder: %d:%d %s %p
",                       proc->pid, thread->pid,                       cmd == BR_DEAD_BINDER ?                       "BR_DEAD_BINDER" :                       "BR_CLEAR_DEATH_NOTIFICATION_DONE",                       death->cookie);             if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) {                 list_del(&w->entry);                 kfree(death);                 binder_stats_deleted(BINDER_STAT_DEATH);             } else                 list_move(&w->entry, &proc->delivered_death);             if (cmd == BR_DEAD_BINDER)                 goto done; /* DEAD_BINDER notifications can cause transactions */         } break;         }         if (!t)             continue;         BUG_ON(t->buffer == NULL);         if (t->buffer->target_node) {             struct binder_node *target_node = t->buffer->target_node;             tr.target.ptr = target_node->ptr;             tr.cookie =  target_node->cookie;             t->saved_priority = task_nice(current);             if (t->priority min_priority &&                 !(t->flags & TF_ONE_WAY))                 binder_set_nice(t->priority);             else if (!(t->flags & TF_ONE_WAY) ||                  t->saved_priority > target_node->min_priority)                 binder_set_nice(target_node->min_priority);             cmd = BR_TRANSACTION;         } else {             tr.target.ptr = NULL;             tr.cookie = NULL;             cmd = BR_REPLY;         }         tr.code = t->code;         tr.flags = t->flags;         tr.sender_euid = t->sender_euid;         if (t->from) {             struct task_struct *sender = t->from->proc->tsk;             tr.sender_pid = task_tgid_nr_ns(sender,                             current->nsproxy->pid_ns);         } else {             tr.sender_pid = 0;         }         tr.data_size = t->buffer->data_size;         tr.offsets_size = t->buffer->offsets_size;         tr.data.ptr.buffer = (void *)t->buffer->data +                     proc->user_buffer_offset;         tr.data.ptr.offsets = tr.data.ptr.buffer +                     ALIGN(t->buffer->data_size,                         sizeof(void *));         if (put_user(cmd, (uint32_t __user *)ptr))             return -EFAULT;         ptr += sizeof(uint32_t);         if (copy_to_user(ptr, &tr, sizeof(tr)))             return -EFAULT;         ptr += sizeof(tr);         binder_stat_br(proc, thread, cmd);         binder_debug(BINDER_DEBUG_TRANSACTION,                  "binder: %d:%d %s %d %d:%d, cmd %d"                  "size %zd-%zd ptr %p-%p
",                  proc->pid, thread->pid,                  (cmd == BR_TRANSACTION) ? "BR_TRANSACTION" :                  "BR_REPLY",                  t->debug_id, t->from ? t->from->proc->pid : 0,                  t->from ? t->from->pid : 0, cmd,                  t->buffer->data_size, t->buffer->offsets_size,                  tr.data.ptr.buffer, tr.data.ptr.offsets);         list_del(&t->work.entry);         t->buffer->allow_user_free = 1;         if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {             t->to_parent = thread->transaction_stack;             t->to_thread = thread;             thread->transaction_stack = t;         } else {             t->buffer->transaction = NULL;             kfree(t);             binder_stats_deleted(BINDER_STAT_TRANSACTION);         }         break;     } done:     *consumed = ptr - buffer;     if (proc->requested_threads + proc->ready_threads == 0 &&         proc->requested_threads_started max_threads &&         (thread->looper & (BINDER_LOOPER_STATE_REGISTERED |          BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */          /*spawn a new thread if we leave this out */) {         proc->requested_threads++;         binder_debug(BINDER_DEBUG_THREADS,                  "binder: %d:%d BR_SPAWN_LOOPER
",                  proc->pid, thread->pid);         if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))             return -EFAULT;     }     return 0; }

前にbinder transaction stackメカニズムを解析し,次にtransaction stackメカニズムの双方向サービスを見る.
このとき,P 1プロセスがS 1サービスを提供する場合,スレッドはt 1,t 1′である.表示;P 2プロセスはS 2サービスを提供し、スレッドはt 2,t 2'...表示;P 3プロセスはS 3サービスを提供し、スレッドはt 3,t 3′...表示;t 1=>t 2=>t 3とすると、t 3は誰にデータを送りますか?ではt 3はP 1のbinder_にデータを置くproc.todoチェーンテーブルは、P 1に新しいスレッドt 1′を用いて処理させるか、それともt 1のbinder_にデータを置くか.thread.todoチェーンテーブルは、t 1に処理させますか?
プロセスAからプロセスBへ:
            1. プロセスA送信BC_TRANSACTION、TRはfrom_を通過parent入桟;
            2. プロセスB返信BR_TRANSACTION,TR通過to_parent入桟;
            3. プロセスB送信BC_REPLY,TR通過to_parentはスタックに入り、TRはfrom_を通過するparent出桟;
            4. プロセスA返信BR_REPLY;
t 1=>t 2:t 1から.sp -> TR1.from_parent -> NULL;
            TR1.from = t1, TR1.to_proc = P2;
t 2=>t 3:t 2から.sp -> TR1.to_parent -> NULL;t2.sp -> TR2.from_parent -> TR1.to_parent -> NULL;
            TR1.from = t1, TR1.to_proc = P2;
t 3=>?:t3.sp -> TR2.to_parent -> NULL;まずbinderを分析しますtransaction関数の次のコード
while (tmp) {
    if (tmp->from && tmp->from->proc == target_proc)
        target_thread = tmp->from;
    tmp = tmp->from_parent;
}

            a. TR2 -> from = t2 -> proc = P2;
            b. tmp = TR1
            c. TR1 -> from = t1 -> proc = P1;target_thread = t1.
ここからt 3がt 1であることが分かるのでTR 2.from_parent -> TR1.from_parent -> NULL;t3.sp -> TR3.from_parent -> TR2.to_parent -> NULL;
ではt 1はt 3のBR_を受け取るTRANSACTION,t1.sp -> TR3.to_parent -> TR1.from_parent -> NULL;
t 1発BC_REPLY,t1.sp -> TR1.from_parent->NULLスタック;t3.sp -> TR2.to_parent->NULLスタック;
t 3 BRを受け取るREPLY,処理TR 2,発行BC_REPLY.t3.sp -> TR2.to_parent -> NULL;t 3から.spでは、TR 2,TR 2を取り出す.from=t 2なので、t 2に返信する.t3.sp->NULLスタック、t 2.sp -> TR1.to_parent -> NULL.
t 2受信BR_REPLY,処理TR 1,BR_を出すREPLY.t 2から.spでスタックトップを取り出すTR 1.from=t 1なので、t 1に返信する.t2.sp = NULL,t1.sp = NULL;
t 1受信BR_REPLY、処理完了.
        
ではbinder_serverのマルチスレッドがどのように動作しているか、clientが要求し、serverがサービスを提供します.サーバが忙しくて来られない場合は、マルチスレッドを作成します.
            1. 駆動は「忙しくて手が回らない」かどうかを判断する.
            2. ドライバはAPPに要求し、新しいスレッドを作成します.
            3. APPは新しいスレッドを作成します.
コードは次のとおりです.
if (proc->requested_threads + proc->ready_threads == 0 &&
    proc->requested_threads_started max_threads &&
    (thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
    BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */
     /*spawn a new thread if we leave this out */) {
    proc->requested_threads++;
    binder_debug(BINDER_DEBUG_THREADS,
         "binder: %d:%d BR_SPAWN_LOOPER
",          proc->pid, thread->pid);     if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))         return -EFAULT; }

ドライバがAPPに「新規スレッド作成要求」を発行する条件:
        1. proc->requested_treads = 0;未処理の新規スレッド要求=0
        2. proc->requesteds=0、アイドルスレッド数0
        3. 開始スレッド数では、アプリはどう書きますか.
        1. max_の設定threads
        2. BRを受け取るSPAWN_LOOPER後、新しいスレッドを作成
        3. 新しいスレッド発行ioctl:BC_REGISTER_LOOPER
        4. メインスレッドのように、read driver、処理のループに入ります.