An article to understand the Android Binder inter-process communication mechanism

Android-Binder inter-process communication mechanism

Overview

Recently, I am learning the Binder mechanism, and I have checked a lot of information on the Internet. I have also read Lao Luo's Binder series blog and Innost's in-depth understanding of the Binder series of blogs. They all start from the bottom, and they are all C code. Although I learned before. I have read C and C++, but the fancy jumps between various functions make me doubt life. It is no exaggeration to say that every time you watch it, it is new content, just like you have never seen it before. Later, I saw Gituan's blog and saw some diagrams as if I had discovered a new continent. 

The following will introduce the Binder mechanism in a graphical way. I believe you will gain something from reading this article.

What is Binder?

Binder is a way of inter-process communication (IPC) in the Android system, and it is also one of the most important features in the Android system. The four major components in Android, Activity, Service, Broadcast, ContentProvider, and different apps, all run in different processes, and it is the bridge for communication between these processes. Just like its name "glue", it glues the various components of the system together and acts as a bridge for each component.

Understanding Binder plays a very important role in understanding the entire Android system. If you don't understand Binder, it is difficult to have a deeper understanding of the Android system mechanism.

1. Binder Architecture

write picture description here

  • Binder communication adopts C/S architecture. From the perspective of components, it includes Client, Server, ServiceManager and Binder drivers. ServiceManager is used to manage various services in the system.
  • Binder is encapsulated in the framework layer, and the Binder architecture of the Native (C/C++) layer is called through JNI technology. 
  • Binder communicates with the Binder driver in the native layer in the form of ioctl.

2. Binder mechanism

write picture description here

  • First, you need to register the server. Only when the server is registered, the client has the target of communication. The server registers the service through the ServiceManager. The registration process is to insert the information of the server into the global linked list binder_procs driven by Binder (binder_proc structure, each There are todo task queues in the binder_proc structure), and then cache the registered services in the svcinfo list of ServiceManager.

  • With the server, the client can communicate with the server. Before communication, the service needs to be obtained, and the proxy of the service can also be understood as a reference. For example the following code:

    //获取WindowManager服务引用
    WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);
    • 1
    • 2

    The way to obtain the server is to query the svcinfo list for the proxy that returns the server through the ServiceManager. The svcinfo list is the address book of all registered services and saves all registered service information.

  • With the reference from the server, we can send requests to the server, send our request parameters to ServiceManager through BinderProxy, and use the kernel method copy_from_user() to copy our parameters to the kernel space by sharing memory. At this time, we The client enters the waiting state, and then the Binder driver inserts a transaction into the todo queue of the server. After the execution, the result of the execution is copied to the user space through copy_to_user() (here only the copy command is executed, and no data is copied. , the binder only makes one copy), wake up the waiting client and respond with the result, thus completing a communication.

How is it very simple, the above is the main communication method of the Binder mechanism, let's take a look at the specific implementation.

3. Binder driver

Let's first understand how user space and kernel space interact.

write picture description here

First understand some concepts

user space/kernel space

For a detailed explanation, please refer to  Kernel Space Definition ; a simple understanding is as follows:

Kernel space is the running space of the Linux kernel, and User space is the running space of user programs. For safety, they are isolated, and even if the user's program crashes, the kernel is not affected.

Kernel space can execute arbitrary commands and call all resources of the system; User space can only perform simple operations and cannot directly call system resources. It must pass the system interface (also known as system call) to issue instructions to the kernel.

System call/kernel mode/user mode

Although the user space and the kernel space are logically separated, it is inevitable that there will always be some user space that needs to access the resources of the kernel; for example, applications access files, the network is a very common thing, what to do?

Kernel space can be accessed by user processes only through the use of system calls.

The only way for user space to access kernel space is through system calls; through this unified entry interface, all resource accesses are executed under the control of the kernel, so as to avoid unauthorized access to system resources by user programs, thus ensuring system security and safety. Stablize. User software is mixed, what if they mess up and break the system? Therefore, some privileged operations must be handed over to a safe and reliable kernel for execution.

When a task (process) executes a system call and is caught in the execution of the kernel code, we say that the process is in the kernel running state (or simply called the kernel state). At this time, the processor executes in the kernel code with the highest privilege level (level 0). When a process is executing the user's own code, it is said to be in the user running state (user state). That is, the processor is running in user code with the lowest privilege level (level 3) at this time. The processor can only execute those privileged CPU instructions when the privilege level is high.

kernel module/driver

Through system calls, user space can access kernel space, so what if one user space wants to communicate with another user space? It is natural to think of adding support to the operating system kernel; traditional Linux communication mechanisms, such as sockets, pipes, etc., are supported by the kernel; but Binder is not part of the Linux kernel, how does it access the kernel space? Linux's dynamically loadable kernel module (LKM) mechanism solves this problem; a module is a program with independent functions, which can be compiled independently, but cannot be run independently. It is linked to the kernel at runtime and runs in kernel space as part of the kernel. In this way, the Android system can run in the kernel space by adding a kernel module, and the communication between user processes can be completed through this module as a bridge.

In the Android system, the kernel module that runs in the kernel space and is responsible for the communication of each user process through the Binder is called the Binder driver;

A driver generally refers to a device driver, which is a special program that enables a computer to communicate with a device. It is equivalent to the interface of the hardware. Only through this interface can the operating system control the work of the hardware device;

The driver is the interface for operating the hardware. In order to support the Binder communication process, the Binder uses a kind of "hardware", so this module is called a driver.

Familiar with the above concepts, let's take a look at the above figure. In the user space, binder_open(), binder_mmap(), binder_ioctl() these methods call the methods in the kernel space Binder driver through system call. Kernel space and user space share memory through copy_from_user(), copy_to_user() kernel methods to complete data transfer between user space and kernel space memory. There is a global binder_procs linked list in the Binder driver to save the process information of the server.

4. Binder process and thread

write picture description here

For the underlying Binder driver, all created binder_proc structures are recorded through the binder_procs linked list. Each binder_proc structure in the binder driver layer corresponds to a process in user space for binder communication, and each process has one and only one ProcessState object, which is guaranteed by the singleton pattern. There can be many threads in each process, each thread corresponds to an IPCThreadState object, and the IPCThreadState object is also a singleton mode, that is, a thread corresponds to an IPCThreadState object, and there is also a corresponding structure in the Binder driver layer, that is, the Binder_thread structure body. In the binder_proc structure, all binder_threads in the current process are recorded through the member variable rb_root threads.

Binder thread pool: Each Server process creates a binder thread pool at startup and registers a Binder thread with it; after that, the Server process can also register new threads with the binder thread pool, or when the Binder driver detects that there are no idle binder threads Actively register a new binder thread with the Server process. For a Server process, there is a limit on the maximum number of binder threads, which is 16 binder threads by default. For example, there are 16 threads in Android's system_server process. Binder requests for all client-side processes are handled by the binder thread of the server-side process.

5. ServiceManager starts

Knowing the Binder driver, how to communicate with the Binder driver? That is through ServiceManager. Many articles say that ServiceManager is a Binder-driven daemon process, a big housekeeper. In fact, the role of ServiceManager is very simple to provide the functions of querying services and registering services. Let's take a look at the process of ServiceManager startup.

write picture description here
- ServiceManager is divided into framework layer and native layer. The framework layer just encapsulates the native layer for easy invocation. The figure shows the startup process of the ServiceManager of the native layer.

  • The startup of ServiceManager is initiated by the init process parsing the init.rc file and calling the main() method entry in service_manager.c when the system is powered on. The native layer has a binder.c that encapsulates some methods for interacting with the Binder driver.

  • The startup of ServiceManager is divided into three steps. First, open the driver to create the global linked list binder_procs, then save the current process information to the binder_procs linked list, and finally open the loop to continuously process the data in the shared memory and process the BR_xxx command (ioctl command, BR can It is understood as the response processed by the binder reply driver).

6. ServiceManager registration service

write picture description here

  • To register the MediaPlayerService server, we register the service through the addService() method of the ServiceManager.

  • First, ServiceManager sends the BC_TRANSACTION command (ioctl command, BC can be understood as the request command sent by the binder client client) to the binder driver, carrying the ADD_SERVICE_TRANSACTION command, and the thread that registers the service enters the waiting state waitForResponse(). The Binder driver receives the request command and adds a transaction for registering the service to the ServiceManager's todo queue. The task of the transaction is to create the server process binder_node information and insert it into the binder_procs linked list.

  • After the transaction is processed, the BR_TRANSACTION command is sent, and the ServiceManager adds the registered service to the svcinfo list after receiving the command. Finally, the BR_REPLY command is sent to wake up the waiting thread and notify that the registration is successful.

7. ServiceManager get service

write picture description here

  • The process of acquiring a service is similar to registration, the reverse process. Services are registered through the getService() method of the ServiceManager.

  • First, ServiceManager sends the BC_TRANSACTION command to the Binder driver with the CHECK_SERVICE_TRANSACTION command, and the thread that obtains the service enters the waiting state waitForResponse().

  • The Binder driver receives the request command and sends BC_TRANSACTION to the ServiceManager to query the registered service, and the query directly responds to BR_REPLY to wake up the waiting thread. If the query fails, it will communicate with the services in the binder_procs list and then respond.

8. Have a full communication

write picture description here

  • When we use Binder, we basically call the method encapsulated by the framework layer. AIDL is the fool-like method provided by the framework layer. Assuming that the service has been registered, let's see how the client executes the server's method.

  • First, we obtain the BinderProxy proxy object of the server through ServiceManager, and pass the parameters and method identifiers (for example: TRANSACTION_test, automatically generated in AIDL) to ServiceManager by calling BinderProxy, and the client thread enters the waiting state.

  • The ServiceManager copies the request data such as user space parameters to the kernel space, and inserts a transaction to execute the execution method to the server. After the transaction is executed, the ServiceManager is notified to copy the execution result from the kernel space to the user space, wake up the waiting thread, respond to the result, and the communication ends.

Summarize

Well, here is just a brief introduction to the working principle of the Binder mechanism from the implementation logic. If you want to understand the Binder mechanism in depth, you have to work hard and look at the source code, although this process is very painful. If I don’t understand it, I will try it again. To be honest, my understanding ability is relatively poor, and I have read it no less than ten times following the blog idea. Efforts will always pay off, and appreciate the charm of fancy jumps between methods in the native layer. In the end you will find that the door of a new world is opening to you.

There is a lot of information on the Internet, and I personally think the best ones are as follows: 
1.  Bander design and implementation 
2. Lao Luo's  Android inter-process communication (IPC) mechanism Binder brief introduction and learning plan  series 
3. Innost's  in  -depth understanding of Binder  series  Series  (Based on Android 6.0)  5.  Binder Study Guide

References

More articles: 
https://github.com/jeanboydev/Android-ReadTheFuckingSourceCode

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325369931&siteId=291194637