深入浅出Android底层(二)-Android中的IPC-Binder机制-Binder驱动

949 阅读9分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第7天,点击查看活动详情

前言

在上一篇Android的IPC-Binder机制中,我们提及了Android中的Binder机制并且提到了几个关于Binder的关键函数,比如binder_open,binder_loop,mmap内存映射等,这一篇我们就来详细说一说这几个函数.

一 、启动ServiceManager

ServiceManager是在init进程启动后被创建的,进程名是/system/bin/servicemanager

我们可以在service_manager.rc文件中看到启动的若干参数


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

我们来看下ServiceManager的main函数

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

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

    //打开binder驱动
    bs = binder_open(driver, 128*1024);
    ...
    //将ServiceManager设置成binder的守护者
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }

    ...
    //开启binder循环,监听数据的到来

    binder_loop(bs, svcmgr_handler);

    return 0;
}

可以看到,在Main方法中,主要做了三件事

  • 1、通过binder_open打开binder驱动,申请了128kb的内存大小空间
  • 2、通过binder_become_context_manager将ServiceManager设置成binder的守护者
  • 3、通过binder_loop开启binder循环,监听数据

二、binder_open()

任何一个进程想要使用binder驱动进行通信时,都需要执行binder_open方法,打开binder驱动.

binder_open方法首先创建初始化binder_proc,并将当前进程的binder_proc保存在binder驱动管理的全局链表binder_procs中.

在binder驱动中,binder_procs链表管理着所有进程执行完binder_open()创建的binder_proc.

image.png

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

    //开启binder驱动
    bs->fd = open(driver, O_RDWR | O_CLOEXEC);
    if (bs->fd < 0) {
        fprintf(stderr,"binder: cannot open %s (%s)\n",
                driver, strerror(errno));
        goto fail_open;
    }

    //获取binder版本信息
    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
        (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
        fprintf(stderr,
                "binder: kernel driver version (%d) differs from user space version (%d)\n",
                vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);
        goto fail_open;
    }
    
    //设置mmap映射大小128kb
    bs->mapsize = mapsize;
    //设置内存映射地址
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    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;
}

这里主要对bs结构体的三个变量进行赋值,它是binder_state类型的结构体.

struct binder_state
{
    int fd;//dev/binder 文件描述符
    void *mapped;//映射的内存地址
    size_t mapsize;//映射的大小
};

由于执行open(driver, O_RDWR | O_CLOEXEC);时会从用户态进入内核态,依次会执行binder的open

三、系统的binder_open

drivers/staging/android/binder.c
static int binder_open(struct inode *nodp, struct file *filp)
{
    //binder_proc结构体,每一个进程在binder驱动中都对应着一个此结构体.
    //binder_proc是进程在使用binder通信时在内核中的数据结构
    struct binder_proc *proc;
    //为binder_proc结构体申请内存,并初始化为0,并返回一个指向
    //分配的内存块起始地址的地址指针
    proc = kzalloc(sizeof(*proc), GFP_KERNEL);      //a. 创建Service_manager进程对应的binder_proc,保存Service_manager进程的信息
    if (proc == NULL)
        return -ENOMEM;
    //获取一个当前线程的task_struct(PCB进程控制块),
    //在linux中,无论是进程还是线程都是有PCB进程控制块来表示的
    get_task_struct(current);
    //将当前线程的task保存到binder进程的tsk
    proc->tsk = current;
    //初始化todo队列(也叫做工作队列),用于存放待处理的请求(server端)
    //由于服务进程不会是一个一个处理请求的,在todo队列中的请求会
    //多线程处理
    INIT_LIST_HEAD(&proc->todo);
    //初始化wait队列,当某个服务进程被请求的次数过多时,多余的
    //任务会放在wait队列中
    init_waitqueue_head(&proc->wait);
    //将当前线程线程优先级保存在binder进程的default_priority中
    //获取当前进程的优先级
    proc->default_priority = task_nice(current);
    
    //持有锁
    binder_lock(__func__);
    binder_stats_created(BINDER_STAT_PROC);
    //将当前的binder_proc对象加入到binder驱动管理的全局binder_procs链表中
    hlist_add_head(&proc->proc_node, &binder_procs);        //binder_procs是一个全局变量,hlist_add_head是将proc->proc_node(proc->proc_node是一个hlist_node链表)加入binder_procs的list中
    //将当前线程所处进程的pid记录在binder进程的pid中
    proc->pid = current->group_leader->pid;
    INIT_LIST_HEAD(&proc->delivered_death);
    filp->private_data = proc;          
    //将binder_proc保存在打开文件file的私有数据成员变量private_data中
    //释放锁
    binder_unlock(__func__);
    return 0;
}

四、mmap函数

关于mmap的实现原理我们之前的文章已经提到过了,通过减少一次内存拷贝次数提高进程访问效率.

/kernel/drivers/android/binder.c      3357行
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
    int ret;
    //内核虚拟空间
    struct vm_struct *area;
    struct binder_proc *proc = filp->private_data;
    const char *failure_string;
    struct binder_buffer *buffer;

    if (proc->tsk != current)
        return -EINVAL;
    //保证映射内存大小不超过4M
    if ((vma->vm_end - vma->vm_start) > SZ_4M)
        vma->vm_end = vma->vm_start + SZ_4M;

    binder_debug(BINDER_DEBUG_OPEN_CLOSE,
             "binder_mmap: %d %lx-%lx (%ld K) vma %lx pagep %lx\n",
             proc->pid, vma->vm_start, vma->vm_end,
             (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,
             (unsigned long)pgprot_val(vma->vm_page_prot));

    if (vma->vm_flags & FORBIDDEN_MMAP_FLAGS) {
        ret = -EPERM;
        failure_string = "bad vm_flags";
        goto err_bad_arg;
    }
    vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;

    mutex_lock(&binder_mmap_lock);
    if (proc->buffer) {
        ret = -EBUSY;
        failure_string = "already mapped";
        goto err_already_mapped;
    }
     //分配一个连续的内核虚拟空间,与进程虚拟空间大小一致
    area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
    if (area == NULL) {
        ret = -ENOMEM;
        failure_string = "get_vm_area";
        goto err_get_vm_area_failed;
    }
     //指向内核虚拟空间的地址
    proc->buffer = area->addr;
      //地址便宜量=用户空间地址-内核空间地址
    proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
      // 释放锁
    mutex_unlock(&binder_mmap_lock);

#ifdef CONFIG_CPU_CACHE_VIPT
    if (cache_is_vipt_aliasing()) {
        while (CACHE_COLOUR((vma->vm_start ^ (uint32_t)proc->buffer))) {
            pr_info("binder_mmap: %d %lx-%lx maps %p bad alignment\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer);
            vma->vm_start += PAGE_SIZE;
        }
    }
#endif
        //分配物理页的指针数组,大小等于用户虚拟内存/4K
    proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
    if (proc->pages == NULL) {
        ret = -ENOMEM;
        failure_string = "alloc page array";
        goto err_alloc_pages_failed;
    }
    proc->buffer_size = vma->vm_end - vma->vm_start;

    vma->vm_ops = &binder_vm_ops;
    vma->vm_private_data = proc;
    // 分配物理页面,同时映射到内核空间和进程空间,目前只分配1个page的物理页
    if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) {
        ret = -ENOMEM;
        failure_string = "alloc small buf";
        goto err_alloc_small_buf_failed;
    }
     // binder_buffer对象,指向proc的buffer地址
    buffer = proc->buffer;
     //创建进程的buffers链表头
    INIT_LIST_HEAD(&proc->buffers);
     //将binder_buffer地址  加入到所属进程的buffer队列
    list_add(&buffer->entry, &proc->buffers);
    buffer->free = 1;
     //将空闲的buffer放入proc->free_buffer中
    binder_insert_free_buffer(proc, buffer);
      // 异步可用空间大小为buffer总体大小的一半
    proc->free_async_space = proc->buffer_size / 2;
    barrier();
    proc->files = get_files_struct(current);
    proc->vma = vma;
    proc->vma_vm_mm = vma->vm_mm;

    /*pr_info("binder_mmap: %d %lx-%lx maps %p\n",
         proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/
    return 0;
//错误跳转
err_alloc_small_buf_failed:
    kfree(proc->pages);
    proc->pages = NULL;
err_alloc_pages_failed:
    mutex_lock(&binder_mmap_lock);
    vfree(proc->buffer);
    proc->buffer = NULL;
err_get_vm_area_failed:
err_already_mapped:
    mutex_unlock(&binder_mmap_lock);
err_bad_arg:
    pr_err("binder_mmap: %d %lx-%lx %s failed %d\n",
           proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
    return ret;
}

image.png

可以看到,mmap首先在内核虚拟地址空间,申请一块与用户虚拟内存相同的大小的内存.然后申请一个page大小的物理内存,再将同一块物理内存分别映射到内核虚拟内存空间和用户虚拟内存空间,从而实现了用户空间的buffer与内核空间的buffer同步操作的功能

五、binder_become_context_manager

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

在binder_become_context_manager方法中,通过ioctl与binder驱动进行通信,并传入数据0作为标识,将ServiceManager设置为binder的守护者,用来统一处理binder的数据传输

ioctl函数最终会调用binder内核的binder_ioctl,在阅读binder机制源码中会看到大量的ioctl函数,因为它是用来实现用户空间和内核空间数据传递的桥梁,ioctl函数主要做的两件事就是write和read.

六、binder_loop

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)); 
// 开启循环,监听数据的到来 
for (;;) 
{
    bwr.read_size = sizeof(readbuf); 
    bwr.read_consumed = 0; 
    bwr.read_buffer = (uintptr_t) readbuf; 
    
    // 获取binder驱动中的数据 
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); 
    
    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); 
   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; 
   } 
 } 
}

在binder_loop中主要做了

  • 1、首先通过binder_write传递了BC_ENTER_LOOPER指令码,告诉binder进入循环
  • 2、开启循环,通过ioctl监听并读取数据
  • 3、一旦读取到数据,将通过binder_parse进行解析

七、binder_write

int binder_write(struct binder_state *bs, void *data, size_t len)
{
    struct binder_write_read bwr;
    int res;
    
    //将数据填充到bwr中
    bwr.write_size = len;
    bwr.write_consumed = 0;
    bwr.write_buffer = (uintptr_t) data;
    bwr.read_size = 0;
    bwr.read_consumed = 0;
    bwr.read_buffer = 0;
    //传输数据
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
    if (res < 0) {
        fprintf(stderr,"binder_write: ioctl failed (%s)\n",
                strerror(errno));
    }
    return res;
}

可以看到,binder_write主要是将数据统一封装到bwr中,bwr是binder_write_read的结构体,当写数据时会将数据写入到write_buffer中,而当读数据时会从read_buffer中读取数据.所以这是一个支持双向读写操作的数据源,以便可以通过ioctl与binder驱动进行读写操作.

由于是首次进入且即将进入循环操作,所以第一次会传递BC_ENTER_LOOPER指令吗,通知binder进行循环操作.

所以通过ioctl发送BINDER_WRITE_READ的通信code,将bwr传递给binder驱动

八、binder_parse

int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uintptr_t ptr, size_t size, binder_handler func)
{
    int r = 1;
    //获取数据截止位的内存地址
    uintptr_t end = ptr + (uintptr_t) size;

    while (ptr < end) {
        uint32_t cmd = *(uint32_t *) ptr;
        ptr += sizeof(uint32_t);
#if TRACE
        fprintf(stderr,"%s:\n", cmd_name(cmd));
#endif
        switch(cmd) {
       ...
        case BR_TRANSACTION: {
            struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
            if ((end - ptr) < sizeof(*txn)) {
                ALOGE("parse: txn too small!\n");
                return -1;
            }
            binder_dump_txn(txn);
            if (func) {
                unsigned rdata[256/4];
                struct binder_io msg;
                struct binder_io reply;
                int res;

                bio_init(&reply, rdata, sizeof(rdata), 4);
                bio_init_from_txn(&msg, txn);
                
                //调用func,对应的是svcmgr_handler
                res = func(bs, txn, &msg, &reply);
                if (txn->flags & TF_ONE_WAY) {
                    binder_free_buffer(bs, txn->data.ptr.buffer);
                } else {
                //发送reply
                    binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
                }
            }
            ptr += sizeof(*txn);
            break;
        }
        case BR_REPLY: {
            ...
            break;
        }
        ...
        default:
            ALOGE("parse: OOPS %d\n", cmd);
            return -1;
        }
    }

    return r;
}

binder_parse主要是解析binder信息,参数ptr指向BC_ENTER_LOOPER,func指向svcmgr_handler。

所以一旦请求到来,就会调用svcmgr_handler,并将处理的结构通过binder_send_reply返回给client端.

这个对应的就是之前文章中说的BC_REPALY.

svcmgr_handler是在外面的binder_loop传递过来的.

九、svcmgr_handler

int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    struct svcinfo *si;
    uint16_t *s;
    size_t len;
    uint32_t handle;
    uint32_t strict_policy;
    int allow_isolated;
    uint32_t dumpsys_priority;

    //ALOGI("target=%p code=%d pid=%d uid=%d\n",
    //      (void*) txn->target.ptr, txn->code, txn->sender_pid, txn->sender_euid);

    if (txn->target.ptr != BINDER_SERVICE_MANAGER)
        return -1;

    if (txn->code == PING_TRANSACTION)
        return 0;

    // Equivalent to Parcel::enforceInterface(), reading the RPC
    // header with the strict mode policy mask and the interface name.
    // Note that we ignore the strict_policy and don't propagate it
    // further (since we do no outbound RPCs anyway).
    strict_policy = bio_get_uint32(msg);
    s = bio_get_string16(msg, &len);
    if (s == NULL) {
        return -1;
    }

    if ((len != (sizeof(svcmgr_id) / 2)) ||
        memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {
        fprintf(stderr,"invalid id %s\n", str8(s, len));
        return -1;
    }

    if (sehandle && selinux_status_updated() > 0) {
#ifdef VENDORSERVICEMANAGER
        struct selabel_handle *tmp_sehandle = selinux_android_vendor_service_context_handle();
#else
        struct selabel_handle *tmp_sehandle = selinux_android_service_context_handle();
#endif
        if (tmp_sehandle) {
            selabel_close(sehandle);
            sehandle = tmp_sehandle;
        }
    }

    switch(txn->code) {
    case SVC_MGR_GET_SERVICE:
    case SVC_MGR_ADD_SERVICE:
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        //查找service
        handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
        if (!handle)
            break;
        bio_put_ref(reply, handle);
        return 0;

    case SVC_MGR_ADD_SERVICE:
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        handle = bio_get_ref(msg);
        allow_isolated = bio_get_uint32(msg) ? 1 : 0;
        dumpsys_priority = bio_get_uint32(msg);
        //注册service
        if (do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated, dumpsys_priority,
                           txn->sender_pid))
            return -1;
        break;

    case SVC_MGR_LIST_SERVICES: {
        uint32_t n = bio_get_uint32(msg);
        uint32_t req_dumpsys_priority = bio_get_uint32(msg);

        if (!svc_can_list(txn->sender_pid, txn->sender_euid)) {
            ALOGE("list_service() uid=%d - PERMISSION DENIED\n",
                    txn->sender_euid);
            return -1;
        }
        si = svclist;
        // walk through the list of services n times skipping services that
        // do not support the requested priority
        
        //遍历service
        while (si) {
            if (si->dumpsys_priority & req_dumpsys_priority) {
                if (n == 0) break;
                n--;
            }
            si = si->next;
        }
        if (si) {
            bio_put_string16(reply, si->name);
            return 0;
        }
        return -1;
    }
    default:
        ALOGE("unknown code %d\n", txn->code);
        return -1;
    }

    bio_put_uint32(reply, 0);
    return 0;
}

svcmgr_handler主要是对service的操作处理,在SVC_MGR_ADD_SERVICE中会通过do_add_service方法来注册service

十、do_add_service

 
int do_add_service(struct binder_state *bs, const uint16_t *s, size_t len, uint32_t handle,
                   uid_t uid, int allow_isolated, uint32_t dumpsys_priority, pid_t spid) {
    struct svcinfo *si;

    //ALOGI("add_service('%s',%x,%s) uid=%d\n", str8(s, len), handle,
    //        allow_isolated ? "allow_isolated" : "!allow_isolated", uid);

    if (!handle || (len == 0) || (len > 127))
        return -1;

    //检查是否能够注册该service
    if (!svc_can_register(s, len, spid, uid)) {
        ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n",
             str8(s, len), handle, uid);
        return -1;
    }

    //查找是否已经注册了
    si = find_svc(s, len);
    if (si) {
        
        if (si->handle) {
            ALOGE("add_service('%s',%x) uid=%d - ALREADY REGISTERED, OVERRIDE\n",
                 str8(s, len), handle, uid);
            svcinfo_death(bs, si);
        }
        si->handle = handle;
    } else {
    //没有注册
    //申请内存
        si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
        if (!si) {
            ALOGE("add_service('%s',%x) uid=%d - OUT OF MEMORY\n",
                 str8(s, len), handle, uid);
            return -1;
        }
        si->handle = handle;
        si->len = len;
        memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
        si->name[len] = '\0';
        si->death.func = (void*) svcinfo_death;
        si->death.ptr = si;
        si->allow_isolated = allow_isolated;
        si->dumpsys_priority = dumpsys_priority;
        si->next = svclist;
        //保存到svclist表中
        svclist = si;
    }

    binder_acquire(bs, handle);
    binder_link_to_death(bs, handle, &si->death);
    return 0;
}

在do_add_service中首先会检查该注册的service是否能够注册,然后再查询现有的svclist中是否存在该service;如果不存在就为该service申请内存空间,最后再加入到svclist注册表中

总结

我们可以看到ServiceManger的几个关键函数都已经分析完毕了

  • 1、首先通过binder_open打开binder驱动,并调用mmap分配了128kb的内存映射空间
  • 2、通过binder_become_context_manager将ServiceManager设置为binder驱动的守护进程,通过0来标识
  • 3、通过binder_loop开启循环,等待与监听client端传递过来的数据
  • 4、在数据监听过程中,使用binder_write通过binder进行循环
  • 5、通过ioctl来与binder驱动进行数据读写
  • 6、通过binder_parse来解析监听到的数据,根据BR_指令码来区分不同的操作,并通过reply与BC_指令码的方式回馈给client端
  • 7、将解析的数据回调给svcmgr_handler进行统一逻辑处理,包括service的注册、查找、验证等操作
  • 8、最终ServiceManger会将注册的service保存到svclist注册表中,以便之后的验证与查询.