image

Binder series first article: “from getSystemService (), open lu Binder communication mechanism, http://www.jianshu.com/p/1050ce12bc1e

Part 2: “Using Plain English to Analyze The Communication Mechanism with Binder” http://www.jianshu.com/p/fe816777f2cf

Binder series article 3: the story of Binder mechanism is one of the time response of http://www.jianshu.com/p/4fba927dce05

CoorChice’s last post, “With getSystemService(), Binder Communication: http://www.jianshu.com/p/1050ce12bc1e “in the left some pit on Binder, maybe when you look at some of the fog, this article, CoorChice began to fill the hole. Binder’s core mechanics will give you an insight into the most important parts of Android.

All right, let’s go!

Start by open_driver ()





image

Binder communication starting with getSystemService() : http://www.jianshu.com/p/1050ce12bc1e “this article, believe that everyone should see in/frameworks/native/libs/binder/ProcessState CPP file, there are such a piece of code.

Static int open_driver() {// open the "/dev/binder" driver file with the descriptor int fd = open("/dev/binder", O_RDWR); . Status_t result = ioctl(fd, BINDER_VERSION, &vers); . size_t maxThreads = 15; Result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads); . return fd; }Copy the code

It will be called when ProcessState is created. It performs an important task of opening the /dev/binder device file in this process and then obtaining the device file descriptor. As you can see, opening the device file is done with the open() function. How does that work?

User space functions and Binder driver functions

First open/drivers/staging/android/binder. The c file, and then find the following structure:

Static const struct file_operations binder_fops = {.owner = THIS_MODULE, binder_fops = {.owner = THIS_MODULE, .poll = binder_poll,.unlocked_ioctl = binder_ioctl,.compat_ioctl = binder_ioctl, // user space mmap() operation, Binder_mmap () is called by Binder drivers. Mmap = binder_mmap, // the user space open() operation, Binder_open () is called by Binder drivers. Open = binder_open,.flush = binder_flush,.release = binder_release,};Copy the code

This construct is associated with linux-defined file operators at Binder driver registration as a member of the following construct.

Static struct miscDevice binder_miscdev = {. Minor = MISC_DYNAMIC_MINOR, // Define device node name. Name = "Binder ", // Associated with Linux file operator. Fops = &binder_fops};Copy the code

This way, after the Binder driver device is registered, the corresponding Binder driver functions are called when the poll(), open() and other functions are called in user space.

The true nature of the open() function

Now we know that when we call open() in user space, the binder_open() function in the Binder driver layer is called accordingly. What does the binder_open() function do?

Static int binder_open(struct inode *nodp, struct inode *nodp) Binder_proc (struct file *filp) {//binder_proc (struct file *filp) {//binder_proc (struct file *filp); // Store information about the current process in binder_proc... // Lock synchronization binder_lock(__func__); . // Save the proc context information to the Binder driver's process tree // for lookup using hlist_add_head(&proc->proc_node, &binder_procs); Proc ->pid = current->group_leader->pid; . // Assign process information structure to file private data filp->private_data = proc; // Release the lock binder_unlock(__func__); . Snprintf (strbuf, sizeof(strbuf), "%u", proc->pid) snprintf(strbuf, sizeof(strbuf), "%u", proc->pid); return 0; }Copy the code

Binder: /dev/binder: /dev/binder: /dev/binder: /dev/binder: /dev/binder: /dev/binder: /dev/binder This way, the process can interact with Binder drivers.

The open() function returns the device descriptor, and the binder_open() function looks like it just returns 0. CoorChice mentioned earlier that binder_open() is only associated with open(), but Linux does the actual opening of the device file. As you can see, the argument to the binder_open() function is not available until the device file is opened. Therefore, the device descriptor should be assigned to the process by Linux.

Next, take a look at another function, ioctl(), that appears in open_driver().

The real ioctl() function

If you understand the open() function above, you know that the user-space ioctl() function causes the binder_ioctl() function in the Binder driver layer to be called. So let’s see what does this function at the driver level do? This is a very important function.

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { ... Struct binder_proc *proc = filp->private_data; struct binder_proc *proc = filp->private_data; struct binder_thread *thread; unsigned int size = _IOC_SIZE(cmd); Void __user *ubuf = (void __user *)arg; . Thread = binder_get_thread(proc); . switch (cmd) { ... }... }Copy the code

Similarly, the user layer calls the ioctl(fd, CMD, arG) function. The Linux kernel obtains the corresponding device body file from the device descriptor FD and then calls the binder_ioctl() function driven by Binder.

This is an important function that CoorChice will parse step by step.





image

Get the process information body

The Binder driver first gets information about the processes in the incoming file body.

struct binder_proc *proc = filp->private_data;
Copy the code

Remember the process information structure generated in binder_open()?

Parameters from user space

void __user *ubuf = (void __user *)arg;
Copy the code

The first thing we need to know is that this ARG is the address passed in from user space. For example, ioctl(FD, BINDER_VERSION, &vers) passes an address to the space used to store Binder version numbers.

With the Binder driver, this address is translated and marked with __user indicating that it points to a user-space address. The data transfer between the arG pointing space and the Binder driven memory space is copy_from_user() or copy_to_user().

Different CMDS correspond to different operations

switch (cmd) {
        ...
    }
Copy the code

There are several commands defined in switch that correspond to different operations, but CoorChice will not be described in this article.

Let’s start by looking at the two CMD’s that appear in open_driver().

  • BINDER_VERSION The BINDER_VERSION command is used to obtain the Binder driver version.
Struct BINDER_VERSION __user *ver = ubuf; struct BINDER_VERSION __user *ver = ubuf; . If (put_user(BINDER_CURRENT_PROTOCOL_VERSION, &ver->protocol_version)) {// Assign the version number to the protocol_version member of binder_version. } break; }Copy the code

Note that instead of a direct assignment, the put_user() function is used. Because this value needs to be written to user space.

  • BINDER_SET_MAX_THREADS Sets the maximum number of threads that the Binder process can communicate with.
Case BINDER_SET_MAX_THREADS: // Use copy_from_user(), Max_threads if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) { ... } break;Copy the code

Notice that copy_from_user() is used to write user-space values to max_threads, a member of the driver’s process body.

The open_driver() hole is fixed.





image

The next see ProcessState: : getStrongProxyForHandle () function of the pit.

Then getStrongProxyForHandle said ()

Attention, from here it is 18 turns of the mountain road, hold it well!

Let’s start with a flow chart.





image

Not hd enough? Click this link to download it! http://ogemdlrap.bkt.clouddn.com/Binder%E8%BF%9B%E9%98%B6%E5%AE%8C%E6%95%B4.png. So Sweet!

The flow lines in the same color represent the same flow, with numbers on them, and you need to look at them in numerical order, because this is a really complicated process!

In addition, two-way arrow lines of the same color point to the same variable or variable with the same value. Similarly, the typed hollow arrows of the same color point to the same variable or to the same value.

The boxes at the top of each function box represent the parameters passed to the function in our flow.

Review the pit in getStrongProxyForHandle()

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle) { sp<IBinder> result; . Handle_entry * e = lookupHandleLocked(Handle); if (e ! = NULL) { IBinder* b = e->binder; if (b == NULL || ! AttemptIncWeak (this) {e->refs->attemptIncWeak(this)) { // A virtual transaction request is executed when the handle corresponding BpBinder is first created. Status_t status = IPCThreadState::self()->transact(0, IBinder::PING_TRANSACTION, data, NULL, 0); If (status == DEAD_OBJECT) // If the ServiceManager is not registered, return NULL; // Create a BpBinder b = new BpBinder(handle); // Create a handle b = new BpBinder(handle); e->binder = b; if (b) e->refs = b->getWeakRefs(); result = b; // return b}... } return result; }Copy the code

CoorChice omitted this code from getStrongProxyForHandle() last time to focus on the flow.

if (handle == 0) { Parcel data; // A virtual transaction request is executed when the handle corresponding BpBinder is first created. Status_t status = IPCThreadState::self()->transact(0, IBinder::PING_TRANSACTION, data, NULL, 0); If (status == DEAD_OBJECT) // If the ServiceManager is not registered, return NULL; }Copy the code

Since we made a request for ServiceManager’s Binder, Handle is 0. Remember? Before the application process first obtains (or creates) the ServiceManager’s Binder, it makes a meaningless communication with the ServiceManager (as you can see, this communication is PING_TRANSACTION). To ensure that the System ServiceManager has been registered. Since this is our first encounter with Binder communication, let’s explore the core flow of the Binder communication mechanism from here.

The creation of IPCThreadState

IPCThreadState::self()->transact(0, IBinder::PING_TRANSACTION, data, NULL, 0)
Copy the code

This code first gets the IPCThreadState singleton. That’s what I left out in the picture.

IPCThreadState* IPCThreadState::self() { if (gHaveTLS) { restart: const pthread_key_t k = gTLS; IPCThreadState* st = (IPCThreadState*) pthread_getSpecific (k); if (st) return st; return new IPCThreadState; // No new IPCThreadState}... }Copy the code

Obviously, this code ensures that there is only one CORRESPONDING IPCThreadState for each thread in the process.

Now look at the constructor of IPCThreadState.

IPCThreadState: : IPCThreadState () / / save's process: mProcess(ProcessState::self()), mMyThreadId(androidGetTid()), mStrictModePolicy(0), mLastTransactionBinderFlags(0) { pthread_setspecific(gTLS, this); clearCaller(); // To receive data from Binder drivers, set its size to 256 min.setdatacapacity (256); // Used to send data to Binder drivers, also set its size to 256 out.setdatacapacity (256); }Copy the code

The CoorChice annotation is more important. If you want to understand the following process, remember the above 3 annotations!

Ok, so we’ve created our IPCThreadState. In fact, IPCThreadState basically encapsulates the logic to communicate with Binder when we need to communicate.





image

Here’s how communication started.

Step 1 IPCThreadState::transact() initiates the communication

You can first find the corresponding flow line in the diagram. Transact () you can look at the picture of the complete code or in/frameworks/native/libs/binder/IPCThreadState CPP to see the source code. Because of the complexity of the process, CoorChice is illustrated in small fragments.

status_t IPCThreadState::transact(int32_t handle, uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { flags |= TF_ACCEPT_FDS; // add TF_ACCEPT_FDS... if (err == NO_ERROR) { ... Err = writeTransactionData(BC_TRANSACTION, FLAGS, Handle, code, data, NULL); }... }Copy the code

First, add a TF_ACCEPT_FDS flag to the flags parameter passed in, indicating that the returned data can contain file descriptors. Here are the meanings of several flag bits:

Enum transaction_FLAGS {TF_ONE_WAY = 0x01, /* Asynchronous unidirectional call, No return value */ TF_ROOT_OBJECT = 0x04, /* The data inside is a component root object */ TF_STATUS_CODE = 0x08, /* The data contains a 32-bit status code */ TF_ACCEPT_FDS = 0x10, /* Allow to return objects containing file descriptors */}Copy the code

The writeTransactionData() function is then called to prepare the data that needs to be sent. Note that the command is BC_TRANSACTION. If you check the parameters against the diagram at any time, the process will be easier to understand.

Step 2 writeTransactionData() prepares to send data

status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags, int32_t handle, uint32_t code, Const Parcel& data, status_t* statusBuffer) {binder_transaction_data tr; tr.target.ptr = 0; // Address of binder_node tr.target.handle = handle; // Handle for target process Binder, corresponding to binder_ref tr.code = code; // indicates the transaction type tr.flags = binderFlags; tr.cookie= 0; . Write CMD mout.writeint32 (CMD); write CMD mout.writeint32 (CMD); Mout.write (&tr, sizeof(tr)); return NO_ERROR; }Copy the code

As you can see, this function basically creates a binder_transaction_data structure T for the communication transaction data, puts the transaction data that needs to be sent into it, and then writes the TR to the mOut of the IPCThreadState. This way, the communication transaction data structure can be retrieved from mOut later. It’s very important. You have to remember what is it? And from where?

In addition, the command of this communication needs to be written into mOut, so that the command of the sender can be obtained later and the corresponding operation can be performed.

Step 3 waitForResponse() waits for the response

After the second step, we return to the IPCThreadState::transact() function.

status_t IPCThreadState::transact(int32_t handle, uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { ... flags |= TF_ACCEPT_FDS; // add TF_ACCEPT_FDS... If ((flags & TF_ONE_WAY) == 0) { If (reply) {err = waitForResponse(reply); } else { Parcel fakeReply; err = waitForResponse(&fakeReply); }... }... return err; }Copy the code

Generally, communication requires a response, so we will only look at the case where there is a response, i.e., flags does not contain the TF_ONE_WAY flag. When the waitForResponse() function is called, if there is no reply, a fakeReplay is created. Let’s review:

transact(0, IBinder::PING_TRANSACTION, data, NULL, 0)
Copy the code

See, the replay we passed above is a NULL, so a fakeReplay will be created.

Then, we will enter the IPCThreadState: : waitForResponse (). You can see the flow line in the figure below, which corresponds to the line numbered 3 in red.

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) { ... If (err=talkWithDriver()) < NO_ERROR) break; if (err=talkWithDriver() < NO_ERROR) break; . }... }Copy the code

This method, some hidden from the start calling a very important method IPCThreadState: : talkWithDriver (), also can see from the name, real and Binder driver talk logic is in this function. This place gives a bad review!





image

Following the code, let’s go into talkWithDriver() to see how user space talks to Binder drivers.

Step 4 talkWithDriver() and Binder Talk!

Note that you need to explain, IPCThreadState: : talkWithDriver () this function parameters default to true! Make sure you remember it, or you won’t be able to read it!

status_t IPCThreadState::talkWithDriver(bool doReceive) { ... // Read/write struct, which is the user space and kernel space messenger binder_write_read BWR; . Bwr. write_size = outAvail; bwr.write_buffer = (uintptr_t)mOut.data(); . If (doReceive && needRead){bwr.read_size = min.datacapacity (); bwr.read_buffer = (uintptr_t)mIn.data(); } else { bwr.read_size = 0; bwr.read_buffer = 0; }... // Set consumed to 0 bwr.write_consumed = 0; bwr.read_consumed = 0; status_t err; do { ... If (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, & BWR) >= 0) err = NO_ERROR; if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, & BWR) >= 0) err = NO_ERROR; . } while (err == -EINTR); . }Copy the code

One important structure defined in this function is binder_write_read. It can store the necessary sent and received communication information, and it acts as a messenger between user space and kernel space, passing information on both ends.

This function first puts user-space to pass/read information into BWR and then talks to Binder drivers via a key line of code called IOCtl (mProcess->mDriverFD, BINDER_WRITE_READ, & BWR). The ioctl() function CoorChice was mentioned in the previous article, which eventually calls binder_ioctl() in Binder kernel. Why? You can revisit the previous article for a refresher.





image

Notice the arguments we pass to the ioctl() function here.

  • The first parameter is to retrieve the previously generated file structure from this process by opening and saving the Binder device file descriptor described in the previous sectionbinder_ioctl()Function. For those of you who don’t remember, look at the previous chapter and review this.
  • The second argument, the command, determines the logic to be executed later in kernel space.
  • In the third argument, we pass the memory address of the messenger BWR we just defined to the kernel space.

These parameters are key to understanding the next steps, so don’t forget! Now, go into the old friend binder_ioctl() function and see what it does when it receives a user-space message.

Step 5 processes the message in binder_ioctl()

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    int ret;
    // 从file结构体中取出进程信息
    struct binder_proc *proc = filp->private_data;
    struct binder_thread *thread;
    unsigned int size = _IOC_SIZE(cmd);
    //表明arg是一个用户空间地址
    //__user标记该指针为用户空间指针,在当前空间内无意义
    void __user *ubuf = (void __user *)arg;
    ...
    //锁定同步
    binder_lock(__func__);
    //取出线程信息
    thread = binder_get_thread(proc);
    ...
    switch (cmd) {
        //读写数据
        case BINDER_WRITE_READ:
            ret = binder_ioctl_write_read(filp, cmd, arg, thread); 
            ...
        }
        ...
    }
    ...
    //解锁
    binder_unlock(__func__);
    ...
}
Copy the code

This function seen from getSystemService () and open lu Binder communication mechanism: http://www.jianshu.com/p/1050ce12bc1e, students should not be unfamiliar. The process information of the process calling ioctl() is first obtained from the file structure obtained by the file descriptor, and then the thread of the process is obtained. The ARG parameter address is then converted into a pointer with a user-space tag. Next, determine what you need to do in the Switch based on the CMD argument. The steps are the same as in the previous article. The difference is that our CMD command is BINDER_WRITE_READ, which means we want to read and write. As you can see, the next read and write logic is in the binder_ioctl_write_read() function.

Well, next we move to step 6. What does this read-write communication logic look like in the Binder driver?

Step 6 binder_iocTL_write_read () talking

static int binder_ioctl_write_read(struct file *filp, unsigned int cmd, unsigned long arg, struct binder_thread *thread) { int ret = 0; Struct binder_proc *proc = filp->private_data; unsigned int size = _IOC_SIZE(cmd); Void __user *ubuf = (void __user *)arg; Struct binder_write_read BWR; . If (copy_from_user(&bwr, ubuf, sizeof(BWR)))... If (bwr.write_size > 0) {// Write ret = binder_thread_write(proc, thread, bwr.write_buffer, bwr.write_size, &bwr.write_consumed); . }Copy the code

Let’s watch the clip above first.

The first step, of course, is to fetch the user-space process information, and then transform to get the user-space parameter address (corresponding to the BWR address in this communication), much as we did in binder_ioctl().

Next, you can see a declaration of binder_write_read struct binder_write_read BWR, This is followed by copying the user-space BWR to the current kernel space BWR via copy_from_user(& BWR, ubuf, sizeof(BWR)). The BWR of the Binder kernel space now gets the communication information from the user space.

Once we get the information from user space, we call binder_thread_write() to process it. Let’s see how it works.

Step 7 binder_thread_write() handles the write

static int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, binder_uintptr_t binder_buffer, size_t size, binder_size_t *consumed) { uint32_t cmd; void __user *buffer = (void __user *)(uintptr_t)binder_buffer; void __user *ptr = buffer + *consumed; // start address void __user *end = buffer + size; While (PTR < end && thread->return_error == BR_OK) {if (get_user(CMD, (uint32_t __user *)ptr)) -EFAULT; ptr += sizeof(uint32_t); Switch (CMD) {case BC_TRANSACTION: case BC_REPLY: {struct binder_transaction_data tr; Binder_transaction_data if (copy_from_user(&tr, PTR, sizeof(tr)))) return -EFAULT; ptr += sizeof(tr); Binder_transaction (proc, thread, &tr, CMD == BC_REPLY); break; }... } *consumed = ptr - buffer; } return 0; }Copy the code

You start by assigning some variables.

First, what is a binder_buffer? Which come of? Go to the area where the parameter is passed and look at bwr.write_buffer, which is the buffer that is written. So what’s in it? This brings us back to Step 4, because that’s where the BWR is defined and initialized. Bwr.write_buffer = (uintptr_t) out.data (), well, it points to the data in mOut. So the question is again? What is the data in mOut? .





image

See, that’s why CoorChice has been emphasizing that some of the previous parameters and variables must be kept in mind! Otherwise it will be in the back of the fog! Fortunately, with the CoorChcie image above, you can always find the answer quickly. Let’s go back to step 2, writeTransactionData(), where the communication transaction structure is defined. See, mOut stores a communication transaction structure.

Now the answer is clear: buffer refers to the communication transaction data in user space.

The other two parameters, PTR, now have the same value as buffer. Because consumed is 0, it is now also a pointer to user-space communication transaction data TR. And end obviously points to the end of TR.

Using the get_user(CMD, (uint32_t __user *) PTR) function, we can copy the CMD of user space tr into kernel space. That’s what the get_user() and put_user() functions do, copying some simple variables. Go back to step 2, writeTransactionData(), and look at the parameters. Yes, CMD is BC_TRANSACTION. So, in switch, case BC_TRANSACTION is executed.

You can see that the BC_TRANSACTION transaction command and BC_REPLAY response command perform the same logic.

Struct binder_transaction_data tr; // Copy user space binder_transaction_data if (copy_from_user(&tr, PTR, sizeof(tr))) return -efau PTR += sizeof(tr); Binder_transaction (proc, thread, &tr, CMD == BC_REPLY);Copy the code

We first define a kernel-space communication transaction data TR, and then copy user-space communication transaction data tr into the kernel. At this point, the PTR pointer moves sizeof(tr) by units, and PTR should now have the same value as end. Binder_transaction () is then called to process the transaction.

Step 8 binder_TRANSACTION () to process the transaction

Follow flow line 8, WTF! This is an incredibly complicated function! A lot of! Very long!

The function starts out with a bunch of variables, so let’s leave it at that, and we’ll see when we use it. Let’s look at the first parameter used, replay. Since the CMD in the previous step is BC_TRANSACTION, this is obviously false, so let’s go straight to the logic in false.

static void binder_transaction(struct binder_proc *proc, struct binder_thread *thread, struct binder_transaction_data *tr, int reply){ ... if (reply) { ... } else {if (tr->target.handle) {if (tr->target.handle) {if (tr->target.handle) {if (tr->target.handle) {if (tr->target.handle) { // Bind to binder_get_ref(proc, tr->target.handle); // Bind to binder_get_ref(proc, tr->target.handle); . // Set the target binder entity to the binder entity referenced by the parameter process found above target_node = ref->node; } else {// If the process handle in the argument transaction information is 0, Binder_context_mgr_node = binder_context_mgr_node; } // set the target process to target_node target_proc = target_node->proc; . }Copy the code

Tr ->target.handle = 0 Remember what we said in the last post? If handle is 0, it indicates a ServiceManager. If handle is not 0, it indicates other services. So what is zero here? Look at the picture!

We follow the green line that leads to TR, and we can see. Transaction data is defined in user space communication, namely step 2 IPCThreadState: : writeTransactionData (), give the tr – > target_handle assignment, look up, This value comes from handle, the argument to the IPCThreadState:: Transact () function. So back to where we first called this function IPCThreadState::self()->transact(0, IBinder::PING_TRANSACTION, data, NULL, 0). Oh, handle is 0. So the target here is the ServiceManager. Binder_context_mgr_node (the binder_context_mgr_node for ServiceManager’s Binder, which is cached in the Binder kernel when ServiceManager is registered) is assigned to target_node. All of this is going to work. In short, we just need to get a target process first.

Next, with target_node, the Binder of the ServiceManager process (or otherwise, the corresponding process), we get the target process information and assign it to target_proc. Remember that!

Move on to the next piece of code.

static void binder_transaction(struct binder_proc *proc, struct binder_thread *thread, struct binder_transaction_data *tr, int reply){ ... // Check whether the target thread is empty if (target_thread) {... // Todo queue of target thread target_list = &target_thread->todo; target_wait = &target_thread->wait; . } else {// Obtain the task queue of the target process target_list = &target_proc->todo; Target_wait = &target_proc->wait; }... }Copy the code

First check whether the target_thread is empty. Since we did not use the TRUE logic of if(replay), the target_thread is empty. Then, remove the TODO task queue and wait object from the target process information target_proc and assign values to target_list and target_WAIT, respectively. Remember that too!

Move on to the next piece of code.

static void binder_transaction(struct binder_proc *proc, struct binder_thread *thread, struct binder_transaction_data *tr, int reply){ struct binder_transaction *t; // represents a binder communication transaction struct binder_work *tcomplete; // indicate a work... struct list_head *target_list; Wait_queue_head_t *target_wait; // The waiting object of the communication target process... T = kzalloc(sizeof(*t), GFP_KERNEL); . tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL); . if (! reply && ! (tr->flags & TF_ONE_WAY)) // Set the thread of transaction t to user space t->from = thread; else t->from = NULL; . T ->to_proc = target_proc; T ->to_thread = target_thread; T ->code = tr->code; T ->flags = tr->flags; . T ->buffer = binder_alloc_buf(target_proc, tr->data_size, tr->offsets_size, ! reply && (t->flags & TF_ONE_WAY)); t->buffer->allow_user_free = 0; T ->buffer->transaction = t; // Set the target object of the buffer to be target_node. T ->buffer->target_node = target_node; . offp = (binder_size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *))); Copy_from_user (t->buffer->data, (const void __user *)(uintptr_t)tr->data.ptr.buffer, tr->data_size); . // copy offsets to start offp copy_from_user(offp, (const void __user *)(uintptr_t)tr->data.ptr.offsets, tr->offsets_size); . Off_end = (void *)offp + tr->offsets_size; . // check if BC_REPLY if (reply) {... binder_pop_transaction(target_thread, in_reply_to); } else if (! (t->flags & TF_ONE_WAY)) {// If there is no ONE_WAY flag, the response needs to be waited for t->need_reply = 1; //1 indicates that this is a synchronous transaction and needs to wait for a reply. T ->from_parent = thread->transaction_stack; t-> thread->transaction_stack; Thread ->transaction_stack = t; }... // Set the work type of this transaction to BINDER_WORK_TRANSACTION t->work.type = BINDER_WORK_TRANSACTION; List_add_tail (&t->work. Entry, target_list); // list_add_tail(&t->work. Entry, target_list); // Set work type to BINDER_WORK_TRANSACTION_COMPLETE tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; // add work of type BINDER_WORK_TRANSACTION_COMPLETE to sender's transaction list list_add_tail(&tcomplete->entry, &thread->todo); If (target_wait) // Wake up the target process and start executing its transaction stack wake_up_interruptible(target_wait); return; }Copy the code

Before we start the analysis, let’s remember the first few variable definitions of this code, otherwise it will be very confusing!

That’s a lot of code! Very long! CoorChice has tried to delete some of the less important ones and I don’t know what they are!!





image

All of this code is used to assign values to binder transaction t members. Let’s briefly look at a couple of assignments that I think are important.

First memory is allocated for transaction T and Work tcomplete. Then set the value of transaction T’s FROM thread (i.e. sender thread). If it is not a BC_REPLAY transaction and the communication flag does not have TF_ONE_WAY, then assign the parameter thread to t-> FROM. As mentioned earlier, this transaction is a BC_TRANSACTION transaction, so transaction T needs to store the thread information of the sender, so that it can be used by the sender later.

Where does thread come from? Looking back, in step 5, binder_ioctl(), we get thread from the process information of the user-space process that called the ioctl() function (the sender process).

Set t->to_proc and t->to_thread to target_proc and target_thread.

Then assign the code and flags in the communication transaction data TR to the code and flags in the transaction t. What are the codes and flags? Back to user space, we define the communication the transaction data, namely the step 2 IPCThreadState: : writeTransaction (), you can see that the code and flags are incoming parameters. IPCThreadState::self()->transact(0, IBinder::PING_TRANSACTION, data, NULL, 0), Code = IBinder::PING_TRANSACTION, flags = 0. Remember that! We’ll use it again.

Then set transaction T’s buffer. The binder_alloc_buf() function first allocates memory for t->buffer in the target process target_proc, i.e. t->buffer points to a segment of memory in the target process space. Then configure t->buffer, which will be used later. Remember that!

Copy_from_user () then copies the user-space data that needs to be sent to the t->buffer data.

If (replay) is used, false logic is used. Thus, transaction T stores the sender’s transaction stack transaction_stack in from_parent, and the sender sets its transaction stack to start with t. Keep these things in mind, or you’ll get more and more confused later on!

Here comes the most important part!

// Set the work type of this transaction to BINDER_WORK_TRANSACTION t->work.type = BINDER_WORK_TRANSACTION; List_add_tail (&t->work. Entry, target_list); // list_add_tail(&t->work. Entry, target_list); // Set work type to BINDER_WORK_TRANSACTION_COMPLETE tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; Add work of type BINDER_WORK_TRANSACTION_COMPLETE to the sender's transaction list list_ADD_tail (&tcomplete-> Entry, & Thread ->todo); If (target_wait) // Wake up the target process and start executing its transaction stack wake_up_interruptible(target_wait); return;Copy the code

Set transaction T’s work.type to BINDER_WORK_TRANSACTION, which determines the flow that follows the transaction, and add transaction T’s task to the target process’s task stack target_list. BINDER_WORK_TRANSACTION_COMPLETE is then set to type BINDER_WORK_TRANSACTION_COMPLETE, which tells the sender that a Binder driven talk is complete and that the task needs to be added to the sender’s task list.

Finally, the dormant target process is awakened by the wake_up_interruptible(target_wait) function to begin processing the tasks in the stack that we just added to the target_list. Then return terminates the function.

Do you think you’ve forgotten this function? Native! And then we look down.

Step 9 binder_thread_read() reads the data

Return to step 7 in the binder_thread_write() function, retrun 0; , the binder_thread_write() function ends. Then go back to the binder_ioctl_write_read() function in step 6 and continue.

static int binder_ioctl_write_read(struct file *filp, unsigned int cmd, unsigned long arg, struct binder_thread *thread) { ... If (bwr.read_size > 0) {// Read data ret = binder_thread_read(proc, thread, bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK); . }Copy the code

Binder drivers call the binder_thread_read() function to read data for the sending process. Let’s see how we read it.

static int binder_thread_read(struct binder_proc *proc, struct binder_thread *thread, binder_uintptr_t binder_buffer, size_t size, binder_size_t *consumed, int non_block) { ... while (1) { uint32_t cmd; struct binder_transaction_data tr; struct binder_work *w; struct binder_transaction *t = NULL; if (! List_empty (&thread->todo)) {list_empty(&thread->todo, struct binder_work, entry); } else if (! List_empty (&proc->todo) &&wait_for_proc_work) {// list_empty(&proc->todo) &&wait_for_proc_work) entry); } else {// No data, retry if (ptr-buffer == 4&&! (thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) goto retry; break; }... }Copy the code

Let’s take a look at this clip, where a bunch of code is skimming over. First, we need to see if we can pull the task out of the stack of thread, the thread that sent the process. Recall from step 8, binder_TRANSACTION (), We added a work of type BINDER_WORK_TRANSACTION_COMPLETE to the task stack of the thread that sent the process. So I can get the task here, so I just go to the next step.

static int binder_thread_read(struct binder_proc *proc, struct binder_thread *thread, binder_uintptr_t binder_buffer, size_t size, binder_size_t *consumed, int non_block) { void __user *buffer = (void __user *)(uintptr_t)binder_buffer; void __user *ptr = buffer + *consumed; // void __user *end = buffer + size; // User space end... while (1) { uint32_t cmd; struct binder_transaction_data tr; struct binder_work *w; . switch (w->type) { ... Case BINDER_WORK_TRANSACTION_COMPLETE: // Set CMD to BR_TRANSACTION_COMPLETE CMD = BR_TRANSACTION_COMPLETE; Put_user (CMD, (uint32_t __user *) PTR); // Delete the work list_del(&w->entry) from the transaction queue; / / release kfree (w); break; . }}... }Copy the code

Since we know in this process that work is of type BINDER_WORK_TRANSACTION_COMPLETE, we’ll just look at this case for now. CMD = BR_TRANSACTION_COMPLETE is important to remember in this code! Then copy CMD to the sending process in user space and delete the task to free up memory.

A communication with Binder drivers is complete!

The binder_thread_read() function is more or less finished with the above code, and will then return to the binder_ioctl_write_read() function.

static int binder_ioctl_write_read(struct file *filp, unsigned int cmd, unsigned long arg, struct binder_thread *thread) { ... If (copy_to_user(ubuf, &bwr, sizeof(BWR)))... }Copy the code

The above function eventually copies the kernel messengers into user space.

Then, we direct again back to step 3 function IPCThreadState: : waitForResponse ().

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) { ... If (err=talkWithDriver()) < NO_ERROR) break; if (err=talkWithDriver() < NO_ERROR) break; err = mIn.errorCheck(); . if (mIn.dataAvail() == 0) continue; CMD = min.readint32 (); . Case BR_TRANSACTION_COMPLETE: if (! reply && ! acquireResult) goto finish; break; . }}... }Copy the code

After just read, this time there is data in mIn oh! We take CMD from mIn. What kind of order is that? The BR_TRANSACTION_COMPLETE that was just written to user space. In this logic, since we passed a fakeReplay in earlier, the program goes bredk and continues the loop, executing the talkWithDriver() function next time. At this point, our communication with the Binder kernel is complete.

But we have not received a response to this newsletter! Guess what the response process is?





image

The article is too long and the response process is moved to the next article.

conclusion

  • Take out spare time to write an article to share need power, still ask everybody to see an officer move a small hand dot praise, give me dot encourage 😄
  • I’ve been creating new dry goods from time to time. To get on, just go to my [personal] page and click “follow me”. Start then ~

CoorChice filled in some of the holes in the previous article and walked through the process of client – and Binder – driven communication. It’s a very complicated process, so let’s go through it and think about it. On second thought, it wasn’t that hard.

As the saying goes, those who know are easy, and those who know are not.

Limited skill, there is a mistake also please point out the exchange.

See here children’s shoes quickly reward yourself with a spicy stick!

For more articles from CoorChice, please follow us here.