Android 시스템의 Binder 서브시스템(아래)

위에서 binder 구동의 구조와 서비스를 어떻게 등록하고 서비스를 얻으며 사용하는지 분석한 다음에 bindertransaction stack 메커니즘을 살펴본다.
앞에서 언급한 바와 같이 프로세스 A가 프로세스 B에게 서로 데이터를 보내는 것은 프로세스 A가 먼저 BC 를 보내는 것이다.TRANSACTION은 프로세스 B에, 프로세스 B는 프로세스 A에 BRTRANSACTION은 데이터가 전달되었음을 나타냅니다.그리고 프로세스 B가 프로세스 A에 BC 를 보냅니다.REPLY는 프로세스 B가 프로세스 A에 회신해야 하는 관련 데이터를 보내고, 프로세스 A는 데이터를 받은 후 프로세스 B에게 BR 를 보냅니다.REPLY는 응답한 데이터가 전달되었음을 나타냅니다.그것들은 구동을 통해 데이터의 상호작용을 하는 것이다. 이 네 가지 모델만 두 프로세스와 관련되고 다른 모델은 app와 구동의 상호작용으로 상태를 바꾸거나 보고하는 데 사용된다.
그럼 두 가지 질문이 있는데 누구한테 보내야 돼요?누구한테 드릴까요?이 두 문제에 대답하려면testclient(A) 및 test서버 (B) 는 어떻게 작동합니까?우리의 이전 코드 분석에 의하면 알 수 있듯이:svcmgrpublish 등록 서비스 시퀀스 호출bindercall 함수call 함수에서 매개변수를 구성할 때 cmd가 BCTRANSACTION.테스트client에서 먼저 BC 전송TRANSACTION이 테스트에server.또한 write와 관련된 데이터를 구성하여 마지막으로 ioctl을 호출하여 BINDERWRITE_READ.그럼 우리 Binder 들어가자.ioctl 함수에서 봐요.BINDER 찾기WRITE_READ 명령, 이전에 만들어진 write와 관련된 데이터에 따라 Binder 로 들어가는 것을 알 수 있습니다thread_write 함수에서binderthread_write 함수에서 BC 찾기TRANSACTION 명령, 그 안에 binder가 만들어진 것을 보았습니다transaction의 동작.
저희가 Binder에 들어가고 있어요.transaction 함수에서 어떻게 완성되었는지 보십시오:
a. 처음에는 양방향 전송이 아닙니다.그래서 데이터는 테스트에 저장됩니다서버의 binderproc.todo 체인 테이블에서;
b. A스택에 들어가기,testclient.binder_thread.transaction_stack 중:.from = test_client、.to_proc = test_server、.to_thread= test_server;
c. 데이터를 테스트에 넣기server.binder_proc.todo 체인 테이블에서 테스트 깨우기server.binder_proc.wait의 라인입니다.
테스트의 경우서버에 BR 을 받았습니다.TRANSACTION, Binderthread_read 함수에서 관련 작업을 수행하려면 다음과 같이 하십시오.
a. 테스트에서server.binder_proc.todo 체인에서 데이터를 꺼내 처리하기;
b. B 스택에 들어가기,testserver.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도 bindertransaction 함수에서 완료된 내용:
a. 스택에서 test 제거server.binder_thread.transaction_stack의 관련 데이터(.from,.to proc,.to thread)from 테스트 응답에 대한 알림client;
b. 아웃(test server)(test)server.binder_thread.transaction_stack = NULL;
c. 데이터 copyfrom_user to testclient;
d. 아웃(test client)(test)client.binder_thread.transaction_stack = NULL;
e. todo 체인 시계를 넣고 깨운다.
마지막 프로세스 A 데이터를 받은 후 프로세스 B에 BR 보내기REPLY.사용자 공간으로 돌아가면 창고의 조작과 관련이 없습니다.
이로써 두 프로세스의 상호작용 과정이 완성되었다.
위에서 설명한 관련 코드는 다음과 같습니다.
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; }

앞에서 우리가 분석한 것은bindertransactionstack 메커니즘이고 다음은transactionstack 메커니즘의 양방향 서비스를 보겠습니다.
이 때 세 개의 프로세스가 있다면: P1 프로세스는 S1 서비스를 제공하고, 라인은 t1, t1 '...나타내다P2 프로세스는 S2 서비스를 제공합니다. 스레드는 t2, t2'...나타내다P3 프로세스는 S3 서비스를 제공합니다. 스레드는 t3, t3'...나타내다t1==>t2==>t3을 가정하면 t3은 누구에게 데이터를 보냅니까?그럼 t3은 데이터를 P1의binder 에 넣을게요.proc.todo 체인 테이블, P1이 새로운 스레드 t1을 사용하여 처리하든지 아니면 데이터를 t1의binder 에 넣든지thread.todo 체인 시계, t1이 처리할까요?
프로세스 A에서 프로세스 B로
            1. 프로세스 A 전송 BCTRANSACTION, TR 통과fromparent 입고;
            2. 프로세스 B 회신 BRTRANSACTION, TR 통과 toparent 입고;
            3. 프로세스 B 전송 BCREPLY, TR 통과 toparent 입고, TR 통과fromparent 출고;
            4. 프로세스 A 회신 BRREPLY;
t1==>t2:t1.sp -> TR1.from_parent -> NULL;
            TR1.from = t1, TR1.to_proc = P2;
t2 ==> t3:t2.sp -> TR1.to_parent -> NULL;t2.sp -> TR2.from_parent -> TR1.to_parent -> NULL;
            TR1.from = t1, TR1.to_proc = P2;
t3 ==>?: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.
여기서 우리는 t3이 t1에게 보낸 것을 알 수 있기 때문에TR2.from_parent -> TR1.from_parent -> NULL;t3.sp -> TR3.from_parent -> TR2.to_parent -> NULL;
그럼 t1은 t3의 BR을 받고...TRANSACTION,t1.sp -> TR3.to_parent -> TR1.from_parent -> NULL;
t1 전송 BCREPLY,t1.sp -> TR1.from_parent -> NULL 입고;t3.sp -> TR2.to_parent -> NULL 출고;
t3 수령 BRREPLY, TR2 처리, BC 전송REPLY.t3.sp -> TR2.to_parent -> NULL;에서sp에서 TR2, TR2 체크 아웃from = t2로 답장하기;t3.sp-> NULL 출고, t2.sp -> TR1.to_parent -> NULL.
t2 BR 수신REPLY, TR1 처리, BR 보내기REPLY.t2.sp에서 스택 상단 제거, TR1.from = t1이므로 t1로 답장합니다.t2.sp = NULL,t1.sp = NULL;
t1 BR 수신REPLY, 처리 완료.
        
그럼 binder서버의 다중 루틴이 어떻게 작동하는지,client에서 요청을 보내고, 서버에서 서비스를 제공합니다.서버가 바쁠 때 다중 루틴을 만듭니다:
            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:BCREGISTER_LOOPER
        4. 주 라인처럼 순환에 들어갑니다:read driver, 처리.

좋은 웹페이지 즐겨찾기