编程知识 cdmana.com

Principle of Android binder (4) startup process of servicemanager

  • Binder principle
  • Android Frame layer

This article was first published on WeChat public 「 Liu Wangshu 」


Preface

In the last article , We use MediaPlayerService For example , Explains how system services are registered (addService), Since there is registration, it is necessary to obtain , But before you know how to get services , We'd better understand first ServiceManager Start up process of , This is more helpful to understand the registration and acquisition process of system services .

Another point to note is , If you want to know ServiceManager Start up process of , Need to view Kernel Binder Part of the source code , This part of the code in the kernel source code ,AOSP The source code does not include the kernel source code , So it needs to be downloaded separately , see Android AOSP Basics ( Two )AOSP Source code and kernel source code download This article

1.ServiceManager The entry function of

ServiceManager yes init The process is responsible for starting , Specifically, it is in the analysis init.rc Configuration file ,init Processes are started when the system starts , therefore ServiceManager The same is true , incomprehension init The process and init.rc You can see Android System startup process ( One ) analysis init Process startup process This article .rc The inside of the document is made up of Android Initialization language (Android Init Language) Script written , It mainly contains five types of statements :Action、Commands、Services、Options and Import. stay Android 7.0 Chinese vs init.rc The file is split , One for each service rc file .ServiceManager The startup script is in servicemanager.rc in :frameworks/native/cmds/servicemanager/servicemanager.rc

service servicemanager /system/bin/servicemanager    class core animation    user system  //1    group system readproc    critical //2    onrestart 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

service Used to inform init The process is created with the name servicemanager The process of , This servicemanager The path of the process execution program is /system/bin/servicemanager. notes 1 Key words of user explain servicemanager It's for users system The identity of the running , notes 2 Situated critical explain servicemanager It's a key service in the system , Key services don't exit , If you quit , The system will restart , When the system restarts, it will start to use onrestart Keyword decorated process , such as zygote、media、surfaceflinger wait .

servicemanager The entry function of is in service_manager.c in :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();    }    binder_loop(bs, svcmgr_handler);//4    return 0;}

notes 1 Situated binder_state Structures are used to store binder The three messages of :

struct binder_state{    int fd; //binder The device's file descriptor     void *mapped; //binder The device file is mapped to the address space of the process     size_t mapsize; // After memory mapping , The size of the address space allocated by the system , The default is 128KB};

main Function does three main things :1. notes 2 In the call binder_open Function is used to open binder Device file , And apply 128k Byte size memory space .2. notes 3 In the call binder_become_context_manager function , take servicemanager Register as Binder The context manager of the mechanism .3. notes 4 In the call binder_loop function , Loop waiting and processing client The request from the client .

Now I will explain these three things separately .

1.1 open binder equipment

binder_open Function is used to open binder Device file , And map it to the address space of the process , 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;    }    // obtain Binder Of version    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;}

notes 1 Is used to open binder Device file , It will be analyzed later . notes 2 Situated ioctl Function is used to get Binder Version of , If you can't get the kernel space or user space binder Not the same version will directly goto To fail_open label , Release binder Of memory space . notes 3 In the call mmap Function for memory mapping , Generally speaking, it means that binder The device file is mapped to the address space of the process , The size of the address space is mapsize, That is to say 128K. After mapping, the starting address and size of the address space will be saved in binder_state In structure mapped and mapsize variable .

Let's repeat here open function , It will be called Kernel Binder Part of the binder_open function , This part of the source code is in the kernel source code , The code version shown here is goldfish3.4.

User mode and kernel mode Insert a knowledge point temporarily : User mode and kernel mode Intel Of X86 Architecturally CPU Provides 0 To 3 Four privilege levels , The smaller the number is. , The higher the authority ,Linux The operating system mainly uses 0 and 3 Two privilege levels , Corresponding to kernel state and user state respectively . The privilege level of user mode is low , Therefore, the process in the user mode can not actively access the data in the kernel space without system call , In this way, users can't enter the kernel space shared by all processes at will , It's protective . Let's talk about user mode and kernel mode . When a process is in user mode while executing user's own code , such as open function , It runs in user space , The current process is in user mode . When a process is executed in kernel code because of system call, it is in kernel state , such as open Functions are called through the system (__open() function ), We found it open Function in Kernel Binder The corresponding function is binder_open, At this time binder_open Running in kernel space , The current process switches from user mode to kernel mode .

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

static int binder_open(struct inode *nodp, struct file *filp){   // representative 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 lock     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 Synchronization lock release     binder_unlock(__func__);    ...    return 0;}

notes 1 Situated binder_proc The structure represents binder process , Used to manage binder All kinds of information . notes 2 To serve for binder_proc Allocate memory space . notes 3 General binder_proc Assign a value to file Pointer private_data Variable , hinder 1.2 This is mentioned again in the section private_data Variable .

1.2 Register as Binder The context manager of the mechanism

binder_become_context_manager The function is used to servicemanager Register as Binder The context manager of the mechanism , There is only one manager in the whole system , The code is as follows .frameworks/native/cmds/servicemanager/binder.c

int binder_become_context_manager(struct binder_state *bs){    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);}

ioctl Function will call Binder Driven binder_ioctl function ,binder_ioctl More function code , Here we intercept BINDER_SET_CONTEXT_MGR The processing part of , The code is as follows .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;                goto err;            }        } 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;}

notes 1 General file In the pointer private_data A variable is assigned to binder_proc, This private_data Variable in binder_open Function said , It's a binder_proc Structure . notes 2 Situated binder_get_thread Function is used to get binder_thread,binder_thread Structure refers to binder Threads ,binder_get_thread The internal part of the function will be from the parameter passed in binder_proc Search for binder_thread, If it is found, it will be returned directly , If the query fails, a new one will be created binder_thread And back to . notes 3 Global variable at binder_context_mgr_node It stands for Binder The context manager of the mechanism corresponds to a Binder object , If it's not for NULL, It means that I have been registered as Binder The context manager of ,Binder The context manager cannot be registered repeatedly , So it will goto To err label . notes 4 Global variable at binder_context_mgr_uid Representative registered Binder The effective user of the process of the mechanism context manager ID, If its value is not -1, This indicates that a process has been registered before Binder The context manager of , So in the notes 5 To determine the valid user of the current process ID Is it equal to binder_context_mgr_uid, It's not equal to goto To err label . If the comment is not satisfied 4 Conditions , Indicates that there was no process registration before Binder The context manager of the mechanism , It'll be in the comments 6 The valid user of the current process will be ID Assign to global variable binder_context_mgr_uid, In addition, there will be comments 7 In the call binder_new_node Function to create a Binder Object and assign it to a global variable binder_context_mgr_node.

1.3 Loop waiting and processing client The request from the client

servicemanager Successfully registered as Binder After the context manager of the mechanism ,servicemanager Namely Binder The mechanism “ main ” 了 , It needs to be handled during system operation client End request , because client The request on the client side is not sure when to send , So we need to do it through infinite loops , The function that implements this requirement 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;        }    }}

notes 1 General BC_ENTER_LOOPER Command passed binder_write Function is written to Binder Driving medium , So the current thread (ServiceManager The main thread ) It becomes a Binder Threads , In this way, requests between processes can be processed . Calling comments in an infinite loop 2 Situated ioctl function , It's constantly used BINDER_WRITE_READ Command query Binder Is there a new request in the driver , If there is, give it to the notes 3 Situated binder_parse Function processing . without , The current thread will be in Binder Driving sleep , Waiting for new interprocess requests .

because binder_write The call chain of functions involves the interaction between kernel space and user space , So here is the emphasis .

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;}

notes 1 Definition binder_write_read Structure , The next code is for bwr Assign a value , What needs to be noted is , notes 2 Situated data The value of is BC_ENTER_LOOPER. notes 3 Situated ioctl The function will bwr The data in is sent to binder drive , We already know that ioctl Function in Kernel Binder The corresponding function in is binder_ioctl, This function has been analyzed before , Here we intercept BINDER_WRITE_READ Command processing part .

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;                goto err;            }        }        ...        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;}

notes 1 Situated copy_from_user function , In the first article in this series Android Binder principle ( One ) Study Binder The knowledge points that we must understand before we start I mentioned . ad locum , It's used to put user spatial data ubuf Copy it and save it to the kernel data bwr(binder_write_read Structure ) in . notes 2 It's about ,bwr When there is data in the input buffer of , Will call comments 3 Situated binder_thread_write Function to handle BC_ENTER_LOOPER agreement , Internally, the state of the target thread is set to BINDER_LOOPER_STATE_ENTERED, So the target thread is a Binder Threads . notes 4 Through copy_to_user Function to add kernel space data bwr Copy to user space .

2. summary

ServiceManager The process of starting is actually analysis ServiceManager The entry function of , There are three main things to do in the entry function , This article goes deep into the kernel source code to analyze these three things one by one , Because there are many functions involved , This article only introduces what we need to master , The rest of you can read the source code , such as binder_thread_write、copy_to_user function .

 Preview

版权声明
本文为[Liu Wangshu]所创,转载请带上原文链接,感谢
https://cdmana.com/2021/04/20210421235129620Q.html

Scroll to Top