Android系统之Binder通信机制

前言

Linunx进程中使用的通信方式有:socket(套接字通信),named(命令管道),pipe(管道),message queque(报文队列),signal(信号),share memory(共享内存)。

Java进程中使用的通信方式有:socket,named,pipe等。

Android进程中使用的通信方式主要是Binder通信,下面就来看看Binder通信机制

Binder通信机制

Binder是由ClientServerServiceManagerBinder驱动程序组成。

ServiceManager

其中ServiceManagerBinder机制的守护进程,同时也是一个特殊的Service。它在init.rc里面就开始启动了,其中Android7.0servicemanager的启动代码拆分到servicemanager.rc里面,代码如下:

//frameworks/native/master/./cmds/servicemanager/servicemanager.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进程,从而执行service_manager.cmain函数,同时重启了下面几个模块

  • healthd 监听电池的状态和信息,同时传递给BatteryService,从而展示电池相关的信息。
  • zygotezygotekill的时候,servicemanager会在这里尝试重新唤醒它
  • audioservermedia 音视频相关的服务
  • surfaceflinger 绘制应用程序的用户界面的服务
  • inputflinger 系统输入事件服务
  • drm 数字版权管理服务
  • cameraserver 相机服务
  • keystore 应用签名文件
  • gatekeeperd 系统的图案/密码认证

接下来在init.rc里面调用启动servicemanager

//init.rc
//....
  class_start core
//....

通过class_start core就启动了servicemanager

接下里就来看看service_manager.cmain函数

//frameworks/native/master/./cmds/servicemanager/service_manager.c
int main()
{
    struct binder_state *bs;
    bs = binder_open(128*1024);
 //....
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }
    //....
    binder_loop(bs, svcmgr_handler);
    return 0;
}

主要做了下面几种

  1. binder_open打开Binder设备
  2. binder_become_context_manager 通知Binder驱动程序自己是Binder上下文管理者
  3. binder_loop 进入一个无穷循环,充当Server的角色,等待Client的请求

Binder驱动程序初始化

Binder驱动程序源码位于/drivers/staging/android/binder.c,在源码中可以看到这行代码

device_initcall(binder_init);

binder_init()

Linux加载完内核的时候,init函数会执行device_initcall。从而执行binder_init函数,初始化binder驱动程序。再来看看binder_init()函数

//android/kernel/msm/android-7.1.2_r0.33/./drivers/staging/android/ binder.c
static int __init binder_init(void)
{
    int ret;
    binder_deferred_workqueue = create_singlethread_workqueue("binder");
    if (!binder_deferred_workqueue)
        return -ENOMEM;
    binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
    if (binder_debugfs_dir_entry_root)
        binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
                         binder_debugfs_dir_entry_root);
    ret = misc_register(&binder_miscdev);
    if (binder_debugfs_dir_entry_root) {
        debugfs_create_file("state",
                    S_IRUGO,
                    binder_debugfs_dir_entry_root,
                    NULL,
                    &binder_state_fops);
        debugfs_create_file("stats",
                    S_IRUGO,
                    binder_debugfs_dir_entry_root,
                    NULL,
                    &binder_stats_fops);
        debugfs_create_file("transactions",
                    S_IRUGO,
                    binder_debugfs_dir_entry_root,
                    NULL,
                    &binder_transactions_fops);
        debugfs_create_file("transaction_log",
                    S_IRUGO,
                    binder_debugfs_dir_entry_root,
                    &binder_transaction_log,
                    &binder_transaction_log_fops);
        debugfs_create_file("failed_transaction_log",
                    S_IRUGO,
                    binder_debugfs_dir_entry_root,
                    &binder_transaction_log_failed,
                    &binder_transaction_log_fops);
    }
    return ret;
}

create_singlethread_workqueue 创建了一个binderworker_thread单一内核进程

debugfs_create_dir debugfs是一种内核调试的虚拟文件系统,内核开发者通过debugfs和用户空间交换数据,它是在Linux运行的时候建立。这里就是创建debugfs相应的文件

misc_register 它传的参数是一个结构体如下

static struct miscdevice binder_miscdev = {
    .minor = MISC_DYNAMIC_MINOR, //次设备号动态分配
    .name = "binder",//设备号
    .fops = &binder_fops// 设备的文件操作系统
};

通过misc_register函数为binder驱动注册一个misc设备

接下来就是创建procstate目录下的一些文件

binder_open()

binder_init初始化之后,接下来service_manager.cmain函数会调用binder_open。先来看看binder_open()函数。

static int binder_open(struct inode *nodp, struct file *filp)
{
    struct binder_proc *proc;
    binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
             current->group_leader->pid, current->pid);
    proc = kzalloc(sizeof(*proc), GFP_KERNEL);
    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_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;
    binder_unlock(__func__);
    if (binder_debugfs_dir_entry_proc) {
        char strbuf[11];
        snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
        proc->debugfs_entry = debugfs_create_file(strbuf, S_IRUGO,
            binder_debugfs_dir_entry_proc, proc, &binder_proc_fops);
    }
    return 0;
}

binder_proc 它是保存打开/dev/binder设备的进程的结构体,保存的信息如下

struct binder_proc {
    //...
    struct rb_root threads;
    struct rb_root nodes;
    struct rb_root refs_by_desc;
    struct rb_root refs_by_node;
    struct task_struct *tsk;
    struct list_head todo;
    wait_queue_head_t wait;
    int max_threads;
    long default_priority;
      //...
};
  1. threads 保存binder_proc进程内的用来用户请求处理的线程,线程最大数由max_threads决定
  2. nodes 保存binder_proc进程内的binder的实体
  3. refs_by_descrefs_by_node 保存binder_proc进程内的binder的引用
  4. tsk 保存binder_proc进程的地址
  5. todo 待处理的事务链表
  6. wait 等待处理的链表
  7. default_priority 默认处理的事务优先级

get_task_struct Linux内核方法,源码如下

#define get_task_struct(tsk) do{ atomic_inc&((tsk) ->[usage] } while(0)

最终调用atomic_add函数,通过原子加的形式实现当前进程引用的计数

接下来就是对binder_proc链表的初始化以及其他进程相关信息进行初始化赋值。

binder_mmap()

binder_open里面有一句代码是

static int binder_open(struct inode *nodp, struct file *filp)
{
//....
  filp->private_data = proc;
//....
}

前面说到binder_init里面会把binder驱动注册一个misc设备。进去misc.c会看到

//kernel/common/drivers/char/misc.c
/ * The structure passed is linked into the kernel and may not be
 *  destroyed until it has been unregistered. By default, an open()
 *  syscall to the device sets file->private_data to point to the
 *  structure. Drivers don't need open in fops for this.
 */

int misc_register(struct miscdevice * misc)
{
//....
list_add(&misc->list, &misc_list);
 out:
    mutex_unlock(&misc_mtx);
    return err;
}

把设备保存在misc_list里面,在上面的注释可知,它是在file->private_data具有指针指向的时候系统会自动调用misc_open函数,而在misc_open函数,会遍历file->private_data里面所有保存的设备的fops

static int misc_open(struct inode * inode, struct file * file)
{
//...
list_for_each_entry(c, &misc_list, list) {
        if (c->minor == minor) {
            new_fops = fops_get(c->fops);
            break;
        }
    }
//...
}

这里fops是传入的binder_miscdev结构体里面的fopsfops对应的是binder_fops结构体

static const struct file_operations binder_fops = {
    .owner = THIS_MODULE,
    .poll = binder_poll,
    .unlocked_ioctl = binder_ioctl,
    .compat_ioctl = binder_ioctl,
    .mmap = binder_mmap,
    .open = binder_open,
    .flush = binder_flush,
    .release = binder_release,
};

从这里可以找出初始化binder_mmap函数。再来看看binder_mmap函数

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

通过filp->private_data得到在打开设备文件 /dev/binder创建的struct binder_proc结构,内存映射信息放在vma参数中。其中vmavm_area_struct结构体,它是给进程使用的一块连续的虚拟地址空间,而vm_struct是个内核使用的一块连续的虚拟地址空间。在同一个物理页面中,一方映射到进程虚拟地址空间,一方面映射到内核虚拟地址空间,这样,进程和内核之间就可以减少一次内存拷贝了。接下来该函数就是处理内存映射和管理的详细步骤。

binder_ioctl()

再来看看binder_ioctl函数

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    int ret;
    struct binder_proc *proc = filp->private_data;
    struct binder_thread *thread;
    unsigned int size = _IOC_SIZE(cmd);
    void __user *ubuf = (void __user *)arg;
    /*pr_info("binder_ioctl: %d:%d %x %lx\n",
            proc->pid, current->pid, cmd, 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);
    if (thread == NULL) {
        ret = -ENOMEM;
        goto err;
    }
    switch (cmd) {
    case BINDER_WRITE_READ:
        ret = binder_ioctl_write_read(filp, cmd, arg, thread);
        if (ret)
            goto err;
        break;
    case BINDER_SET_MAX_THREADS:
        if (copy_from_user_preempt_disabled(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
            ret = -EINVAL;
            goto err;
        }
        break;
    case BINDER_SET_CONTEXT_MGR:
        ret = binder_ioctl_set_ctx_mgr(filp);
        if (ret)
            goto err;
        break;
    case BINDER_THREAD_EXIT:
        binder_debug(BINDER_DEBUG_THREADS, "%d:%d exit\n",
                 proc->pid, thread->pid);
        binder_free_thread(proc, thread);
        thread = NULL;
        break;
    case BINDER_VERSION: {
        struct binder_version __user *ver = ubuf;
        if (size != sizeof(struct binder_version)) {
            ret = -EINVAL;
            goto err;
        }
        if (put_user_preempt_disabled(BINDER_CURRENT_PROTOCOL_VERSION, &ver->protocol_version)) {
            ret = -EINVAL;
            goto err;
        }
        break;
    }
    default:
        ret = -EINVAL;
        goto err;
    }
    ret = 0;
err:
    if (thread)
        thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
    binder_unlock(__func__);
    wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
    if (ret && ret != -ERESTARTSYS)
        pr_info("%d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
err_unlocked:
    trace_binder_ioctl_done(ret);
    return ret;
}

它主要是负责在两个进程间进行收发数据

你可能感兴趣的:(Android系统之Binder通信机制)