background
Two days ago, I talked with my colleagues about Binder mechanism of Android platform. I had a brief understanding of Binder before, but a question suddenly came up during the conversation. Why not directly use the MMAP file MAP_SHARED mode? Mmap also has the efficiency advantage Binder says it has of copying once. What are the main advantages of Binder over MMAP?
I looked up some information on the Internet, and many of them were not clear, so I had to turn over the interpretation of Android source code book [1], now summarized as follows.
Communication mechanisms for Binder
The virtual address space of a process is divided into user address space and kernel address interval, corresponding to the virtual address area accessible by user mode program and the virtual address area accessible only by kernel mode program respectively.
The user address Spaces of the virtual address Spaces of different processes are isolated from each other. Process A cannot directly use the user-mode virtual address of process B. For example, process B cannot directly use the memory address obtained by process A malloc. In order to realize multi-process communication, kernel-state must be involved, because the kernel address space of different processes is shared (virtual address is the same), and the kernel address space can only be accessed by kernel-state.
Binder creates a Binder device file in the filesystem: /dev/binder, whose drivers work in kernel state. Each process communicates through this driver.
1. Binder driver initialization
Binder registers a Binder device (type MISC) with the operating system in its driver initialization function binder_init and specifies the implementation of its device file operation method (binder_fops), as shown in the code snippet below.
Binder device file /dev/binder. Binder_xxx is called inside the driver
static struct file_operations binder_fops = {
.owner = THIS_MODULE,
.poll = binder_poll,
.unlocked_ioctl = binder_ioctl,
.mmap = binder_mmap,
.open = binder_open,
.flush = binder_flush,
.release = binder_release,
};
// Define binder as MISC devices
static struct miscdevice binder_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "binder",
.fops = &binder_fops
};
// Initializer function for binder device drivers
static init __init binder_init(void)
{...// Register with binder devices, as defined above in static variable definitionsret = misc_register(&binder_miscdev); . }Copy the code
Binder drivers operating in kernel mode call binder_open when a user-mode application calls open on /dev/binder files, and binder_mmap when mmap is invoked.
2. Binder file operation
Here are the main things that go on inside these file operations:
-
The open – binder_open
- Create the binder_PROC structure proc and initialize and register it
- Each process that communicates with Binder has a corresponding binder_PROC structure containing process-related information
- Storing the proc in a struct file structure (filep->private_data) associated with the file handle will allow subsequent methods such as mmap to fetch the filEP to get the proc
- Create the binder_PROC structure proc and initialize and register it
-
Mmap – binder_mmap
- After calling open, you also need to call Mmap to map the Binder device files to the process virtual address space to use the Binder interprocess communication mechanism
- /dev/binder is a virtual appliance that mmap uses to allocate kernel buffers to current processes to transfer interprocess communication data
Note: The parameters passed in to mmap in user mode are: PROT_READ, MAP_PRIVATE, that is, the mapped memory region is read-only and private in user mode — it cannot be accessed by other processes through Mmap calls, and is written to by Binder in another way that binder controls its permissions. Binder also provides many peripheral functions (such as serviceManager, etc.) in addition to data transfer. All of them are encapsulated in /dev/binder device files. If mMAP is used directly, permission control cannot be closed and system robustness is damaged.
Here’s an overview of the main work in binder_mmap:
//filep corresponds to the handle returned by open(), where filep->private_data holds the struct binder_proc pointer // VMA represents a segment of user-mode address space (virtual address space) static int binder_mmap(struct file *filep, struct vm_area_struct *vma) { 1.Ensure that the memory size specified by the VMA does not exceed4M 2.Check that the memory region specified in the VMA is read-only (not write, not copy)3.Allocate a virtual address space (area) in the kernel address space that is the same size as the VMA4.Allocate actual physical pages for vMA and area (in proc->buffers) and map the virtual addresses in VMA and area to the addresses of allocated physical pages * Actually allocate only one physical page first, and allocate more physical pages later if needed}Copy the code
A process using the Binder interprocess communication mechanism must map Binder device files to its own address space before the Binder driver assigns it a kernel buffer for transferring interprocess communication data.
- Binder drivers assign kernel buffers to processes with two addresses, user-space addresses (corresponding to the VMA in the above snippet) and kernel space addresses (corresponding to the area in the snippet)
- The actual kernel buffer data resides in a series of physical pages that are mapped to both the user address space and the kernel address space of the process
- When a Binder driver wants to transfer a piece of data to a process B, it stores the piece of data in a kernel buffer allocated for B, then tells B the user-space address of the kernel buffer, and B reads the data from the buffer.
How does A pass data to B? This is where the write method is used. Inside the driver, both write/read methods are converted to binder_ioctl(BINDER_WROTE_READ…). Methods.
- Write/read – binder_ioctl (BINDER_WRITE_READ)
- First call copy_from_user to copy some meta data (BWR) in the user address space to the kernel (note: not actual data) for use in subsequent steps
- If there is data to write in the BWR, call binder_thread_write to write the data
- Binder_thread_write calls copy_from_user to copy data from the user area to the target kernel buffer
- If there is data in the BWR to read, binder_thread_read is called to read
- Only meta data is manipulated, without actually copying the data, because the consumer of the data (such as process B) can read the buffer data directly with a known user-mode virtual address
Write in the back
This paper briefly combs the underlying communication mechanism of Binder. Limited by the author’s knowledge, many details are not elaborated in detail. Readers are welcome to comment on any mistakes or misinterpretations in the comments section. In addition, for readers who want more details or other “superstructures,” references or other more detailed articles are recommended.
The resources
- [1] Scenario Analysis of Android System Source Code by Luo Shengyang