AndroidシステムのBinderサブシステム(下)
38827 ワード
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つのプロセスのインタラクティブなプロセスが完了しました.
関連コードは次のとおりです.
binder_call関数は次のとおりです.
binder_Transaction関数は次のとおりです.
binder_thread_write関数は次のとおりです.
binder_thread_read関数は次のとおりです.
前に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関数の次のコード
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は新しいスレッドを作成します.
コードは次のとおりです.
ドライバが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、処理のループに入ります.
プロセス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、処理のループに入ります.