preface

The binder mechanism is why it is designed and used in this way, and this is the underlying mechanism design that this share will delve into.

Dig deep into your knowledge base

Processes and multiple processes

The things that computers can actually do are essentially very simple, such as calculating the sum of two numbers, finding an address in memory, and so on. These most basic computer actions are called instructions. The so-called program is a collection of such a series of instructions. We can program computers to perform complex operations. Most of the time, the program is stored as executable files, and the process is a specific embodiment of the program. The process is the execution process of a program, and a program can be executed multiple times. Different programs have different processes. This is a matter of course, because different programs need to run at the same time and do not interfere with each other. At the same time, some processes need to communicate with each other. WindowManagerService is a program that runs in another process. It is a system-level process. Because the phone has only one screen, the system naturally manages all the Windows on the screen. Simple can be understood as the program to run is a process, multiple programs to run that naturally have multiple processes, multiple programs to communicate with each other that is cross-process communication.

What does cross-process communication cross?

When two processes need to communicate with each other, why can’t they communicate directly? Cross-process communication is literally a cross-process. Naturally, there is an obstacle between multiple processes, so we need to cross it. If there is no obstacle, we don’t need to cross it.

Process through an analogy to explain, as we have one country on earth, every country has its own territory, the concept provides a specific territory for the state of the carrier, in which most of the territory of the country is a continuous, but some countries have a lot of overseas territories, on the whole territory can be called a nation, Even the Vatican has its own territory. Process is the same reason, then, do in the process of process, because the system ensures its independence, give him the territory that is allocated memory space, at the same time, it will be isolated from the space, this is the process of isolation, cross-process communication across is that the memory space isolation, essence is the process of different memory space for data exchange.

Process isolation

Process isolation is designed to prevent process A from writing to process B in the operating system. Process isolation implementation, using virtual address space. The virtual address of process A is different from that of process B. This prevents process A from writing data to process B. Here again involves a virtual address, and the matching of a physical address, also said the program in front of the most of the time is stored as an executable file, this is the existence of physical register above, then there will be an address to represent the storage location, this is the physical address, in the implementation process isolation in nature can’t register incision, And more importantly, the physical memory is fragmented, in the above process is stored in scattered, in order to improve the efficiency of programming and improving the efficiency of physical memory, with a set of virtual address, constitute a virtual address space, the virtual address and physical address is there is a mapping relationship, for each process and assign a different virtual address space of the continuous, These virtual memory address Spaces are mapped to fragmented physical memory addresses. The general idea is to logically isolate processes by allocating different virtual memory address Spaces

Memory space

Said in front of the each process of memory space between completely isolated if no gap, then cross-process communication nature also impossible to achieve, can achieve, is the system in each process on the distribution of memory space design, each process of allocated memory space is divided into two parts, one part is the user space, is part of the kernel space.

For 32-bit systems, the virtual memory space allocated by each process is 4G(2^32), where the continuous low 0 3G is the user space of the process, and the high 3 4G is the kernel space of the process. For 64-bit systems, the user space and kernel space can reach 128T.

Our phones may only have 1 or 2 gigabytes of physical memory, so no virtual memory can be so large. Isn’t virtual memory mapped to physical memory? This involves the mechanism of memory paging, so if you are interested, you can go into more details.

User space is the private domain of a process, which cannot be accessed directly by other processes. Kernel space can be shared with other processes, and user space can interact with kernel space through the hands of the system, which provides a channel for cross-process communication.

But to say is, the application is running in user space, but also a process of user space cannot be accessed directly through a pointer to the kernel space, because the system will access to user space pointer address for inspection, so a process to communicate and other processes by the kernel space, must use the system to access the kernel space, This is the main problem that Binder mechanisms address.

Binder mechanism

Now that the groundwork is set, Binder mechanisms are formally introduced.

In conjunction with the above, there are two main problems to be solved for cross-process communication

1. The user space of a process interacts with the kernel space.

2. The kernel space data is shared among different processes.

Binder is a feature of Android designed to solve these two problems by enabling cross-process communication.

Binder architecture Design

Layered implementation

The complete Binder architecture is implemented in multiple layers, which can be roughly divided into application layer, Framework layer and driver layer. Let’s take a look at the layers from the bottom up:

Driver layer

The driver layer is located in the Linux kernel, which provides the lowest level of data transfer, object identification, thread management, call process control and other functions. The driver layer is the core of the Binder mechanism.

The Framework layer

Although driver layer provides many at the bottom of the function, but his these functions more primitive, but also because the Linux kernel driver layer, it is implemented in C, so you also cannot be used directly in the specific business, which requires a middle tier for encapsulation, and the function of the C implementation encapsulation and cohesion through JNI provides a Java call interface, This is the Framework layer.

The application layer

The part we wrote in Java during Android development, such as the introduction, is the implementation and use of Binder mechanism in specific businesses.

Role division

The Binder architecture vertically from the bottom up layer division, at the same time also on the lateral to the division of functions and responsibilities of different roles, since is the realization of communication, that naturally involves two sides, one is a communication request the party want to use a service, one is to provide a service response communication request of one party, this is a typical C/S architecture, There are the following roles

Client

Service consumer

Server

Service provider

ServiceManager

In reality, the system does not have only one Service, nor does it have more than one application running. How does a Client find the Service it needs? You need a manager that can manage various services and provide clients with a way to get the specified Service. This is a ServiceManager.

Binder drive

Client, Server, and ServiceManager are all user-space programs located in the Binder overall architecture, and Binder drivers located in the kernel space are at the bottom level to provide basic capabilities.

Binder drivers are essentially the same type of device drivers as graphics card drivers and motherboard drivers. But the difference is that it is a virtual device driver, with only software and no hardware. For a device, a simple hardware is just a body, want to make it work, also need to give it a thought, let it can call the physical ability, that this is the driver, for the system is only deny hardware driver, the driver defines a hardware of all kinds of information and provide the function of the hardware interface, A virtual device is a nonexistent entity that has no hardware but only drivers, but is treated as a device by the system.

Drivers are running in the kernel space, so it can provide a cross-process communication channel, mainly through the Linux defined driver function IOTCL function to achieve data interaction.

So there’s a general context for this, and you can look at this diagram

Working principle of Binder

In this part, we also speak from bottom to top according to the division of three layers mentioned above, so as to facilitate in-depth understanding.

Binder driver loading

It’s worth reviewing the Android startup process

The user presses the power button, the motherboard powers up, the system powers up for self-check, the boot program is loaded, and the kernel image is loaded into memory, followed by the kernel program’s kernel_init

  1. Android/kernel/among MSM – 4.9 / init/main. C
  2. Android/kernel/among MSM – 4.9 / init/main. C: kernel_init_freeable

3. The android/kernel/among MSM – 4.9 / init/main. C: do_initcall_level

Here a for loop is performed on an array to perform various subdivided initializations. The device driver initializations of interest are defined in this array, as defined in Android /kernel/ mSM-4.9 /include/ Linux /init.h

4. The android/kernel/among MSM – 4.9 / include/Linux/init. H

This function has an input parameter fn, which is the specific initialization method for each device. Binder initialization is the one we care about, which is called device_initCall (binder_init); In binder.c, the binder_init method is defined

5./kernel/drivers/staging/android/binder.c

Static const struct file_operations binder_fops = {.owner = THIS_MODULE,.poll = binder_poll, .unlocked_ioctl = binder_ioctl, .compat_ioctl = binder_ioctl, .mmap = binder_mmap, .open = binder_open, .flush = binder_flush, .release = binder_release, }; Static struct miscdevice binder_miscdev = {. Minor = MISC_DYNAMIC_MINOR,.name = "binder", .fops = &binder_fops// Bind binder driver action function}; // Binder driver initializes static int __init binder_init(void) {int ret; binder_deferred_workqueue = create_singlethread_workqueue("binder"); if (! binder_deferred_workqueue) return -ENOMEM; Binder binder_debugfS_dir_entrY_root = debuGFS_create_DIR ("binder", NULL); If (binder_debugfs_dir_entry_root) // Create directory /binder/proc binder_debuGFS_dir_entry_proc = debuGFS_create_dir ("proc", binder_debugfs_dir_entry_root); // Register binder driver ret = misc_register(&binder_miscdev); If (binder_debugfs_dir_entry_root) {binder/proc/state debugfS_create_file ("state", S_IRUGO, binder_debugfs_dir_entry_root, NULL, &binder_state_fops); Binder /proc/stats debugfS_create_file ("stats", S_IRUGO, binder_debuGFS_dir_entry_root, NULL, &binder_stats_fops); binder/proc/stats debugfS_create_file ("stats", S_IRUGO, binder_debuGFS_dir_entry_root, NULL, &binder_stats_fops);  / / create a file/binder/proc/the transactions debugfs_create_file (" the transactions ", S_IRUGO binder_debugfs_dir_entry_root, NULL, &binder_transactions_fops); Binder /proc/transaction_log debugfS_create_file ("transaction_log", S_IRUGO, binder_debugfS_dir_entry_root, binder_debugfS_dir_entry_root, &binder_transaction_log, &binder_transaction_log_fops); Binder /proc/failed_transaction_log debugfs_create_file("failed_transaction_log", S_IRUGO, binder_debugfs_dir_entry_root, &binder_transaction_log_failed, &binder_transaction_log_fops); } return ret; }Copy the code

Binder drivers are loaded before the first process when the Linux kernel initializes and looks for init.rc in the system file and starts init. You have moved from kernel space into user space

The init process then starts the Zygote process after a lot of work, and all other system processes are generated by the Zygote process fork itself.

Work with Binder drivers

Now that Binder drivers are loaded, let’s see how they work. Binder drivers exist as device files: /dev/binder: /dev/binder: /dev/binder: /dev/binder: /dev/binder: /dev/binder: /dev/binder: /dev/binder

At the heart of these are three functions:

binder_open

Use to open binder virtual devices

binder_mmap

A memory mapping method used by binder drivers to apply virtual kernel address Spaces

binder_iotcl

Perform the actual operation. The Client requests to the Server and the Server returns the request results to the Client through iocTL.

Binder’s entity struct binder_node structure exists in kernel space in the process that provides the entity, but it can have many references in other processes, so that the outline of the channel between the two processes is constructed

1.Server invokes mmap to /dev/binder devices after startup

Binder_mmap (binder_mmap, binder_mmap, binder_mmap, binder_mmap, binder_mmap, binder_mmap, binder_mmap, binder_mmap

3. The Client sends a request using the BINDER_WRITE_READ command. The request is sent to the driver first and the data to be transferred is copied from the Client’s user space to the kernel space

4. The driver notifies the Server of a request through BR_TRANSACTION, and the Server processes the request. Because this memory is also mapped in user space, the Server process’s code is directly accessible

ServerManager, a separate process like Client and Server, defines a *binder_context_mgr_node in binder drivers. This variable is ServiceManager, ServiceManager can start ahead of all the server, but it is worth noting that in order to use convenient, ServiceManager is implemented as a server, but it has a special position in the binder drive, You can easily access it.

Framework layer work

Framework layer mainly through C++ to achieve the underlying capabilities of the driver package, and through JNI, provides a Java interface for the upper application layer to call directly.

The C++ portion of the Framework layer is packaged as libbinder.so.

There are several classes in Libbinder that are defined in terms of remote local, i.e. client and server.

class describe
BpRefBase A subclass of RefBase that provides remote() methods to get remote binders
IInterface Binder The base class for service interfaces, which are typically required to provide both local and remote interfaces
BpInterface A base class for a remote interface, which is a set of interfaces that can be invoked by clients
BnInterface The base class for local interfaces, which are the set of interfaces that need to be truly implemented in the service
IBinder Binder object’s base class, BBinder and BpBinder are subclasses of this class
BpBinder Remote Binder, this class provides transact methods to send requests and is used in the BpXXX implementation
BBinder Local binders, the base class for service implementers, provide the onTransact interface to receive requests
ProcessState Represents processes that use Binder
IPCThreadState Represents threads that use Binder and encapsulates the logic that communicates with Binder drivers
Parcel Wrappers for data passed over Binder

The c++ part of the Framework has done a good job of communication function encapsulation, so how to provide the application layer to use, is through what we often say JNI connection.

There are these core classes

class describe
IInterface Interfaces inherited by Java layer Binder service interfaces
IBinder The IBinder class in the Java layer provides transact methods to invoke remote services
Binder The IBinder interface is implemented to encapsulate the implementation of JNI. Base class for Binder services in the Java layer
BinderProxy The IBinder interface is implemented to encapsulate the implementation of JNI. Provides transact methods to invoke remote services
JavaBBinderHolder JavaBBinder is stored internally
JavaBBinder Pass the onTransact call from the C++ side to the Java side
Parcel For data wrappers in the Java layer, see Parcel class analysis in the C++ layer

The IInterface, IBinder and C++ layer classes have the same name. The name is not a coincidence: not only do they have the same name, but what they do and the interfaces they contain are almost the same, with one being a C++ layer and the other a Java layer.

Binder and BinderProxy classes are also corresponding to C++ classes in addition to IInterface and IBinder. The following lists the corresponding relationship between Java layer and C++ layer classes

C++ Java layer
IInterface IInterface
IBinder IBinder
BBinder Binder
BpProxy BinderProxy
Parcel Parcel

Why Binder

Binder is more suitable for Android than traditional IPC for three reasons:

1. The performance

Performance benefits: Pipes, message queues, and sockets all require two copies of data, whereas binders require only one. It should be noted that for the underlying IPC form of the system, one less copy of data has a very large impact on overall performance

Socket: a common interface, low transmission efficiency and high cost. It is mainly used for inter-process communication across the network

Message queue and pipe: store and forward. Data is copied from the sender cache to the kernel buffer and then from the kernel buffer to the receiver cache twice

Shared memory: No copying is required, but control is complex and use is difficult

Binder: Requires only one copy of data and performs much better than message queues and pipes

How do Binder drivers copy once in kernel space? When sending data to the Server, the Client copies the communication data from its own user space to the kernel space. Because the Server and kernel share the data, the Client does not need to copy the data again. Instead, the Client directly obtains the data address from the offset of the memory address. In general, the data is copied only once.

The binder_mmap is used to enable Server and kernel Spaces to share data. Its main function is to apply for a piece of memory with the same size as user virtual memory in the kernel virtual address space, and then apply for a page size memory, which is mapped to the kernel virtual address space and user virtual memory space, so as to realize the synchronization function of user space buffer and kernel space buffer.

Traditional IPC mechanism

Binder mechanism

2. The security

Traditional IPC mechanisms cannot obtain reliable process ids from the other side. Traditional IPC only allows users to enter UID and PID into data packets. However, this is unreliable and cannot be used by malicious programs.

Android assigns its own UID to each installed APP, which Binder uses to identify the caller based on the UID of the requesting side, ensuring security

3. Stable

Binder is based on C/S architecture with clear responsibilities and clear architecture

Binder Supports both real-name and anonymous BINders, providing high security, and supports only one copy during transmission.

In essence, service_manager maintains a Handle linked list, which is convenient for clients to query with binder service name, then obtain binder agent Handle, and finally encapsulate it into binder agent object

A Binder driver is a bridge between a Binder server and a Binder client. When a Binder server is created, another Binder object is created in the Binder driver. When a client wants to access a remote Binder server, It’s all done with this Binder object

Refer to Paul. Pub/android – bin… Paul. Pub/android – bin… Paul. Pub/android – bin…