Andorid Binder inter-process communication --- start ServiceManager

Source: Internet
Author: User

This article is based on the source code Scenario Analysis of the Android system, by Luo shengyang.

I ,~ /Android/frameworks/base/cmd/servicemanager

----- Binder. h

----- Binder. c

----- Service_manager.c

~ /Android // kernel/goldfish/drivers/staging/android

----- Binder. c

----- Binder. h


Ii. Source Code Analysis

1. Run the command from main of service_manager.c.

----~ /Android/frameworks/base/cmd/servicemanager/service_manager.c

Int main (int argc, char ** argv) {struct binder_state * bs; void * svcmgr = BINDER_SERVICE_MANAGER; bs = binder_open (128*1024); if (binder_become_context_manager (bs )) {LOGE ("cannot become context manager (% s) \ n", strerror (errno); return-1 ;}svcmgr_handle = svcmgr; // note that svcmgr_handle is different from svcmgr_handler below. svcmgr_handle is a global variable. Currently, it is a null function pointer (void *) 0) binder_loop (bs, svcmgr_handler ); // svcmgr_handler is a function pointer pointing to the specific function return 0 ;}
----~ /Android/frameworks/base/cmd/servicemanager/binder. c

struct binder_state{           int fd;    void *mapped;    unsigned mapsize;};
----~ /Android/frameworks/base/cmd/servicemanager/binder. h

#define BINDER_SERVICE_MANAGER ((void*) 0)
The ServiceManager startup process consists of three steps: the first is to call the function binder_open to open the device file/dev/binder and map it to the address space of the current process; the second is to call binder_become_context_manager to register itself as the context manager of the communication mechanism between Binder processes. The third step is to call the binder_loop function to cyclically wait for and process the communication requirements of Client processes.


2. Open and map the Binder Device File

----~ /Android/frameworks/base/cmd/servicemanager/binder. c

Struct binder_state * binder_open (unsigned mapsize) {struct binder_state * bs; bs = malloc (sizeof (* bs); if (! Bs) {errno = ENOMEM; return 0;} bs-> fd = open ("/dev/binder", O_RDWR ); // The file descriptor is stored in fd. if (bs-> fd <0) {fprintf (stderr, "binder: cannot open device (% s) \ n ", strerror (errno); goto fail_open;} bs-> mapsize = mapsize; // 128*1024 bs-> mapped = mmap (NULL, mapsize, PROT_READ, MAP_PRIVATE, bs-> fd, 0); // map to the current process address space if (bs-> mapped = MAP_FAILED) {fprintf (stderr, "binder: cannot map device (% s) \ n ", strerror (errno); goto fail_map;}/* TODO: check version */return bs; fail_map: close (bs-> fd); fail_open: free (bs); return 0 ;}
Here, open ("/dev/binder", O_RDWR) maps to the binder driver binder_open method.

The binder_open method is located in ~ /Android/kernel/goldfish/drivers/staging/android/binder. c

Static int binder_open (struct inode * nodp, struct file * filp) {struct binder_proc * proc; if (binder_debug_mask & BINDER_DEBUG_OPEN_CLOSE) printk (KERN_INFO "binder_open: % d: % d \ n ", current-> group_leader-> pid, current-> pid); proc = kzarloc (sizeof (* proc), GFP_KERNEL ); // create the binder_proc struct if (proc = NULL) return-ENOMEM; get_task_struct (current); proc-> tsk = current; // initialize the INIT_LIST_HEAD (& proc-> todo); init_waitqueue_head (& proc-> wait); proc-> default_priority = task_nice (current); mutex_lock (& binder_lock ); binder_stats.obj_created [BINDER_STAT_PROC] ++; hlist_add_head (& proc-> proc_node, & binder_procs ); // Add the binder_proc struct proc to a global hash queue binder_procs. proc-> pid = current-> group_leader-> pid; INIT_LIST_HEAD (& proc-> delivered_death ); filp-> private_data = proc; // Save the binder_proc struct in the filp member variable private_data mutex_unlock (& binder_lock); if (binder_proc_dir_entry_proc) {// If/proc/binder/proc directory char strbuf [11]; snprintf (strbuf, sizeof (strbuf), "% u", proc-> pid) exists ); remove_proc_entry (strbuf, binder_proc_dir_entry_proc); create_proc_read_entry (strbuf, S_IRUGO, binder_proc_dir_entry_proc, binder_read_proc_proc, proc ); // create a read-only file named process ID in the/proc/binder/proc directory} return 0 ;}
The binder_proc struct is created, and each parameter is initialized. The binder_proc struct proc is added to a global hash queue binder_procs. The Binder driver adds all processes that open the device file/dev/binder to the global hash queue binder_procs. Therefore, by traversing this hash queue, we can see how many processes are currently using the Binder inter-process communication mechanism. Then, save the binder_proc struct proc in the member variable private_data of the filp parameter. Finally, create a read-only file named process ID in the/proc/binder/proc directory and use the binder_read_proc_proc function as its file content reading function. By reading the file/proc/binder/proc/ To obtain the process. Binder thread pool, Binder object, Binder reference object, and kernel buffer.

Mmap (NULL, mapsize, PROT_READ, MAP_PRIVATE, bs-> fd, 0) maps to the binder driver binder_mmap method.


3. register as the Binder context Manager

---~ /Android/frameworks/base/cmd/servicemanager/binder. c

int binder_become_context_manager(struct binder_state *bs){       return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);}
The ioctl method maps to the binder driver binder_ioctl method.

Binder_ioctl is located in ~ /Android/kernel/goldfish/drivers/staging/android/binder. c

Static long binder_ioctl (struct file * filp, unsigned int cmd, unsigned long arg) {int ret; struct binder_proc * proc = filp-> private_data; // obtain the binder_proc struct binder_thread * thread created in open; unsigned int size = _ IOC_SIZE (cmd ); // command size void _ user * ubuf = (void _ user *) arg; // parameter address ......... mutex_lock (& binder_lock); thread = binder_get_thread (proc); // obtain or create a binder_thread struct if (thread = NULL) {ret =- ENOMEM; goto err;} switch (cmd) {...... case BINDER_SET_CONTEXT_MGR: if (binder_context_mgr_node! = NULL) {...} if (binder_context_mgr_uid! =-1 ){.........} elsebinder_context_mgr_uid = current-> cred-> euid; // valid IDbinder_context_mgr_node = binder_new_node (proc, NULL, NULL) of the initialization process ); // initialize a Binder object if (binder_context_mgr_node = NULL) {ret =-ENOMEM; goto err;} binder_context_mgr_node-> local_weak_refs ++; // strength pointer, binder_context_mgr_node-> local_strong_refs ++; binder_context_mgr_node-> has_strong_ref = 1; binder_context_mgr_node-> has_weak_ref = 1; break;...} ret = 0; err: if (thread) thread-> logoff & = ~ BINDER_LOOPER_STATE_NEED_RETURN; mutex_unlock (& binder_lock); ...... return ret ;}
Binder_get_thread implementation is as follows:

~ /Android/kernel/goldfish/drivers/staging/android/binder. c

Static struct binder_thread * binder_get_thread (struct binder_proc * proc) {struct binder_thread * thread = NULL; struct rb_node * parent = NULL; struct rb_node ** p = & proc-> threads. rb_node; while (* p) {// based on the current master thread pid, find whether the binder_thread struct parent = * p; thread = rb_entry (parent, struct binder_thread, rb_node); if (current-> pid <thread-> pid) p = & (* p)-> rb_left; else if (current-> pid> thread-> pid) p = & (* p)-> rb_right; elsebreak;} if (* p = NULL) {// if thread = kzarloc (sizeof (* thread) is not found ), GFP_KERNEL); // allocate the binder_thread struct if (thread = NULL) return NULL; binder_stats.obj_created [BINDER_STAT_THREAD] ++; thread-> proc = proc; // initialize the variables thread-> pid = current-> pid; init_waitqueue_head (& thread-> wait); INIT_LIST_HEAD (& thread-> todo ); rb_link_node (& thread-> rb_node, parent, p ); // insert thread-> rb_node to the red-black tree maintained by proc-> threads (rb_insert_color (& thread-> rb_node, & proc-> threads) based on the pid ); thread-> looper | = BINDER_LOOPER_STATE_NEED_RETURN; thread-> return_error = BR_ OK; thread-> return_error2 = BR_ OK;} return thread ;}
A process has many threads, so thread-> rb_node is a node in the red black number maintained by proc-> threads. Thread inserts rb_node into the corresponding node of the red/black tree maintained by proc-> threads according to the pid size. So first, based on the current master thread pid, find whether the binder_thread struct has been allocated. If not, allocate the binder_thread struct, initialize various variables, and insert thread-> rb_node to the red/black tree maintained by proc-> threads according to the pid. Set the loginstatus device to BINDER_LOOPER_STATE_NEED_RETURN, indicating that the thread needs to return to the user space immediately after completing the current operation, rather than processing inter-process communication requests.

The global variable binder_context_mgr_node is used to describe a Binder object. If its value is not NULL, it indicates that the Binder context manager has been registered. The global variable binder_context_mgr_uid is used to describe the valid user ID of a process. If the value of binder_context_mgr_uid is not-1, the context manager of the inter-process communication of the Binder has been registered. Currently, there is no Binder context manager for inter-process communication. Therefore, binder_context_mgr_uid and binder_context_mgr_node must be initialized.


Binder_context_mgr_node is implemented as follows:

-----~ /Android/kernel/goldfish/drivers/staging/android/binder. c

Static struct binder_node * binder_new_node (struct binder_proc * proc, void _ user * ptr, void _ user * cookie) {struct rb_node ** p = & proc-> nodes. rb_node; struct rb_node * parent = NULL; struct binder_node * node; while (* p) {// Based on the node ptr, find whether the binder_node struct parent = * p has been allocated; node = rb_entry (parent, struct binder_node, rb_node); if (ptr <node-> ptr) p = & (* p)-> rb_left; else if (ptr> node-> ptr) p = & (* p)-> rb_right; elsereturn NULL;} node = kzarloc (sizeof (* node), GFP_KERNEL ); // if not found, allocate the binder_node struct if (node = NULL) return NULL; binder_stats.obj_created [BINDER_STAT_NODE] ++; rb_link_node (& node-> rb_node, parent, p ); // insert node-> rb_node into proc-> nodes Based on ptr rb_insert_color (& node-> rb_node, & proc-> nodes); node-> debug_id = ++ binder_last_id; // initialize each variable node-> proc = proc; node-> ptr = ptr; // NULLnode-> cookie = cookie; // NULLnode-> work. type = BINDER_WORK_NODE; INIT_LIST_HEAD (& node-> work. entry); INIT_LIST_HEAD (& node-> async_todo); if (binder_debug_mask & BINDER_DEBUG_INTERNAL_REFS) printk (KERN_INFO "binder: % d: % d node % d u % p c % p created \ n ", proc-> pid, current-> pid, node-> debug_id, node-> ptr, node-> cookie); return node ;}
A process has many entity objects, so node-> rb_node is a node in the red/black number maintained by proc-> nodes. Node inserts rb_node into the corresponding node of the red/black tree maintained by proc-> threads according to the size of the ptr address. So first, based on the ptr of the current node, find whether the binder_node struct has been allocated. If not, allocate the binder_node struct, initialize various variables, and insert thread-> rb_node to the red/black tree maintained by proc-> threads Based on ptr.

Then, in binder_ioctl, The BINDER_LOOPER_STATE_NEED_RETURN status bit of thread-> logoff is cleared.


4. Wait cyclically for Client process requests

Return to the user space. The main function starts to execute binder_loop. The implementation is as follows:

---~ /Android/frameworks/base/cmd/servicemanager/binder. c

Void binder_loop (struct binder_state * bs, binder_handler func) {int res; struct binder_write_read bwr; unsigned readbuf [32]; bwr. write_size = 0; bwr. write_consumed = 0; bwr. write_buffer = 0; readbuf [0] = BC_ENTER_LOOPER; // write the BC_ENTER_LOOPER protocol to binder_write (bs, readbuf, sizeof (unsigned) in the buffer zone first )); // call binder_write to send it to the Binder driver for (;) {bwr. read_size = sizeof (readbuf); bwr. read_consumed = 0; B Wr. read_buffer = (unsigned) readbuf; res = ioctl (bs-> fd, BINDER_WRITE_READ, & bwr); // bwr. write_size is 0, bwr. read_size is not 0 if (res <0) {LOGE ("binder_loop: ioctl failed (% s) \ n", strerror (errno); break ;} res = binder_parse (bs, 0, readbuf, bwr. read_consumed, func); if (res = 0) {LOGE ("binder_loop: unexpected reply ?! \ N "); break;} if (res <0) {LOGE (" binder_loop: io error % d % s \ n ", res, strerror (errno )); break ;}}}
First, write the BC_ENTER_LOOPER Protocol into the buffer zone readbuf, and then call binder_write to send it to the Binder driver. The binder_write function is implemented as follows:

---~ /Android/frameworks/base/cmd/servicemanager/binder. c

Int binder_write (struct binder_state * bs, void * data, unsigned len) {struct binder_write_read bwr; int res; bwr. write_size = len; bwr. write_consumed = 0; bwr. write_buffer = (unsigned) data; bwr. read_size = 0; bwr. read_consumed = 0; bwr. read_buffer = 0; res = ioctl (bs-> fd, BINDER_WRITE_READ, & bwr); // The write_size of bwr is not 0, and read_size is 0 if (res <0) {fprintf (stderr, "binder_write: ioctl failed (% s) \ n", strerror (errno);} return res ;}
The ioctl method is also mapped to the binder driver binder_ioctl method.
Binder_ioctl is located in ~ /Android/kernel/goldfish/drivers/staging/android/binder. c

Static long binder_ioctl (struct file * filp, unsigned int cmd, unsigned long arg) {int ret; struct binder_proc * proc = filp-> private_data; struct binder_thread * thread; unsigned int size = _ IOC_SIZE (cmd); void _ user * ubuf = (void _ user *) arg ;......... mutex_lock (& binder_lock); thread = binder_get_thread (proc); // The Last retrieved thread. The logoff value is 0if (thread = NULL) {ret =-ENOMEM; goto err ;} switch (cmd) {// cmd indicates the BI passed above NDER_WRITE_READcase BINDER_WRITE_READ: {struct binder_write_read bwr; if (size! = Sizeof (struct binder_write_read) {ret =-EINVAL; goto err;} if (copy_from_user (& bwr, ubuf, sizeof (bwr ))) {// copy the binder_write_read struct passed in from the user space and save it in the variable bwr ret =-EFAULT; goto err ;}......... if (bwr. write_size> 0) {// bwr. write_size is greater than 0. Run ret = binder_thread_write (proc, thread, (void _ user *) bwr here. write_buffer, bwr. write_size, & bwr. write_consumed); if (ret <0) {bwr. read_consumed = 0; if (copy_to_user (Ubuf, & bwr, sizeof (bwr) ret =-EFAULT; goto err ;}} if (bwr. read_size> 0) {// bwr. read_size is equal to 0. Do not execute ret = binder_thread_read (proc, thread, (void _ user *) bwr. read_buffer, bwr. read_size, & bwr. read_consumed, filp-> f_flags & O_NONBLOCK); if (! List_empty (& proc-> todo) wake_up_interruptible (& proc-> wait); if (ret <0) {if (copy_to_user (ubuf, & bwr, sizeof (bwr ))) ret =-EFAULT; goto err ;}}........... if (copy_to_user (ubuf, & bwr, sizeof (bwr) {// returns the result to the user space bwrret =-EFAULT; goto err ;} break ;}.......... ret = 0; err: if (thread) thread-> logoff & = ~ BINDER_LOOPER_STATE_NEED_RETURN; // The logoff value is BINDER_LOOPER_STATE_ENTERED. Because the corresponding bit is already 0, the execution of this sentence has no effect. mutex_unlock (& binder_lock );........... return ret ;}
Because bwr. write_size is greater than 0, binder_thread_write is executed. The implementation is as follows:

Binder_ioctl is located in ~ /Android/kernel/goldfish/drivers/staging/android/binder. c

Intbinder_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; // start position void _ user * end = buffer + size; // end position while (ptr <end & thread-> return_error = BR_ OK) {if (get_user (cmd, (uint32_t _ user *) ptr )) // cmd is BC_ENTER_LOOPERreturn-EFAULT; ptr + = sizeof (uint32_t); // since there is only one cmd, ptr is already equal to end ............ switch (cmd ){........... case BC_ENTER_LOOPER :.............. if (thread-> logoff & BINDER_LOOPER_STATE_REGISTERED) {// at this time, logoff is 0 and thread-> logoff | = BINDER_LOOPER_STATE_INVALID; binder_user_error ("binder: % d ERROR: "" bc_enter_low.called after "" bc_register_loter\ n ", proc-> pid, thread-> pid);} thread-> logoff | = BINDER_LOOPER_STATE_ENTERED; // execute this write operation, the ultimate goal is to set the logoff to BINDER_LOOPER_STATE_ENTEREDbreak ;......... * consumed = ptr-buffer; // since there is only one cmd, consumed is size} return 0 ;}
Binder_ioctl is returned, and bwr. read_size is equal to 0. It is not executed, and the result is returned to the user space bwr.

Return to the user space and execute binder_loop. From the above, we can see that whether the driver performs read and write operations depends on the size of the user space write_size and read_size. In this case, write_size is 0 and read_size is not 0. The ioctl method is also mapped to the binder_ioctl method of the binder driver.
Binder_ioctl is located in ~ /Android/kernel/goldfish/drivers/staging/android/binder. c

Static long binder_ioctl (struct file * filp, unsigned int cmd, unsigned long arg) {int ret; struct binder_proc * proc = filp-> private_data; struct binder_thread * thread; unsigned int size = _ IOC_SIZE (cmd); void _ user * ubuf = (void _ user *) arg ;......... mutex_lock (& binder_lock); thread = binder_get_thread (proc); // The Last retrieved thread. logoff is BINDER_LOOPER_STATE_ENTEREDif (thread = NULL) {ret =-ENOMEM; goto err;} swi Tch (cmd) {// cmd is the BINDER_WRITE_READcase BINDER_WRITE_READ passed above: {struct binder_write_read bwr; if (size! = Sizeof (struct binder_write_read) {ret =-EINVAL; goto err;} if (copy_from_user (& bwr, ubuf, sizeof (bwr ))) {// copy the binder_write_read struct passed in from the user space and save it in the variable bwr ret =-EFAULT; goto err ;}......... if (bwr. write_size> 0) {// bwr. write_size is equal to 0. Do not execute ret = binder_thread_write (proc, thread, (void _ user *) bwr here. write_buffer, bwr. write_size, & bwr. write_consumed); if (ret <0) {bwr. read_consumed = 0; if (copy_to_use R (ubuf, & bwr, sizeof (bwr) ret =-EFAULT; goto err ;}} if (bwr. read_size> 0) {// bwr. read_size is greater than 0. Run ret = binder_thread_read (proc, thread, (void _ user *) bwr here. read_buffer, bwr. read_size, & bwr. read_consumed, filp-> f_flags & O_NONBLOCK); if (! List_empty (& proc-> todo) wake_up_interruptible (& proc-> wait); if (ret <0) {if (copy_to_user (ubuf, & bwr, sizeof (bwr ))) ret =-EFAULT; goto err ;}}........... if (copy_to_user (ubuf, & bwr, sizeof (bwr) {// returns the result to the user space bwrret =-EFAULT; goto err ;} break ;}.......... ret = 0; err: if (thread) thread-> logoff & = ~ BINDER_LOOPER_STATE_NEED_RETURN; mutex_unlock (& binder_lock); ...... return ret ;}

Because bwr. read_size is greater than 0, binder_thread_read is started. The implementation is as follows:

Binder_ioctl is located in ~ /Android/kernel/goldfish/drivers/staging/android/binder. c

Static intbinder_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; // start position void _ user * end = buffer + size; // end position int ret = 0; int wait_for_proc_work; if (* consumed = 0) {if (put_user (BR_NOOP, (uint32_t _ user *) ptr) // store BR_NOOP to return-EFAULT in the local variable just now; ptr + = sizeof (uint32_t);} retr Y: wait_for_proc_work = thread-> transaction_stack = NULL & list_empty (& thread-> todo); // wait_for_proc_work is currently 1, indicates that the thread has no task to process if (thread-> return_error! = BR_ OK & ptr <end ){..........} thread-> looper | = BINDER_LOOPER_STATE_WAITING; // looper: BINDER_LOOPER_STATE_ENTERED, prepare (wait_for_proc_work) // 1proc-> ready_threads ++; // ready_threads is 1, the process has an additional idle thread mutex_unlock (& binder_lock); if (wait_for_proc_work) {// 1if (! (Thread-> logoff & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED) {// false ...............} binder_set_nice (proc-> default_priority); // set the priority of the current thread to the priority of the process to which it belongs. if (non_block) {// if (! Binder_has_proc_work (proc, thread) // If a task exists, it is executed. If no task exists, ret =-EAGAIN;} elseret = wait (proc-> wait, binder_has_proc_work (proc, thread); // wait until the process to which it belongs has a new unprocessed item} else {if (non_block) {// if (! Binder_has_thread_work (thread) when there is a task, it will be executed next time. If there is no task, it will return ret =-EAGAIN;} elseret = wait_event_interruptible (thread-> wait, binder_has_thread_work )); // wait until the thread has a new unprocessed item} mutex_lock (& binder_lock); if (wait_for_proc_work) // 1proc-> ready_threads --; // ready_thread is 0thread-> logoff & = ~ BINDER_LOOPER_STATE_WAITING; // The logoff value is BINDER_LOOPER_STATE_ENTEREDif (ret) return ret; while (1 ){........} done: * consumed = ptr-buffer ;.......... return 0 ;}

static intbinder_has_proc_work(struct binder_proc *proc, struct binder_thread *thread){return !list_empty(&proc->todo) || (thread->looper & BINDER_LOOPER_STATE_NEED_RETURN);}static intbinder_has_thread_work(struct binder_thread *thread){return !list_empty(&thread->todo) || thread->return_error != BR_OK ||(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN);}
If there is no data to be processed in the thread, the data waiting on the proc process will be processed. If there is no data to be processed on the process, wait until the process to which it belongs has a new unprocessed item.

If the thread contains data to be processed, the data on the thread is processed. If no data is to be processed on the thread, wait until the thread has a new unprocessed item.

If the access is non-blocking and there is no data, the system immediately returns and does not wait for sleep. If there is data, continue to execute.

At present, because the thread does not have any data to process and the process does not have any data to process, wait until the process to which it belongs has a new unprocessed item.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.