Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Binder Subsystem of Android system (part two)

2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)06/02 Report--

After analyzing the binder-driven framework and how it registers, acquires, and consumes services, let's take a look at the binder transaction stack mechanism.

We also mentioned earlier that process A sends data to process B. the mode is that process A sends a BC_TRANSACTION to process B first, and process B replies to process A with a BR_TRANSACTION indicating that the data has been received. Then process B sends a BC_REPLY to process A to send the relevant data that process B needs to reply to process A. after receiving the data, process A sends a BR_REPLY to process B indicating that the reply data has been received. They interact with data through drivers, only these four modes involve two processes, and the other modes are just app and driver interactions for changing / reporting status.

So there are two questions. Who do you need to send it to? Who should I give it back to? To answer these two questions, let's first look at how test_client (A) and test_server (B) work. According to our previous code analysis, we know that the binder_call function is called in the svcmgr_publish registration service sequence, while the cmd is BC_TRANSACTION when the parameter is constructed in the binder_call function. So test_client sends a BC_TRANSACTION to test_server first. And construct the data related to write, and finally call ioctl to send BINDER_WRITE_READ. So let's go into the binder_ioctl function. Find the BINDER_WRITE_READ command and enter the binder_thread_write function according to the write-related data of the previous construction. Find the BC_TRANSACTION command in the binder_thread_write function, and we see a binder_transaction action in it.

Let's go into the binder_transaction function and see how it is done:

a. At first, it was not "two-way transmission". So, the data will be placed in the binder_proc.todo linked list of test_server

b. Enter the A stack, in test_client.binder_thread.transaction_stack: .from = test_client, .to _ proc = test_server, .to _ thread= test_server

c. The data is put into the test_server.binder_proc.todo linked list to wake up the thread on the test_server.binder_proc.wait.

For test_server, if you receive a BR_TRANSACTION, do something about it in the binder_thread_read function:

a. Take data from the test_server.binder_proc.todo linked list and process it

b. Enter the B stack, in test_server.binder_thread.transaction_stack: .from = test_client, .to _ proc = test_server, .to _ thread= test_server

For the same binder_transaction, it is placed on the sender's stack through .from _ parent and on the receiver's stack via .to _ parent.

Then B sends a BC_REPLY to A, which is also done in the binder_transaction function:

a. Take the relevant data (.from, .to _ proc, .to _ thread) in test_server.binder_thread.transaction_stack from the stack and reply to test_client from .from

b. Test_server, that is, test_server.binder_thread.transaction_stack = NULL

c. Data copy_from_user to test_client

d. Test_client, that is, test_client.binder_thread.transaction_stack = NULL

e. Put it into the todo linked list and wake up.

Finally, process A receives the data and sends a BR_REPLY to process B. Return to user space, and this piece does not involve stack operations.

At this point, the interaction between the two processes is complete.

The relevant code involved above is as follows:

Int svcmgr_publish (struct binder_state * bs, uint32_t target, const char * name, void * ptr) {int status; unsigned iodata [512 msg 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)) / / Registration Service return-1; status = bio_get_uint32 (& reply); binder_done (bs, & msg, & reply); return status;}

The binder_call function is as follows

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\ n"); goto fail;} / / Construction parameters 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); / / call ioctl to send data if (res

< 0) { fprintf(stderr,"binder: ioctl failed (%s)\n", strerror(errno)); goto fail; } res = binder_parse(bs, reply, (uintptr_t) readbuf, bwr.read_consumed, 0); if (res == 0) return 0; if (res < 0) goto fail; }fail: memset(reply, 0, sizeof(*reply)); reply->

Flags | = BIO_F_IOERROR; return-1;}

The binder_transaction function is as follows

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:% dvv% d got reply transaction"with no transaction stack\ n", 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:% DGV% d got reply transaction"with bad transaction stack,"transaction% d has target% DV% d\ n", 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:% DV% d got reply transaction"with bad target transaction stack% d,"expected% d\ n", 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 got"transaction to invalid handle\ n", 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)

< 0) { return_error = BR_FAILED_REPLY; goto err_invalid_target_handle; } if (!(tr->

Flags & TF_ONE_WAY) & & thread- > transaction_stack) {struct binder_transaction * tmp; tmp = thread- > transaction_stack If (tmp- > to_thread! = thread) {binder_user_error ("binder:% DGV% d got new"transaction with bad transaction stack", transaction% d has target% DV% d\ n ", 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 BC_REPLY% d->% d,"data% Pluto% p size% zd-%zd\ n", 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:% BC_TRANSACTION% d->"% d-node% d, data% PMI% p size% zd-%zd\ n ", 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 invalid proc\ n", 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 got transaction with invalid"data ptr\ n", 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 got transaction with invalid"offsets ptr\ n", 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:% dvv% d got transaction with"invalid offsets size,% zd\ n", 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

< off_end; offp++) { struct flat_binder_object *fp; if (*offp >

T-> buffer- > data_size-sizeof (* fp) | | * offp

< off_min || t->

Buffer- > data_size

< sizeof(*fp) || !IS_ALIGNED(*offp, sizeof(void *))) { binder_user_error("%d:%d got transaction with invalid offset, %zd (min %zd, max %zd)\n", proc->

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:% DVR% d sending u% p"node% d, cookie mismatch% p! =% p\ n", 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% du% p-> ref% d desc% d\ n", 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:% dvv% d got"transaction with invalid"handle,% ld\ n", 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% du% p\ n", 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)\ n", 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 got reply with fd,% ld, but target does not allow fds\ n", 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:% DVR% d got transaction with fd,% ld, but target does not allow fds\ n", 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 got transaction with invalid fd,% ld\ n", 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)

< 0) { fput(file); return_error = BR_FAILED_REPLY; goto err_get_unused_fd_failed; } target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC); if (target_fd < 0) { fput(file); return_error = BR_FAILED_REPLY; goto err_get_unused_fd_failed; } task_fd_install(target_proc, target_fd, file); binder_debug(BINDER_DEBUG_TRANSACTION, " fd %ld ->

% d\ n ", fp- > handle, target_fd); / * TODO: fput? * / fp- > handle = target_fd;} break; default: binder_user_error (" binder:% d got transactio% got transactio "" n with invalid object type,% lx\ n ", 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 transaction failed% d, size% zd-%zd\ n", 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;}

The binder_thread_write function is as follows

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

< end && thread->

Return_error = = BR_OK) {if (get_user (cmd, (uint32_t _ user *) ptr)) return-EFAULT; ptr + = sizeof (uint32_t); if (_ IOC_NR (cmd))

< ARRAY_SIZE(binder_stats.bc)) { binder_stats.bc[_IOC_NR(cmd)]++; proc->

Stats.bc [_ IOC_NR (cmd)] + +; thread- > stats.bc [_ IOC_NR (cmd)] + +;} switch (cmd) {/ * omit extraneous code * / 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 } / * omit extraneous codes * / default: printk (KERN_ERR "binder:% d unknown command% d\ n", proc- > pid, thread- > pid, cmd); return-EINVAL;} * consumed = ptr-buffer;} return 0;}

The binder_thread_read function is as follows

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

< end) { if (thread->

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 ERROR: Thread waiting"for process work before calling BC_REGISTER_"LOOPER or BC_ENTER_LOOPER (state% x)\ n" Proc- > pid, thread- > pid, thread- > looper) Wait_event_interruptible (binder_user_error_wait, binder_stop_on_user_error

< 2); } binder_set_nice(proc->

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

< sizeof(tr) + 4) break; switch (w->

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 BR_TRANSACTION_COMPLETE\ n", 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:% DGV% d% s% du% p c% p\ n", 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:% DVR% d node% du% p c% p deleted\ n", 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:% DVR% d node% du% p c% p state unchanged\ n", 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:% dvv% d% s% p\ n", 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)

< target_node->

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:% dvv% d% s% d% dvv% d, cmd% d"size% zd-%zd ptr% PMI% p\ n", 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

< proc->

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 BR_SPAWN_LOOPER\ n", proc- > pid, thread- > pid); if (put_user (BR_SPAWN_LOOPER, (uint32_t _ user *) buffer) return-EFAULT;} return 0;}

Before we analyze the binder transaction stack mechanism, let's take a look at the transaction stack mechanism two-way service.

At this point, if there are three processes: P1 process provides S1 service, thread uses T1, T1'. The P2 process provides S2 services, and threads use T2, T2'. The P3 process provides S3 services, and threads use T3, T3'. If T1 = = > T2 = = > T3, who does T3 send the data to? So does T3 put the data in P1's binder_proc.todo list and let P1 use the new thread T1'to process it, or put the data in T1's binder_thread.todo list and let T1 handle it?

From process A to process B:

1. Process A sends BC_TRANSACTION,TR into the stack via from_parent

two。 Process B replies to BR_TRANSACTION,TR to enter the stack via to_parent

3. Process B sends BC_REPLY,TR into the stack through to_parent, and TR passes through from_parent to exit the stack.

4. Process A replies to BR_REPLY

From T1 = > T2: t1.sp-> TR1.from_parent-> NULL

TR1.from = T1, TR1.to_proc = P2

From T2 = > T3: t2.sp-> TR1.to_parent-> NULL;t2.sp-> TR2.from_parent-> TR1.to_parent-> NULL

TR1.from = T1, TR1.to_proc = P2

From T3 = >? : t3.sp-> TR2.to_parent-> NULL; Let's first analyze the following code in the binder_transaction function

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 target thread = T1.

From here we can know that T3 is sent to T1, so TR2.from_parent-> TR1.from_parent-> NULL;t3.sp-> TR3.from_parent-> TR2.to_parent-> NULL

So T1 receives BR_TRANSACTION,t1.sp-> TR3.to_parent-> TR1.from_parent-> NULL of T3.

T1 issues BC_REPLY,t1.sp-> TR1.from_parent-> NULL into the stack; t3.sp-> TR2.to_parent-> NULL out of the stack

T3 receives the BR_REPLY, processes the TR2, and issues the BC_REPLY. T3.sp-> TR2.to_parent-> NULL; from t3.sp, take out TR2,TR2.from = T2, so reply to t2bot t3.sp-> NULL to stack, t2.sp-> TR1.to_parent-> NULL.

T2 receives the BR_REPLY, processes the TR1, and issues the BR_REPLY. Take out the top of the stack from the t2.sp, TR1.from = T1, so reply T1 to T2.sp = NULL,t1.sp = NULL

T1 received BR_REPLY, processing complete.

So how does the multithreading of binder_server work? client makes a request and server provides services. If server is too busy, create multithreading:

1. Drive to judge whether you are "too busy"

two。 Driver sends a request to APP to create a new thread

3. APP creates a new thread.

The code is as follows:

If (proc- > requested_threads + proc- > ready_threads = = 0 & & proc- > requested_threads_started

< proc->

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 BR_SPAWN_LOOPER\ n", proc- > pid, thread- > pid) If (put_user (BR_SPAWN_LOOPER, (uint32_t _ user *) buffer)) return-EFAULT;}

The condition under which the driver issues a "request to create a new thread" to APP:

1. Proc- > requested_treads = 0; "unprocessed new thread requests" = 0

2. Proc- > requesteds = 0, and the number of idle threads is 0

3. Number of threads started < max_threads

So how to write APP?

1. Set up max_threads

two。 After receiving the BR_SPAWN_LOOPER, create a new thread

3. New thread issues ioctl:BC_REGISTER_LOOPER

4. Like the main thread, enter a loop: read driver, processing.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report