Talk to the interviewer about Binder (1) and the interviewer about binder
The interviewer was too busy. After a long time, he remembered me, so he called me again by phone. He said he wanted to know more about the Binder. In fact, I don't know much about the Binder, but I think people have asked me. If I don't want to say anything, where should I put this face? Think about it. In the last lecture, we mentioned two sides of the communication in the Binder: server and client. That is to say, the Binder uses the Client-Server communication mode. One process acts as the Server and multiple processes act as the Client, and cross-process requests from the Server for services.
We can think of the server settings in Web development. How do we know which server sends a request to which application? One is the IP address and the other is the port number. If there are multiple applications on the server, we still need to know the specific application name, right. In short, a server must have a fixed access point or address to accept the request. For example, the last time we said that the ServiceManager handle is always 0, this is a fixed access point.
Now that we know where the other party is, Good, we can communicate, the problem arises. How can we communicate? Speaking to the British and Americans, we know that we can speak in English, Chinese, Mandarin, and local people. We can speak dialects. On the network, this is the protocol that specifies the communication Specifications and methods, such as the HTTP protocol. I know that you use get to obtain information, and post to push information. Obviously, there must be such protocols and specifications in the Binder mechanism for data communication.
In the previous article, we mentioned that when the ServiceManager process is not called ServiceManager, we use the BINDER_SET_CONTEXT_MGR command to make it a ServiceManager that everyone depends on. BINDER_SET_CONTEXT_MGR is one of the contents specified in the Binder mechanism. The Binder develops the Command-Reply protocol and interacts with the driver through the ioctl Command to tell the driver what commands to perform operations on the data, as shown below:
#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read)#define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, int64_t)#define BINDER_SET_MAX_THREADS _IOW('b', 5, size_t)#define BINDER_SET_IDLE_PRIORITY _IOW('b', 6, int)#define BINDER_SET_CONTEXT_MGR _IOW('b', 7, int)#define BINDER_THREAD_EXIT _IOW('b', 8, int)#define BINDER_VERSION _IOWR('b', 9, struct binder_version)
As we have mentioned in BINDER_SET_CONTEXT_MGR, there are also very important BINDER_WRITE_READ commands. As we all know, they must be about read/write commands.
BINDER_WRIT_READ: Write Data to or from the driver and then read the data. Both the write and read operations are asynchronous requests. If both write and read operations are performed, these operations are synchronous requests. BINDER_SET_MAX_THREADS: inform the driver that the maximum capacity of the receiver thread pool is BINDER_SET_CONTEXT_MGR: set the current process to ServiceManager. There can be only one ServiceManager process in the system. BINDER_THREAD_EXIT: notifies the driver that the current process has exited. BINDER_VERSION: Get the current driver version. BINDER_WRITE_READ protocol format (command + Data) Write
The corresponding ioctl commands start with BC and BR, respectively indicating the meaning of the commands and returned results.
BinderCommand - BC BC_TRANSACTION = _IOW_BAD('c', 0, struct binder_transaction_data), BC_REPLY = _IOW_BAD('c', 1, struct binder_transaction_data), BC_REGISTER_LOOPER = _IO('c', 11), BC_ENTER_LOOPER = _IO('c', 12), BC_EXIT_LOOPER = _IO('c', 13),BinderReply - BR BR_ERROR = _IOR_BAD('r', 0, int), BR_OK = _IO('r', 1), BR_SPAWN_LOOPER = _IO('r', 13), BR_TRANSACTION = _IOR_BAD('r', 2, struct binder_transaction_data), BR_REPLY = _IOR_BAD('r', 3, struct binder_transaction_data), BR_TRANSACTION_COMPLETE = _IO('r', 6),
The functions of the preceding commands are as follows: write data to the driver:
BC_TRANSACTION/BC_REPLY should be the BC_REGISTER_LOOP for data interaction with the driver. The Server notifies the driver that a new line has been created in the thread pool. The BC_ENTER_LOOP notification driver has already entered the LOOP and can receive data. BC_EXIT_LOOP indicates that the thread has exited the LOOP and no longer receives data.
Data format read from the driver
BR_NOOPBR_ERROR/br_okbr_spawn_lon_: the driver informs the receiver that there is no thread, and it needs to be created or no longer receives data. BR_TRANSACTION/BR_REPLY: corresponding to BC_TRANSACTION/BC_REPLYBR_TRANSACTION_COMPLETE. The driver will inform the sender of the result whether the result is successful or not.
Of course, if you want to learn more about the specific points and deeper points, you need to take a good look at the source code. At this time, I secretly looked at the interviewer's face, which is awesome.
We can see that its parameter has binder_transaction_data, right? This is definitely the result we returned after we write from the user space to the kernel space and find the corresponding Server from the driver. The following is the structure of binder_transaction_data.
struct binder_transaction_data {union {size_t handle;void *ptr;} target;void *cookie;unsigned int code;unsigned int flags;pid_t sender_pid;uid_t sender_euid;size_t data_size;size_t offsets_size;union {struct {const void *buffer;const void *offsets;} ptr;uint8_t buf[8];} data;};
We can see a union structure, a handle, and a * ptr pointer. As mentioned in the previous interview, references between different processes are called handles, and references in the same process are called pointers, that is, references to virtual addresses in the process space. When the Client sends a request to the Server, it only knows that the Server is in another process and certainly cannot know its corresponding address reference. what it knows is only a handle that ServiceManager tells it. When the driver receives this request, the driver knows that the handle corresponds to a reference in another process (Server. Therefore, handle will be replaced with * ptr, so that the Server process can use this * ptr directly.
The interviewer may not quite understand why the driver knows that handle and * ptr pointers correspond to each other? I deeply feel that I have moved on again... Reclaim it! The interviewer's wisdom is infinite. He knows that I have talked so much about it. In fact, he only talked about two things: 1) the Binder is based on the CS architecture and has a corresponding protocol, A large number of commands and data structures are defined to transmit data between different processes. 2) data is transmitted between different processes through the driver's ioctl command to read and write data, that is, a lot of data is interacted in the driver. But the question comes again. What is the whole process like? Can it be specific?
The interviewer seems to be getting angry. Well, let's think about it again. How can this problem be solved? Server and Client, the so-called Client must first have a Server to request. No. You should have ServiceManager before. Yes, but ServiceManager is also a Server. This should be the case: 1) when the Server is started, it will create a Binder object whose handle is 0, such as BpBinder (0). When BINDER_SET_CONTEXT_MGR is set, when the driver receives this command, it knows that it wants to set the current process to ServiceManager, so it will create a Binder node (BinderNode) for the current Binder object ), it records mappings such as 0-> ServiceManager. 2) When another Server is started, it will also create a Binder entity named XXXManagerService, but its handle will not be 0. What is it? I don't know. When the Binder object is put into the driver, the driver will also create a Binder node for it and create a random handle for it, for example, 1. So we know that in the driver, 1-> XXXManagerService references. It enters the driver to search for handle 0 and registers itself, that is, addService. Of course, the driver will find the handle 0 and find that it corresponds to ServiceManager. Then, the corresponding data will be processed by ServiceManager, which of course uses the memory of the kernel space. In ServiceManager, a service list svclist stores handles corresponding to different service names. For example, ServiceManager saves such a corresponding 1-> "XXXManagerService" in its own space ". OK. Now, XXXManagerService registers itself to ServiceManager. 3) a Client is also started. It is looking for resources of the XXXManagerService request point. However, it only knows the name of the XXXManagerService, but does not know its specific handle, it's just the end of the world. But it knows that there is something called ServiceManager, and it may know where XXXManagerService is, right, it knows that its handle is 0, so it finds a 0 handle in the driver, find and communicate with him, saying that it is looking for XXXManagerService, that is, findService. After XXXManagerService was registered, ServiceManager passed its handle to the Client. Now the Client finally knows the address (handle) of XXXManagerService, so it repacks the data, this time, the handle is not 0, but 1, and the driver is plunged to continue to do what to do.
It seems like an image, and the interviewer seems to understand it, with a wonderful expression.
Okay, just give a picture. I hope I can give the interviewer a score.
After the interviewer finishes reading the paper, he still has no facial expression. I am so anxious. What is it like? In combination with the above, this figure should be understandable. It may be that the painting is too bad.