Carefully observe Binder and mmap; analyze Android interprocess communication

foreword

Binder is an IPC (inter-process communication) mechanism in the Android system, which enables components in different processes to interact and communicate with each other. In Binder, the client in one process and the server in another process usually communicate through the Binder driver. This type of communication provides security and efficiency.

In Android, Binder is widely used in various scenarios. For example, system services, application components, and multi-process communication, etc. The use of Binder is mainly done by calling the API, such as creating a Binder service, binding a Binder service, passing data, and so on. At the same time, tools like AIDL (Android Interface Description Language) can also be used to simplify the use of Binder and make the process of data transmission more convenient.

Binder principle

The basic principle of Binder is to use the Binder driver to establish a channel between different processes, and data can be transferred between processes.

In Binder communication, there are three roles: client, server and Binder driver. When the client needs to communicate with the server, it needs to be bound to the Binder object on the server. At this point, the Binder driver will perform reference counting on the object and return a Binder proxy object (Proxy), through which the client calls the method provided by the server. Through the Binder proxy object, the client can send a request message to the server and wait for the server's reply.

After the server receives the request from the client, it needs to unpack the request data and perform the required operations. After executing the operation, the server needs to package the result and return it to the client. At the same time, the server also needs to process the reference count of the Binder object itself. When the server no longer needs the Binder object, it will release the object and notify the Binder driver to process accordingly.

In Binder's message delivery, it also involves Binder's underlying transmission mechanism and related concepts, such as Binder nodes, Binder threads, and Binder caches. Understanding these concepts can help developers better understand and use Binder.

Binder usage code example

The following is a simple Binder usage example, create a simple Binder service, and transfer data through the client calling the method provided by the server.

First, create an AIDL file to define the interface provided by the server, for example:

//定义Binder服务端接口
interface IMyService {
    int add(int a, int b);
}

Then, implement this interface and create a Binder server class:

public class MyService extends Service {
    private final IBinder mBinder = new MyBinder();
    public class MyBinder extends Binder {
        public MyService getService() {
            //返回当前服务端对象
            return MyService.this;
        }
    }
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    //服务端实现接口方法
    public int add(int a, int b) {
        return a + b;
    }
}

Finally, bind this service in the client and perform data transmission through the interface provided by the server:

public class MainActivity extends AppCompatActivity {
    //定义服务端接口对象
    private IMyService myService;
    //定义服务端连接对象
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            //获取服务端接口对象
            myService = IMyService.Stub.asInterface(iBinder);
        }
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            myService = null;
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //绑定服务端
        Intent intent = new Intent(this, MyService.class);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
        //调用服务端接口
        if (myService != null) {
            try {
                int result = myService.add(1, 2);
                Toast.makeText(this, "result=" + result, Toast.LENGTH_SHORT).show();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }
    //解除服务端绑定
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(connection);
    }
}

In this example, the server implements the IMyService interface and provides an add method to transmit data on the client through the interface provided by the server. In the client, first bind the server, then call the server method through the server interface and pass parameters, obtain the data returned by the server, and finally unbind the server.

This is a simple Binder example showing the basic usage of Binder.

Binder core function mmap source code analysis

Binder is an IPC (inter-process communication) mechanism, which is often used for inter-process communication in the Android system. mmap is a core function in Binder, which is used to map data into memory.

The function prototype is as follows:

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

Parameter Description:

  • addr: Points to the starting address of the memory to be mapped, usually set to 0, which means that the operating system will automatically select a new free address.
  • length: The memory size to be mapped.
  • prot: Memory protection flag. PROT_READ indicates that the page can be read; PROT_WRITE indicates that the page can be written; PROT_EXEC indicates that the page is executable.
  • flags: memory mapping flag, usually set to MAP_SHARED or MAP_PRIVATE. MAP_SHARED means that the write operation to the memory after mapping will be synchronized to the file on the disk; MAP_PRIVATE means that the modification after mapping will not be synchronized to the disk file.
  • fd: The file descriptor that needs to be mapped into memory.
  • offset: The offset from the beginning of the file.

In Binder, the mmap function is mainly used to map the shared memory (Shared Memory) area in the Binder driver. The Binder driver uses shared memory to cache transmitted data, reducing the number of system calls and memory copies, and improving system performance. The application program can map the shared memory into its own address space through the mmap function, so as to facilitate its read and write operations.

For specific implementation, please refer to the implementation of the mmap function in the Binder driver.

In Binder's massive IPC communication, the calling frequency of the mmap function is very high, so the release and recovery of memory should be considered in the implementation to avoid problems such as memory leaks and slow release of memory.

Detailed mmap memory mapping

mmap is a way of mapping a disk file or other process memory into the process address space. It can associate part or all of a file to a contiguous virtual memory region in the process address space (also called a memory-mapped file). This method can improve the access speed of the file, because the kernel can directly read the contents of the virtual memory page into the memory every time it is accessed, without reading the file.

It should be noted that mmap is only valid for regular files, but for other types of files (such as device files), unpredictable consequences will occur. Moreover, before a file can be mapped into the process address space, the file first needs to be opened.

The prototype of mmap is as follows:

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,
           int fd, off_t offset);

The meaning of each parameter is as follows:

  1. addr: A pointer to the starting address of the memory to be mapped, usually set to NULL to indicate that it is automatically allocated by the system, or explicitly specified as an address.
  2. length: The length (in bytes) of the memory area to be mapped, usually corresponding to the size of the file. It should be noted that for a memory area mapped to a file, its length must be an integer multiple of the page size. On Linux systems, the default page size is 4KB.
  3. prot: the protection mode of the mapping area. Possible values ​​for this parameter are:
  • PROT_READ: Indicates that the mapped area is readable.
  • PROT_WRITE: Indicates that the mapped area is writable.
  • PROT_EXEC: Indicates that the mapped area is executable.

Any combination of these values ​​will do. For example, if the mapped area is both readable and writable, the prot parameter should be set to PROT_READ | PROT_WRITE.

  1. flags: used to describe the type and attributes of the mapped area. Possible values ​​are:
  • MAP_SHARED: Indicates that the mapped area is shared by multiple processes. Write operations to this area will directly affect the contents of this area in the disk file and all shared memory, while read operations to this area will read data from disk or shared memory.
  • MAP_PRIVATE: Indicates that the mapped area is private to the process. Writes to this area will only affect that process's memory, not files on disk or the same area of ​​shared memory in other processes.
  • MAP_FIXED: Indicates that the start address of the mapping area can be specified. If the addr parameter is not empty, the system will try to locate the start address of the mapping area to this address. If other objects already exist in that address range, the mapping fails.
  • MAP_ANONYMOUS: Indicates that the mapped area is not associated with a file but an anonymous memory area allocated by the kernel. When using this flag bit for mapping, the fd and off parameters must be 0.
  • MAP_DENYWRITE: Indicates that file write operations are not allowed to be mapped to this area. Only valid for shared memory, usually used to prevent the process from trying to modify the code segment in the mapped area.
  1. fd: file descriptor, used to specify the file to be mapped.
  2. offset: file offset, used to specify the starting position of the mapped area in the file.

After the mmap function is successfully executed, it will return the starting address of the mapped area, and if it fails, it will return MAP_FAILED. After using the mapped area, you need to call the munmap function to release the mapped area.

When using the mmap function, you need to pay attention to the following points:

  1. The length of the mapped area must be an integer multiple of the page size. In Linux systems, the default page size is 4KB.
  2. For the case where multiple processes access the same memory-mapped area, it is necessary to ensure that shared memory is used, otherwise it will cause data out-of-sync problems.
  3. The start address of the mapped area must be page-aligned, otherwise memory access errors may occur.
  4. Once the protection mode and attributes of the mapped area are determined, they cannot be changed.
  5. The mapped area is a contiguous piece of virtual memory, but the actual physical memory may not be contiguous. If the virtual memory exceeds the actual physical memory, then there will be memory paging.
  6. Mapped areas that use the MAP_ANONYMOUS flag are not associated with disk files and therefore cannot be used for persistent storage. Only the mapped area using the MAP_SHARED and MAP_PRIVATE flag bits can be used for file read and write operations.

To sum up, the mmap function is a convenient and efficient memory mapping method. It can map disk files or shared memory into the address space of the process to improve the efficiency of reading and writing files, and through the operation of the memory mapping area to Read and write operations on files. But pay attention to the setting of parameters and the memory management of the mapped area. For more information about Android's technical framework communication mechanism, here you can refer to the technical document "Android Core Technical Manual" . For details, please click to view.

in conclusion

Binder and mmap are both technologies related to memory operations, but their application scenarios and implementation methods are different.

Binder is an inter-process communication mechanism, which is often used for communication between different processes in the Android system. In the Binder mechanism, a process can register one or more objects of itself in the Binder driver, and other processes can realize the purpose of interacting with these objects by sending messages to the Binder driver. The Binder mechanism is actually implemented based on underlying technologies such as shared memory and inter-process semaphores. Specifically, the Binder driver will allocate a piece of shared memory as a buffer for each process to store messages and data passed between processes.

Unlike Binder, mmap is a memory mapping mechanism, which is often used to map files into the memory space of applications to achieve random access to files. In the Linux system, the mmap function can map a file to the virtual address space of a process to implement file read and write operations. When an application program accesses the mapped area, the Linux kernel will automatically convert the access to the corresponding file operation on the disk, thus realizing transparent read and write operations.

Guess you like

Origin blog.csdn.net/m0_62167422/article/details/130394607