This article was first published on the wechat public account “Backend Technical Officer”

Associated series Android AOSP Basic series Android system startup series Application process startup series Android In-depth understanding of the four major components of Android Context series Android in-depth understanding of JNI series WMS series AMS series Android package management mechanism series Android input system series

preface

In the last article, we take MediaPlayerService as an example, explained how the system service is registered (addService), since there is a registration is necessary to obtain, but before understanding the access to services, we had better understand the start process of ServiceManager, This helps to understand the process of registering and acquiring system services.

Another point to note is that in order to understand the startup process of ServiceManager, you need to view the source code of Kernel Binder, which is part of the Kernel source code, AOSP source code is not included in the Kernel source code, so it needs to be downloaded separately. See Android AOSP foundation (ii) AOSP source code and kernel source code download this article.

1. The entry function of the ServiceManager

The ServiceManager is started by the init process, specifically when the init.rc configuration file is parsed. The init process is started at system startup, and so is the ServiceManager. If you don’t understand the init process and init.rc, start the init process. The rc file contains scripts written by the Android Init Language that contain five types of statements: Action, Commands, Services, Options, and Import. The init.rc file was split in Android 7.0, one rc file per service. ServiceManager startup scripts in ServiceManager. In the rc: frameworks/native/CMDS/ServiceManager/ServiceManager rc

service servicemanager /system/bin/servicemanager
    class core animation
    user system/ / 1group system readproc
    critical/ / 2onrestart restart healthd  
    onrestart restart zygote
    onrestart restart audioserver
    onrestart restart media
    onrestart restart surfaceflinger
    onrestart restart inputflinger
    onrestart restart drm
    onrestart restart cameraserver
    onrestart restart keystore
    onrestart restart gatekeeperd
    writepid /dev/cpuset/system-background/tasks
    shutdown critical
Copy the code

Service is used to notify the init process creation process, called servicemanager the servicemanager application process execution path to/system/bin/servicemanager. The keyword user in comment 1 indicates that Servicemanager runs as the system user. The keyword critical in comment 2 indicates that Servicemanager is a critical service in the system. If a critical service exits, the system restarts. Processes decorated with the onrestart keyword, such as Zygote, Media, surfaceFlinger, and so on, are started when the system restarts.

Servicemanager entry function in the service_manager. C: frameworks/native/CMDS servicemanager/service_manager. C

int main(int argc, char** argv)
{
    struct binder_state *bs;/ / 1
    union selinux_callback cb;
    char *driver;

    if (argc > 1) {
        driver = argv[1];
    } else {
        driver = "/dev/binder";
    }

    bs = binder_open(driver, 128*1024);/ / 2.if (binder_become_context_manager(bs)) {/ / 3
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return - 1; }...if(getcon(&service_manager_context) ! =0) {
        ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
        abort(a); } binder_loop(bs, svcmgr_handler);/ / 4

    return 0;
}
Copy the code

The binder_state structure at comment 1 is used to store three bits of information for binder:

struct binder_state
{
    int fd; // File descriptor for binder devices
    void *mapped; // Binder device files are mapped to the process address space
    size_t mapsize; // specifies the size of the address space allocated by the system after memory mapping. The default size is 128KB
};
Copy the code

The main function does three things: 1. Binder_open is called at comment 2 to open binder device files and request 128K bytes of memory. 2. Call the binder_become_Context_manager function at note 3 to register the Servicemanager as the context manager for the Binder mechanism. 3. At comment 4, the binder_loop function is called to wait for and process requests from the client.

Now let’s talk about each of these three things.

1.1 Opening Binder Devices

The binder_open function is used to open the binder device file and map it to the process’s address space, as shown below.

frameworks/native/cmds/servicemanager/binder.c

struct binder_state *binder_open(const char* driver, size_t mapsize)
{
    struct binder_state *bs;
    struct binder_version vers;

    bs = malloc(sizeof(*bs));
    if(! bs) { errno = ENOMEM;return NULL;
    }

    bs->fd = open(driver, O_RDWR | O_CLOEXEC);/ / 1
    if (bs->fd < 0) {
        fprintf(stderr."binder: cannot open %s (%s)\n",
                driver, strerror(errno));
        goto fail_open;
    }
    // Get the version of Binder
    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == - 1) || (vers.protocol_version ! = BINDER_CURRENT_PROTOCOL_VERSION)) {/ / 2
        fprintf(stderr."binder: kernel driver version (%d) differs from user space version (%d)\n",
                vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);
        goto fail_open;
    }

    bs->mapsize = mapsize;
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);/ / 3
    if (bs->mapped == MAP_FAILED) {
        fprintf(stderr."binder: cannot map device (%s)\n",
                strerror(errno));
        goto fail_map;
    }
    return bs;

fail_map:
    close(bs->fd);
fail_open:
    free(bs);
    return NULL;
}
Copy the code

Note 1 is used to open binder device files, which will be analyzed later. The ioctl function in comment 2 is used to retrieve the Binder version. If the Binder version is not available or if the kernel and user-space binders are not of the same version, the Binder goes directly to the fail_open tag to release the Binder memory. The mmap function is called in note 3 to perform memory mapping, which generally means that binder device files are mapped to the process’s address space of mapsize, i.e. 128K. Once mapped, the starting address and size of the address space are stored in the mapped and mapsize variables in the binder_state structure.

The open function, in particular, calls the binder_open function of the Kernel Binder section, which is in the Kernel source code. The version shown here is Goldfish3.4.

The Intel X86 CPU provides four privilege levels from 0 to 3. The smaller the number, the higher the privilege level. The Linux operating system uses two privilege levels: 0 and 3, which correspond to the kernel state and user state respectively. The privilege level of the user mode is low. Therefore, a process in the user mode cannot actively access data in the kernel space without a system call. In this way, users cannot enter the kernel space shared by all processes at will, which protects the data. Let’s talk about user and kernel modes. When a process is in user mode while executing the user’s own code, such as the open function, which runs in user space, the current process is in user mode. A process is in Kernel state when it executes in Kernel code because of a system call. For example, the open function is found in Kernel binder_open through the system call (__open()). Binder_open runs in Kernel space. The current process is switched from user mode to kernel mode.

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

static int binder_open(struct inode *nodp, struct file *filp)
{   // represents the Binder process
	struct binder_proc *proc;/ / 1
	binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
		     current->group_leader->pid, current->pid);
    // Allocate memory space
	proc = kzalloc(sizeof(*proc), GFP_KERNEL);/ / 2
	if (proc == NULL)
		return -ENOMEM;
	get_task_struct(current);
	proc->tsk = current;
	INIT_LIST_HEAD(&proc->todo);
	init_waitqueue_head(&proc->wait);
	proc->default_priority = task_nice(current);
    / / binder synchronization locks
	binder_lock(__func__);

	binder_stats_created(BINDER_STAT_PROC);
	hlist_add_head(&proc->proc_node, &binder_procs);
	proc->pid = current->group_leader->pid;
	INIT_LIST_HEAD(&proc->delivered_death);
	filp->private_data = proc;/ / 3
    //binder synchronizes lock releasebinder_unlock(__func__); .return 0;
}
Copy the code

The binder_proc structure at note 1 represents the binder process used to manage various binder information. Comment 2 is used to allocate memory space for binder_proc. Note 3 assigns binder_proc to the file pointer’s private_data variable, which will be mentioned again in section 1.2.

1.2 Register as a context manager for the Binder mechanism

The binder_become_Context_manager function is used to register the Servicemanager as a context manager for the Binder mechanism, which has only one manager in the entire system, as shown in the code below. 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

Driven ioctl function will be called Binder binder_ioctl function, binder_ioctl function code is more, here the interception BINDER_SET_CONTEXT_MGR parts of the processing, the code is shown below. kernel/goldfish/drivers/staging/android/binder.c

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	int ret;
	struct binder_proc *proc = filp->private_data; / / 1
	struct binder_thread *thread;
	unsigned int size = _IOC_SIZE(cmd);
	void __user *ubuf = (void __user *)arg;
	trace_binder_ioctl(cmd, arg);

	ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
	if (ret)
		goto err_unlocked;

	binder_lock(__func__);
	thread = binder_get_thread(proc);/ / 2
	if (thread == NULL) {
		ret = -ENOMEM;
		goto err;
	}

	switch (cmd) {
    ...
	case BINDER_SET_CONTEXT_MGR:
		if(binder_context_mgr_node ! =NULL) {/ / 3
			printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");
			ret = -EBUSY;
			goto err;
		}
		ret = security_binder_set_context_mgr(proc->tsk);
		if (ret < 0)
			goto err;
		if(binder_context_mgr_uid ! =- 1) {/ / 4
			if(binder_context_mgr_uid ! = current->cred->euid) {/ / 5
				printk(KERN_ERR "binder: BINDER_SET_"
				       "CONTEXT_MGR bad uid %d ! = %d\n",
				       current->cred->euid,
				       binder_context_mgr_uid);
				ret = -EPERM;
				gotoerr; }}else
			binder_context_mgr_uid = current->cred->euid;/ / 6
		binder_context_mgr_node = binder_new_node(proc, NULL.NULL);/ / 7
		if (binder_context_mgr_node == NULL) {
			ret = -ENOMEM;
			goto err;
		}
		binder_context_mgr_node->local_weak_refs++;
		binder_context_mgr_node->local_strong_refs++;
		binder_context_mgr_node->has_strong_ref = 1;
		binder_context_mgr_node->has_weak_ref = 1;
		break; . err_unlocked: trace_binder_ioctl_done(ret);return ret;
}
Copy the code

Note 1 assigns the private_data variable in the file pointer to binder_proc, which is a binder_PROC structure as described in the binder_open function. The binder_get_thread function in comment 2 is used to get binder_thread, the binder_thread structure refers to binder threads, The binder_get_thread function internally looks for the binder_thread from the passed parameter binder_proc and returns it if it finds it, or creates a new binder_thread if it does not. Binder_context_mgr_node = binder_context_node = binder_context_node = binder_context_node = binder_context_node = binder_context_node = binder_context_node = binder_context_node = binder_context_node Binder context managers are not re-registered and therefore goto err tags. Binder_context_mgr_uid the binder_context_mgr_uid global variable at note 4 represents the valid user ID of the process that is registered with Binder’s mechanism context manager. If this value is not -1, Binder’s context manager has already been registered with other processes. Therefore, in comment 5, determine whether the valid user ID of the current process is equal to binder_context_mgr_uid and not equal to the goto to err tag. Note 6 assigns the valid user ID of the current process to the global variable binder_context_mgr_uid. The binder_new_node function is also called in comment 7 to create a Binder object and assign it to the global binder_context_mgr_node variable.

1.3 Waiting for and processing requests from the client

After a Servicemanager is successfully registered as a context manager for the Binder mechanism, the Servicemanager is the “manager” for the Binder mechanism. It needs to process client requests during system running. This is done through an infinite loop, and the function that does this is binder_loop. frameworks/native/cmds/servicemanager/binder.c

void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;
    uint32_t readbuf[32];

    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;

    readbuf[0] = BC_ENTER_LOOPER;
    binder_write(bs, readbuf, sizeof(uint32_t));/ / 1

    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;

        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);/ / 2

        if (res < 0) {
            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
            break;
        }

        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);/ / 3
        if (res == 0) {
            ALOGE("binder_loop: unexpected reply? ! \n");
            break;
        }
        if (res < 0) {
            ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
            break; }}}Copy the code

Note 1 writes the BC_ENTER_LOOPER directive to the Binder driver via the binder_write function, making the current thread (the ServiceManager’s main thread) a Binder thread that can handle interprocess requests. The BINDER_WRITE_READ instruction is used to check for new requests in Binder drivers. If there are new requests, the binder_parse function at comment 3 is used to handle them. If not, the current thread sleeps in the Binder driver, waiting for new interprocess requests.

Since the binder_write function’s call chain involves interaction between kernel space and user space, I’ll focus here.

frameworks/native/cmds/servicemanager/binder.c

int binder_write(struct binder_state *bs, void *data, size_t len)
{
    struct binder_write_read bwr;/ / 1
    int res;

    bwr.write_size = len;
    bwr.write_consumed = 0;
    bwr.write_buffer = (uintptr_t) data;/ / 2
    bwr.read_size = 0;
    bwr.read_consumed = 0;
    bwr.read_buffer = 0;
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);/ / 3
    if (res < 0) {
        fprintf(stderr."binder_write: ioctl failed (%s)\n",
                strerror(errno));
    }
    return res;
}
Copy the code

The binder_write_read structure is defined in comment 1, and the following code assigns a value to the BWR. Note that the value of data at comment 2 is BC_ENTER_LOOPER. BINDER_WRITE_READ (BINDER_WRITE_READ, BINDER_WRITE_READ, BINDER_WRITE_READ, BINDER_WRITE_READ, BINDER_WRITE_READ, BINDER_WRITE_READ, BINDER_WRITE_READ, BINDER_WRITE_READ, BINDER_WRITE_READ, BINDER_WRITE_READ)

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

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{...void __user *ubuf = (void__user *)arg; .switch (cmd) {
	case BINDER_WRITE_READ: {
		struct binder_write_read bwr;
		if(size ! =sizeof(struct binder_write_read)) {
			ret = -EINVAL;
			goto err;
		}
		if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {/ / 1
			ret = -EFAULT;
			goto err;
		}
		binder_debug(BINDER_DEBUG_READ_WRITE,
			     "binder: %d:%d write %ld at %08lx, read %ld at %08lx\n",
			     proc->pid, thread->pid, bwr.write_size, bwr.write_buffer,
			     bwr.read_size, bwr.read_buffer);

		if (bwr.write_size > 0) {/ / 2
			ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);/ / 3
			trace_binder_write_done(ret);
			if (ret < 0) {
				bwr.read_consumed = 0;
				if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
					ret = -EFAULT;
				gotoerr; }}... binder_debug(BINDER_DEBUG_READ_WRITE,"binder: %d:%d wrote %ld of %ld, read return %ld of %ld\n",
			     proc->pid, thread->pid, bwr.write_consumed, bwr.write_size,
			     bwr.read_consumed, bwr.read_size);
		if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {/ / 4
			ret = -EFAULT;
			goto err;
		}
		break; }...return ret;
}
Copy the code

The copy_from_user function in Note 1 is essential to Binder. In this case, it is used to copy user-space data UBUF and save it to the kernel data BWR (binder_write_read structure). When BWR has data in its input cache, the binder_thread_write function at comment 3 is called to handle the BC_ENTER_LOOPER protocol, which internally sets the state of the target thread to BINDER_LOOPER_STATE_ENTERED. The target thread is then a Binder thread. Note 4 uses the copy_to_user function to copy the kernel space data BWR to user space.

2. To summarize

ServiceManager startup process is actually the analysis of ServiceManager entry function, in the entry function to do three things, this article in-depth into the kernel source code to analyze these three one by one, because of the more functions involved, this article only introduces what we need to master, The rest you can read for yourself, such as the binder_thread_write and copy_to_user functions.

For more information, check out my independent blog’s body of knowledge: liuwangshu.cn/system/