想掌握 Binder 机制?驱动核心源码详解和Binder超系统学习资源,想学不会都难!

本文深入剖析Android Binder机制,从getService调用开始,逐步揭示Binder驱动在进程间通信中的关键角色。通过分析binder_ioctl、binder_get_thread、binder_ioctl_write_read等核心函数,理解数据准备、拷贝、目标进程查找等步骤。通过源码学习,助力开发者掌握Binder机制。
摘要由CSDN通过智能技术生成

本文设计源码及逻辑较多,已尽量画图辅助,完全理解可能要耗一定精力,建议收藏。

应用程序中执行 getService() 需与 ServiceManager 通过 binder 跨进程通信,此过程中会贯穿 Framework、Natve 层以及 Linux 内核驱动。

binder 驱动的整体分层如上图,下面先来宏观的了解下 getService() 在整个 Android 系统中的调用栈,ServiceManager 本身的获取:

与 ServiceManager 进行 IPC 通信:

「本文将主要分析此过程中 binder 驱动具体承担了哪些工作」,也就是上图中 IPCThreadState 与 binder 驱动的 ioctl 调用。

binder 驱动中做的工作可以总结为以下几步:

  1. 准备数据,根据命令分发给具体的方法去处理
  2. 找到目标进程的相关信息
  3. 将数据一次拷贝到目标进程所映射的物理内存块
  4. 记录待处理的任务,唤醒目标线程
  5. 调用线程进入休眠
  6. 目标进程直接拿到数据进行处理,处理完后唤醒调用线程
  7. 调用线程返回处理结果

在源码中实际会执行到的函数主要包括:

  1. binder_ioctl()
  2. binder_get_thread()
  3. binder_ioctl_write_read()
  4. binder_thread_write()
  5. binder_transaction()
  6. binder_thread_read()

下面按照这些 binder 驱动中的函数,以工作步骤为脉络,深入分析驱动中的源码执行逻辑,彻底搞定 binder 驱动!

1.binder_ioctl()

在 IPCThreadState 中通过系统调用 ioctl 陷入系统内核,调用到 binder_ioctl() 方法:

ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)

binder_ioctl() 方法中会根据 BINDER_WRITE_READ、BINDER_SET_MAX_THREADS 等不同 cmd 转调到不同的方法去执行,这里我们只关注 BINDER_WRITE_READ,代码如下:

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){
    int ret;
    //拿到调用进程在 binder_open() 中记录的 binder_proc
    struct binder_proc *proc = filp->private_data;
    struct binder_thread *thread;
    binder_lock(__func__);
    //获取调用线程 binder_thread
    thread = binder_get_thread(proc);
    switch (cmd) {
    case BINDER_WRITE_READ:
      //处理 binder 数据读写,binder IPC 通信的核心逻辑
     ret = binder_ioctl_write_read(filp, cmd, arg, thread);
     if (ret)
      goto err;
     break;
    ...
}

之前文章介绍过 binder_open() 方法, binder_open() 方法主要做了两个工作:

  1. 创建及初始化每个进程独有一份的、用来存放 binder 相关数据的 binder_proc 结构体
  2. 「将 binder_proc 记录起来,方便后续使用」

正是通过 file 的 private_data 来记录的:

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

拿到调用进程后,进一步通过 binder_get_thread() 方法拿到调用线程,然后就交给 binder_ioctl_write_read() 方法去执行具体的 binder 数据读写了。

可见 binder_ioctl() 方法本身的逻辑非常简单,将数据 arg 透传了出去。

下面分别来看 binder_get_thread()、binder_ioctl_write_read() 这两个方法。

2.binder_get_thread()

static struct binder_thread *binder_get_thread(
                            struct binder_proc *proc){
    struct binder_thread *thread = NULL;
    struct rb_node *parent = NULL;
    //从 proc 中获取红黑树根节点
    struct rb_node **p = &proc->threads.rb_node; 
    //查找 pid 等于当前线程 id 的thread,该红黑树以 pid 大小为序存放
    while (*p) {
        parent = *p;
        thread = rb_entry(parent, struct binder_thread, rb_node);
        //current->pid 是当前调用线程的 id
        if (current->pid < thread->pid) 
            p = &(*p)->rb_left;
        else if (current->pid > thread->pid)
            p = &(*p)->rb_right;
        else
            break;
    }
    
    if (*p == NULL) {//如果没有找到,则新创建一个
        thread = kzalloc(sizeof(*thread), GFP_KERNEL);
        if (thread == NULL)
            return NULL;
        binder_stats_created(BINDER_STAT_THREAD);
        thread->proc = proc;
        thread->pid = current->pid;
        init_waitqueue_head(&thread->wait); //初始化等待队列
        INIT_LIST_HEAD(&thread->todo); //初始化待处理队列
        //加入到 proc 的 threads 红黑树中
        rb_link_node(&thread->rb_node, parent, p);  
        rb_insert_color(&thread->rb_node, &proc->threads);
        thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN;
        thread->return_error = BR_OK;
        thread->return_error2 = BR_OK;
    }
    return thread;
}

binder_thread 是用来描述线程的结构体,binder_get_thread() 方法中逻辑也很简单,首先从调用进程 proc 中查找当前线程是否已被记录,如果找到就直接返回,否则新建一个返回,并记录到 proc 中。

也就是说所有调用 binder_ioctl() 的线程,都会被记录起来。

3.binder_ioctl_write_read

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值