Android进阶之路Android技术知识

FrameWork源码【Binder 驱动解析】

2022-07-15  本文已影响0人  码农的地中海

前言

我们知道 Binder 驱动主要用于跨进程的通信, 它的身影出现在 Androrid 系统方方面面, 是 Android 系统框架中非常重要, 又非常难懂以搞懂的一部分 。

Binder作为Android进程间通信的机制,可以看做是一个驱动,因为本身也是一个Java类,Binder.java : IBinder

在Android中,常见的进程间通信例如系统类的:打电话、闹钟等;自己创建的:像WebView、视频播放、音频播放、大图浏览等。


f31cdf173f1e9af2d09192b76a19c8a6.jpeg

优势

binder架构原理

Binder是一个类,主要用在Service组件应用中。

/framework/base/core/java/Android/os/Binder.java(包含内部类BinderProxy)

image.png image.png

binder相对来说还是比较复杂的,有framework层与native层的binder。framework层的binder采用JNI技术来调用native(C/C++ framework)层的binder架构,从而为上层应用程序提供服务。需要注意的是:

(1) framework层的Binder是建立在Native层架构基础之上的,核心逻辑都是交予Native层方法来处理

(2)framework层的Service Manager类与Native层的功能并不完全对应,java层的Service Manager类的实现最终是通过BinderProxy传递给Native层来完成的。


image.png

图中红色代表整个framework层binder架构相关组件;Binder类代表Server端,BinderProxy类代表Client端;图中蓝色代表Native层Binder架构相关组件。

1 Binder框架层

a、Native Binder框架层包含以下类(frameworks/native/libs/binder):IInterface,BnInterface,BpInterface,等。

b、Java框架层包含以下类(frameworks/base/core/java/android/os):

IBinder,Binder,IInterface,ServiceManagerNative,ServiceManager,BinderInternal,IServiceManager,ServiceManagerProxy

Java框架层的类的部分方法的实现在本地代码里(frameworks/base/core/jni)。

2 Binder核心层

Binder核心层主要是IBinder及它的两个子类,即BBinder和BpBinder,分别代表了最基本的服务端及客户端。源码位于frameworks/native/libs/binder目录下。

binder service服务端实体类会继承BnInterface,而BnInterface会继承自BBinder,服务端可将BBinder对象注册到servicemananger进程。

客户端程序和驱动交互时只能得到远程对象的句柄handle,它可以调用调用ProcessState的getStrongProxyForHandle函数,利用句柄handle建立BpBinder对象,然后将它转为IBinder指针返回给调用者。这样客户端每次调用IBinder指针的transact方法,其实是执行BpBinder的transact方法。

3 binder adapter层

主要是IPCThreadState.cpp和ProcessState.cpp,源码位于frameworks/native/libs/binder目录下,这两个类都采用了单例模式,主要负责和驱动直接交互。

a、ProcessState,进程相关的类,负责打开binder设备,进行一些初始化设置并做内存映射;
void ProcessState::startThreadPool()该方法启动的新线程,并通过joinThreadPool读取binder设备,查看是否有请求。
b、IPCThreadState,线程相关的类,负责直接和binder设备通信,使用ioctl读写binder驱动数据。

4 binder驱动层

Android因此添加了binder驱动,其设备节点为/dev/binder,主设备号为10,binder驱动程序在内核中的头文件和代码路径如下:

kernel/drivers/staging/binder.h

kernel/drivers/staging/binder.c

binder驱动层的主要作用是完成实际的binder数据传输。

Service与客户端通信,有两种方式,AIDL和Messenger。AIDL基于Binder,而Messenger基于AIDL。AIDL只是方便使用Binder,也可以不用AIDL。在IPC的时候是需要使用Service的,通过启动后Service才能获取到远程Binder,这样就可以通信了。不过还是使用AIDL更方便些,客户端启动绑定Service后在onServiceConnected获取到Binder,通过Stub的asInterface能很简单的转化成对应的业务接口。

Binder驱动源码分析

从App启动,Binder是怎样被初始化的,还有就是一些细节的处理,从最底层的C++代码看Binder驱动是怎么实现的

如果大家想要看系统的源码,其实不用去下载源码,直接在在线看就行在线看系统源码,点击就可以看


image.png

1.binder_init

binder_init是binder的初始化操作


/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);                   
    ……
    }
    return ret;
}

device_initcall(binder_init);

static struct miscdevice binder_miscdev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "binder",
    .fops = &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_init函数中,调用了misc_register函数,其中传入的参数binder_miscdev,定义了misc设备的名称”binder“,就是注册了一个binder设备,MISC_DYNAMIC_MINOR是动态分配的一个设备号;还有一个数据结构,binder_fops,其中包含了binder_open、binder_ioctl、binder_mmap等重要的函数,也是在这个时候完成了初始化,具体怎么完成的初始化,接着往下看

# /drivers/char/misc.c
int misc_register(struct miscdevice * misc)
{
    dev_t dev;
    ……

    misc->this_device = device_create(misc_class, misc->parent, dev,
                      misc, "%s", misc->name);
    if (IS_ERR(misc->this_device)) {
        int i = DYNAMIC_MINORS - misc->minor - 1;
        if (i < DYNAMIC_MINORS && i >= 0)
            clear_bit(i, misc_minors);
        err = PTR_ERR(misc->this_device);
        goto out;
    }
    /*
     * Add it to the front, so that later devices can "override"
     * earlier defaults
     */
    list_add(&misc->list, &misc_list);
    out:
    mutex_unlock(&misc_mtx);
    return err;
}

misc_register中调用了device_create函数,device_create就是用来创建binder misc device设备

# /drivers/base/core.c

struct device *device_create(struct class *class, struct device *parent,
                 dev_t devt, void *drvdata, const char *fmt, ...)
{
    va_list vargs;
    struct device *dev;

    va_start(vargs, fmt);
    dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
    va_end(vargs);
    return dev;
}

device_create_groups_vargs(struct class *class, struct device *parent,
               dev_t devt, void *drvdata,
               const struct attribute_group **groups,
               const char *fmt, va_list args)
{
    struct device *dev = NULL;
    //给设置分配内存  GFP_KERNEL
    dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    ……
    retval = device_add(dev);
    ……
}

所以binder_init的主要作用就是:
1 初始化binder misc设备
2 给设备分配内存
3 调用list_add,将misc device设备放入设备列表中misc_list

2.binder_open

当Android要进行进程间通信的时候,通过客户端或者服务端调用binder_open,打开Binder驱动,例如在app中,调用bindService,就是通过Framework层,JNI调用底层binder_open打开驱动

static int binder_open(struct inode *nodp, struct file *filp)
{
    struct binder_proc *proc;
    
    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);
    //将proc添加到链表中
    hlist_add_head(&proc->proc_node, &binder_procs);
    proc->pid = current->group_leader->pid;
    INIT_LIST_HEAD(&proc->delivered_death);
    
    return 0;
}

在binder_open中,首先给binder_proc分配内存,然后将当前进程的信息,保存到binder_proc中,然后将binder_proc添加到binder_procs表中(hlist_add_head),每个进程拥有一个binder_proc,独一份!

3.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;
    ……
    //进程的虚拟内存 不能超过4M
    if ((vma->vm_end - vma->vm_start) > SZ_4M)
        vma->vm_end = vma->vm_start + SZ_4M;
    //在内核中,分配一块跟用户空间大小相同的一块虚拟内存
    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;
    ……
    //分配物理内存
    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;
    
    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;
    }
    
    vma->vm_ops = &binder_vm_ops;
    vma->vm_private_data = proc;
    ……
    buffer = proc->buffer;
    INIT_LIST_HEAD(&proc->buffers);
    list_add(&buffer->entry, &proc->buffers);
    buffer->free = 1;
    binder_insert_free_buffer(proc, 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;
……
}

首先明白几个变量代表的含义
vm_area_struct *vma : 用户态的虚拟内存
vm_struct *area : 内核态的虚拟内存

代码分析

1-----
if ((vma->vm_end - vma->vm_start) > SZ_4M)
        vma->vm_end = vma->vm_start + SZ_4M;

在这里,驱动做了限制,用户态的虚拟内存,不能超过4M,应用所占用的大概有1M - 8K(异步为一半,这里是同步的标准),所以为什么Intent传递数据时,不能超过1M,就是因为Intent传递数据基于Binder机制,因此不能超过1M,这是Binder驱动定的。

2-----
area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);

在内核空间中,分配一块跟用户空间大小相同的虚拟内存

3-----
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;
}
static int binder_update_page_range(struct binder_proc *proc, int allocate,
                    void *start, void *end,
                    struct vm_area_struct *vma)
{
    void *page_addr;
    unsigned long user_page_addr;
    struct vm_struct tmp_area;
    struct page **page;
    struct mm_struct *mm;
    ……
    if (allocate == 0)
        goto free_range;


    for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
        int ret;
    
        page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
    
        BUG_ON(*page);
        *page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
        ……
        tmp_area.addr = page_addr;
        tmp_area.size = PAGE_SIZE + PAGE_SIZE /* guard page? */;
        //内核虚拟内存与物理内存完成映射
        ret = map_vm_area(&tmp_area, PAGE_KERNEL, page);
    
        user_page_addr =
            (uintptr_t)page_addr + proc->user_buffer_offset;
        //用户空间与物理内存完成映射
        ret = vm_insert_page(vma, user_page_addr, page[0]);
        ……
    }

allocate == 1,意味着分配1页的物理内存(4KB),并与内核空间的虚拟内存完成映射(map_vm_area);vm_insert_page 将用户空间的虚拟内存与物理内存完成映射


image.png

看一下这个图就能明白binder_mmap的作用了

3.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;
    ……
    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:
    //设置ServiceManager为大管家,下一节会走到这个地方
    case BINDER_SET_CONTEXT_MGR:
    case BINDER_THREAD_EXIT:
    case BINDER_VERSION: 
    ……
}

从源码中可以看到,binder_ioctl只要是通过cmd命令来执行相应的操作,这里主要看下BINDER_WRITE_READ 读写操作

static int binder_ioctl_write_read(struct file *filp,
                unsigned int cmd, unsigned long arg,
                struct binder_thread *thread)
{
    int ret = 0;
    struct binder_proc *proc = filp->private_data;
    unsigned int size = _IOC_SIZE(cmd);
    void __user *ubuf = (void __user *)arg;
    struct binder_write_read bwr;
    ……
    if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
        ret = -EFAULT;
        goto out;
    }
    ……
    if (bwr.write_size > 0) {
        ret = binder_thread_write(proc, thread,
                      bwr.write_buffer,
                      bwr.write_size,
                      &bwr.write_consumed);
        trace_binder_write_done(ret);
        if (ret < 0) {
            bwr.read_consumed = 0;
            if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
                ret = -EFAULT;
            goto out;
        }
    }
    if (bwr.read_size > 0) {
        ret = binder_thread_read(proc, thread, bwr.read_buffer,
                     bwr.read_size,
                     &bwr.read_consumed,
                     filp->f_flags & O_NONBLOCK);
        trace_binder_read_done(ret);
        if (!list_empty(&proc->todo))
            wake_up_interruptible(&proc->wait);
        if (ret < 0) {
            if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
                ret = -EFAULT;
            goto out;
        }
    }
    ……
    if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
        ret = -EFAULT;
        goto out;
    }
}

读写操作主要是判断write_size和read_size是否存在读写的数据,通过copy_from_user 和 copy_to_user完成数据的读写操作


07Java Binder中系统服务的注册过程.png 06Java Binder的初始化.png

更多framework学习;可以前往:Frame Work源码解析手册

文末

其实这里主要的作用就是,实现Java层和native层册映射关系,在Java能够调用native层的方法

从Binder架构图中可以看到,JNI主要就是Framework层与Native层交互,通过native与内核层的数据读写,返回到Server端

上一篇下一篇

猜你喜欢

热点阅读