This document analyzes the service management process of Binder by ServiceManager.
Outline:
- Remove the veil of Binder
- The management of the Binder
- 1 Enable the Binder driver
- 2 becomes the unique context of the system
- 3 Enter the binder cycle
- 4 Register and obtain system services
- conclusion
- The resources
This article is about 3.7K words, about 15 minutes to read.
Android source code based on 8.0.
Remove the veil of Binder
Binders are peripherals (disembodied peripherals), like keyboards and monitors. Due to the wide variety of peripherals, operating systems such as Linux abstract file views to facilitate user use of peripherals. That is, for users, through reading and writing peripheral files, so that the operating system will send instructions to the peripheral controller, to achieve the operation of external devices.
In Linux, various peripheral files are placed under /dev:
However, these files are not peripheral drivers like those on Windows. Instead, they provide a port for the user to access the peripheral (just like file access), such as:
- /dev/console: indicates the system console
- /dev/mem: full mirror of the physical memory. Can be used for direct access to physical memory.
- /dev/kmem: full mirror of virtual memory as seen by the kernel. Can be used to access content in the kernel.
- /dev/tty0: indicates the virtual terminal
- .
Linux abstracts the file view to provide users with a unified interface. A simple procedure for operating peripherals is as follows:
// Open the peripheral file under /dev
intFd = open ("/dev/XXX ");for (int i = 0; i < 10; i++) {
// Perform read and write operations
write(fd,i,sizeof(int));
}
// Close the file
close(fd);
Copy the code
When a user reads or writes a peripheral file, Linux will find the address and content format of the peripheral controller through the peripheral file and send him appropriate instructions to operate the peripheral.
/dev/adb shell/adb shell/adb shell/adb shell/adb shell/adb shell
Binder, HWBinder, VNdbinder, we just focus on binder.
As you can see from the article “How to Start an Android Application process in one Picture,” processState. CPP has the code when an application starts its binder thread pool.
//ProcessState.cpp
sp<ProcessState> ProcessState::self(a){
// Pass in the binder peripheral file path
gProcess = new ProcessState("/dev/binder");
return gProcess;
}
//ProcessState constructor
ProcessState::ProcessState(const char *driver)
// Assign path to mDriverName
: mDriverName(String8(driver))
//1. Enable the binder driver
, mDriverFD(open_driver(driver))
,/ /...
{
//2. Map memory
mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
}
Copy the code
Let’s look at opening the Binder driver open_driver function,
//ProcessState.cpp
static int open_driver(const char *driver){
// open the peripheral file /dev/binder
int fd = open(driver, O_RDWR | O_CLOEXEC);
int vers = 0;
// Obtain the binder version for checking
status_t result = ioctl(fd, BINDER_VERSION, &vers);
size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
// Set the maximum number of binder threads to 15
result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
// Return an int fd to mDriverFD
return fd;
}
Copy the code
It looks very similar to the Linux peripherals program, except that the read and write operations are replaced by ioctl.
In computers, iocTL (input/output Control) is a system call dedicated to device input/output operations. The call passes in a device-related request code. The function of the system call depends entirely on the request code. For example, a CD-ROM driver that can eject a cd-rom drive provides a corresponding Ioctl request code. Device-independent request codes provide kernel-call permissions. The name IOCtl first appeared in Unix version 7. It is available on many Unix-like systems (such as Linux, Mac OSX, etc.), but the request code varies from system to system.
— quoted from encyclopedia ioctl
The visible IOCTL is a system call that controls the device I/O channel through which the user space communicates with the device driver.
As for why iocTL is used, it is mainly for non-standard devices (binder is a non-standard peripheral), see the IocTL background in encyclopedia.
Ioctl functions are as follows:
int ioctl(intFd, ind CMD,...).;Copy the code
The first argument, fd, is a file descriptor, such as a binder peripheral file;
The second parameter CMD is a control command, such as the instruction BINDER_SET_MAX_THREADS is “set the number of threads”, and the final ellipsis is the parameter required by each instruction, such as maxThreads indicates that the maximum number of threads is 15.
The BINDER_SET_MAX_THREADS directive is defined as follows:
#define BINDER_SET_MAX_THREADS _IOW('b', 5, __u32)
Copy the code
_IOW is a macro, and the Linux kernel provides some macros to make it easy for users to define instructions (wrapped in various arguments) :
// nr indicates the sequence number and datatype indicates the datatype, such as int
_IO(type, nr ) // Command with no arguments
_IOR(type, nr, datatype) // Read data from the driver
_IOW(type, nr, datatype) // Write data to the driver
_IOWR(type,nr, datatype) // Two-way transmission
Copy the code
The name is easy to understand. It stands for IO Read Write.
Binder is nothing more than a peripheral that operates as a file through IOCtl.
The management of the Binder
As you can see from the article “Mapping Android Services”, the init process starts a ServiceManager service that runs in a separate process to centrally manage registration and acquisition of system services.
In the main function of the ServiceManager’s entry function, service_manager.c,
//frameworks/native/cmds/servicemanager/service_manager.c
int main(int argc, char** argv){
char *driver = "/dev/binder";
//1. Enable the binder driver
struct binder_state *bs = binder_open(driver, 128*1024);
//2. Make yourself the only context manager for the entire system.
// Then other processes can find the ServiceManager to register the service
binder_become_context_manager(bs);
//3. Enter the binder loop and wait for system service registration and lookup requests
binder_loop(bs, svcmgr_handler);
}
Copy the code
The following three steps are analyzed.
1 Enable the Binder driver
BINDER_VM_SIZE = 1MB-8KB; BINDER_VM_SIZE = 1MB-8KB; So they’re not the same size,
//ProcessState.cpp
// The maximum size that a Binder communication can transmit is 1MB-4KB*2
#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
// Map memorymmap(... , BINDER_VM_SIZE, ...) ;Copy the code
Back to ServiceManager, binder_open() ‘s internal implementation binder.c:
//frameworks/native/cmds/servicemanager/binder.c
struct binder_state *binder_open(const char* driver, size_t mapsize){
struct binder_state *bs;
// Allocate space
bs = malloc(sizeof(*bs));
// Open binder driver to get file descriptor fd of type int
bs->fd = open(driver, O_RDWR | O_CLOEXEC);
// Record the incoming 128KB
bs->mapsize = mapsize;
// Map memory, record the pointer to the memory mapping area
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
return bs;
}
Copy the code
Mmap can map a file or other object into memory.
void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
Copy the code
The parameters are as follows:
- Start: indicates the start address of the mapping area. NULL indicates that the system determines the start address of the mapping area
- Length: indicates the length of the mapping area. The value is 128kb
- Prot: Expected memory protection flag, pass PROT_READ read-only
- Flags: Specifies the type of mapping object, mapping options, and whether the mapping page can be shared. Pass MAP_PRIVATE to set up a private map that is copied on write. Write to memory areas does not affect the original file
- Fd: A valid file descriptor, usually returned by the open() function
- Offset: indicates the starting point of the mapped object. Pass 0
- Return: On success, mmap() returns a pointer to the mapped region
Mmap maps a portion of the Binder driver file to the memory space based on the entry and returns a pointer to that memory space.
Finally, binder_open() returns the following bs structure:
//frameworks/native/cmds/servicemanager/binder.c
struct binder_state{
// binder driver file descriptor
int fd;
// The pointer to the memory-mapped area obtained by mmap
void *mapped;
// 128kb
size_t mapsize;
};
Copy the code
2 becomes the unique context of the system
The ServiceManager makes itself the only context manager for the entire system so that other processes can find the ServiceManager to register services,
//frameworks/native/cmds/servicemanager/binder.c
int binder_become_context_manager(struct binder_state *bs){
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
Copy the code
Visible is the aforementioned IOCtl call that sends the instruction “I ServiceManager has become the global context manager” to the Binder driver.
Binder driver code is not followed for the time being, we just need to know:
Binder_context_mgr_node for ServiceManager binder_context_mgr_node for ServiceManager Binder_context_mgr_node for ServiceManager Binder_context_mgr_node It is so special in the system that it is specified that any application must use handle 0 to access it across processes.
– Quotes from blog – Tea cup Binder
3 Enter the binder cycle
Enter the Binder loop and wait for registration and lookup requests for system services,
//frameworks/native/cmds/servicemanager/binder.c
void binder_loop(struct binder_state *bs, binder_handler func)
{
int res;
struct binder_write_read bwr;
Readbuf is used to communicate data with binder drivers
uint32_t readbuf[32];
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
// command: binder starts the loop
readbuf[0] = BC_ENTER_LOOPER;
// Send the command to binder
Ioctl (bs->fd, BINDER_WRITE_READ, & BWR);
binder_write(bs, readbuf, sizeof(uint32_t));
for (;;) { // enter the loop
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
// Sends read and write instructions to binder
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
// Parse data read from binder and pass it to svcmgr_handler
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func); }}Copy the code
The binder_parse parse logic is as follows:
//frameworks/native/cmds/servicemanager/binder.c
int binder_parse(struct binder_state *bs, struct binder_io *bio,
uintptr_t ptr, size_t size, binder_handler func){
int r = 1;
// Calculate the offset to read the last instruction
uintptr_t end = ptr + (uintptr_t) size;
while (ptr < end) {
// Read instructions sent back by binder from readbuf
uint32_t cmd = *(uint32_t *) ptr;
// Offset each column after reading
ptr += sizeof(uint32_t);
switch(cmd) { // Handle various instructions
case BR_TRANSACTION_COMPLETE:
break;
case BR_TRANSACTION:
// pass to the svcmgr_handler function for processing
res = func(bs, txn, &msg, &reply);
/ /...
break;
case BR_REPLY:
/ /...
break;
/ /...}}return r;
}
Copy the code
Then you see that the handler that was passed in, svcmgr_handler,
//frameworks/native/cmds/servicemanager/service_manager.c
int svcmgr_handler(struct binder_state *bs, struct binder_transaction_data *txn, struct binder_io *msg, struct binder_io *reply){
/ /... Omit logic for data wrapping and parsing
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
// Find the system servicehandle = do_find_service(...) ; bio_put_ref(reply, handle);case SVC_MGR_ADD_SERVICE:
// Add system services
do_add_service(...);
}
}
Copy the code
The svcmgr_handler function performs logic based on different semantic codes, such as searching for do_find_service of system services and adding do_add_service of system services.
At this point, you can see the Binder startup process for ServiceManager:
4 Register and obtain system services
The following is a brief analysis of the system service registration and acquisition, in “a map to understand the Android system services” article has been introduced to the upper logic, here directly look at do_add_service and do_find_service two methods.
1. Add system service do_add_service.
Struct svcinfo *svclist lists all system services as follows:
//frameworks/native/cmds/servicemanager/service_manager.c
struct svcinfo{
// Next service
struct svcinfo *next;
// Binder handle value for the service
uint32_t handle;
struct binder_death death;
int allow_isolated;
size_t len;
// The name given when the service was registered
uint16_t name[0];
};
Copy the code
Then you see do_add_service,
//frameworks/native/cmds/servicemanager/service_manager.c
struct svcinfo *svclist; / / list
int do_add_service(struct binder_state *bs,
const uint16_t *s, size_t len,
uint32_t handle, uid_t uid, int allow_isolated,
pid_t spid){
if(! svc_can_register(s, len, spid, uid)) {// Determine whether system services can be registered
// Only root processes, SystemServer processes, and processes declared in the allowed[] array are allowed
return - 1;
}
// Allocate space to the new node
struct svcinfo *si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
// Record the binder handle value of the service
si->handle = handle;
si->len = len;
// Record the name given when the service was registered
memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
// Appends an terminator
si->name[len] = '\ 0';
/ /... There are all kinds of assignments
// Next of the new node points to the list
si->next = svclist;
// List header
svclist = si;
}
Copy the code
As follows,
The binder handle value of type INT is assigned by the Binder driver and returned to us wrapped in a specific data structure.
The ServiceManager uses a linked list svCList to manage binder handles for system services. The structure is SvCInfo.
And corresponding to the binder driver layer, it is with the chain table binder_procs management, structure is binder_proc, in/drivers/android/binder. C,
//drivers/android/binder.c
// List header
static HLIST_HEAD(binder_procs);
/ / structure
struct binder_proc {
// List of common nodes, by its next and pprev linked list
struct hlist_node proc_node;
//4 red black trees, rb = red black
// Record information about the thread that performs the transfer action binder_thread
struct rb_root threads;
// Record the binder entity binder_node
struct rb_root nodes;
// Record binder agent binder_ref
struct rb_root refs_by_desc;
struct rb_root refs_by_node;
};
Copy the code
Here, HLIST_HEAD and Hlist_Node are used to chain linked lists. The code of binder driver layer is not expanded at present.
2. Search for system service do_find_service.
//frameworks/native/cmds/servicemanager/service_manager.c
uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid){
// Walk through the list to find the node
struct svcinfo *si = find_svc(s, len);
// Return the binder handle value
return si->handle;
}
struct svcinfo *find_svc(const uint16_t *s16, size_t len){
struct svcinfo *si;
// Walk through the list to find the node
for (si = svclist; si; si = si->next) {
if ((len == si->len) &&
!memcmp(s16, si->name, len * sizeof(uint16_t))) {
returnsi; }}return NULL;
}
Copy the code
To sum up, do_find_service for searching system service and do_add_service for adding system service are shown as follows:
conclusion
As a process that manages system services, the ServiceManager starts registering and obtaining system services through the binder driver, registering as the unique context of the system, and entering the Binder cycle. The registration and acquisition process of system services implements IPC communication based on a Binder mechanism, which is essentially a peripheral that operates as a file through iocTL system calls.
Two questions remain:
- Handle to the binders
Remote transfer to Local
- One way asynchronous mode and its serial call (async_todo), synchronous mode parallel call
Series of articles:
- Graphic | Android launch
- Graphic | a figure out the Android system services
- The illustration | a figure Android application process to ascertain the start
- (a) shallow of graphic | Binder
supplement
-
The System services are managed by the ServiceManager process, but the user-defined Service components, called IBinder handles from the onServiceConnected callback of bindService, are managed by the AMS of the SystemServer process, which is discussed below.
It’s easy to confuse the two processes, post them to consolidate…
The resources
- Books – Scenario analysis of Android system source code
- Video – HIT operating system IO and display
- Wikipedia – ioctl
- Blog – Black tea cup Binder
More sexy articles, pay attention to the original technology public account: Halliday EI