业余整理的工作笔记

binder驱动

2020-04-19  本文已影响0人  龙遁流

binder

android 系统进程间通信方式,C/S架构。只需要执行一次拷贝操作。
用户空间的service和client进程通过内核空间的binder驱动(/dev/binder)进行通讯。

service manager,用户空间进程,binder通信机制的上下文管理者。service启动时注册到service manager中。
client从中获取service实例,间接使用service的功能。

虚拟进程地址空间(vm_area_struct)和虚拟内核地址空间(vm_struct)都映射到同一块物理内存空间。
当Client端与Server端发送数据时,Client(作为数据发送端)先从自己的进程空间把IPC通信数据copy_from_user拷贝到内核空间,
而Server端(作为数据接收端)与内核共享数据,不再需要拷贝数据,而是通过内存地址空间的偏移量,即可获悉内存地址,
整个过程只发生一次内存拷贝。一般地做法,需要Client端进程空间拷贝到内核空间,再由内核空间拷贝到Server进程空间,会发生两次拷贝。

对于进程和内核虚拟地址映射到同一个物理内存的操作是发生在数据接收端,而数据发送端还是需要将用户态的数据复制到内核态

binder 驱动程序

基于 Android 9.0 源码。
binder驱动以misc设备进行注册,作为虚拟字符设备,没有直接操作硬件,只是对设备内存的处理。
主要是驱动设备的初始化(binder_init),打开 (binder_open),映射(binder_mmap),数据操作(binder_ioctl)。

比如打开binder驱动,从用户态到系统调用到内核态调用链,open->__open->binder_open

涉及文件

common/include/uapi/linux/android/binder.h
common/drivers/android/binder.c
common/drivers/android/binder_alloc.c
common/drivers/android/binder_alloc_selftest.c
common/drivers/android/binder_alloc.h
common/drivers/android/binder_trace.h

关键数据结构

    struct binder_context {
        struct binder_node *binder_context_mgr_node;
        struct mutex context_mgr_node_lock;

        kuid_t binder_context_mgr_uid;
        const char *name;
    };

    struct binder_device {
        struct hlist_node hlist;
        struct miscdevice miscdev; // misc 设备
        struct binder_context context;
    };

    // binder进程信息
    struct binder_proc {
        struct hlist_node proc_node;        // binder_procs列表元素
        struct rb_root threads;             // 存储进程中线程的红黑树
        struct rb_root nodes;               // 存储此进程相关 binder node 的红黑树,依据node->ptr排序
        struct rb_root refs_by_desc;        // 以ref->desc排序的 binder ref 的红黑树
        struct rb_root refs_by_node;        // 以ref->node排序的 binder ref 的红黑树
        struct list_head waiting_threads;   // 等待执行进程work的线程列表
        int pid;                            // 进程的 group_leader 的pid
        struct task_struct *tsk;            // 进程的 group_leader 的 task_struct
        struct files_struct *files; //
        struct mutex files_lock;
        struct hlist_node deferred_work_node; // binder_deferred_list 的元素
        int deferred_work;                  // 需要执行的 deferred work 的位图
        bool is_dead;                       // 进程是否死亡,等待清除

        struct list_head todo;              // 进程执行work列表
        struct binder_stats stats;          // 每进程 binder 统计信息
        struct list_head delivered_death;   // 分发死亡通知列表
        int max_threads;                    // binder 进程最大支持多少线程
        int requested_threads;              // 请求了但是没启动的binder线程数,0/1
        int requested_threads_started;      // 启动了的binder线程数
        int tmp_ref;                        // 临时引用表示进程使用中
        struct binder_priority default_priority; // 默认调度优先级
        struct dentry *debugfs_entry;       // debugfs 节点
        struct binder_alloc alloc;          // binder allocator 信息
        struct binder_context *context;     // 此进程的binder_context,初始后不可变
        spinlock_t inner_lock;
        spinlock_t outer_lock;
    };
    
    struct binder_transaction_data 
    {
        /* The first two are only used for bcTRANSACTION and brTRANSACTION,
         * identifying the target and contents of the transaction.
         */
        union {
            /* target descriptor of command transaction */
            __u32   handle;     // binder_ref(即handle)
            /* target descriptor of return transaction */
            binder_uintptr_t ptr;   // Binder_node的内存地址
        } target;  // 对于BpBinder则使用handle,对于BBinder则使用ptr
        binder_uintptr_t    cookie; /* target object cookie */   // BBinder指针
        __u32       code;       /* transaction command */        // RPC代码,代表Client与Server双方约定的命令码

        /* General information about the transaction. */
        __u32           flags;    // 标志位,比如TF_ONE_WAY代表异步,即不等待Server端回复
        pid_t       sender_pid;   // 发送端进程的pid
        uid_t       sender_euid;  // 发送端进程的uid
        binder_size_t   data_size;  /* number of bytes of data */   // data数据的总大小
        binder_size_t   offsets_size;   /* number of bytes of offsets */  // IPC对象的大小

        /* If this transaction is inline, the data immediately
         * follows here; otherwise, it ends with a pointer to
         * the data buffer.
         */
        union {
            struct {
                /* transaction data */
                binder_uintptr_t    buffer;   // 数据区起始地址
                /* offsets from buffer to flat_binder_object structs */
                binder_uintptr_t    offsets;  // 数据区IPC对象偏移量
            } ptr;
            __u8    buf[8];
        } data;   //RPC数据
    };

关键全局变量

    static HLIST_HEAD(binder_devices); // 存储各个binder设备
    static HLIST_HEAD(binder_procs); //存储binder_proc
    
    // 映射binder操作函数
    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,
    };
    
    // 内存map操作函数
    static const struct vm_operations_struct binder_vm_ops = {
        .open = binder_vma_open,
        .close = binder_vma_close,
        .fault = binder_vm_fault,
    };

关键函数

注册 /dev/binder 或 /dev/hwbinder

    static int __init binder_init(void) 
    {   
        ...
        struct binder_device *device; // 驱动设备
        ...
        binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL); //创建debugfs文件系统
        ...
        // 初始化 binder device, 可能有多个
        device_tmp = device_names;
        while ((device_name = strsep(&device_tmp, ",")))
            ret = init_binder_device(device_name);
    }
    
    // 初始化binder驱动设备
    static int __init init_binder_device(const char *name)
    {
        int ret;
        struct binder_device *binder_device;

        binder_device = kzalloc(sizeof(*binder_device), GFP_KERNEL);
        binder_device->miscdev.fops = &binder_fops; //设备的文件操作结构
        binder_device->miscdev.minor = MISC_DYNAMIC_MINOR; //次设备号 动态分配
        binder_device->miscdev.name = name; //设备名字

        binder_device->context.binder_context_mgr_uid = INVALID_UID;
        binder_device->context.name = name;

        // 注册misc设备
        ret = misc_register(&binder_device->miscdev);
        
        // 添加设备到设备列表中
        hlist_add_head(&binder_device->hlist, &binder_devices);

        return ret;
    }

进程使用binder通信机制前,需先打开binder设备获取文件描述符。通过文件操作来使用binder
驱动将所有打开了binder设备的进程都加入到全局hash队列 binder_procs 中。

filp 指向一个打开的文件结构体,当进程调用open会获取到一个文件描述符,这两者是关联的。
进程后续以此文件描述符为参数执行mmap或ioctl与驱动交互时,内核会将关联的文件结构体传递给驱动。
驱动通过file结构体的 private_data 获得进程的 binder_proc。

    // 创建binder_proc对象,并把当前进程等信息保存到binder_proc对象
    static int binder_open(struct inode *nodp, struct file *filp)
    {
        struct binder_proc *proc;
        struct binder_device *binder_dev;

        proc = kzalloc(sizeof(*proc), GFP_KERNEL); //分配 binder_proc 进程结构体
        
        get_task_struct(current->group_leader);
        proc->tsk = current->group_leader; //将当前线程的task保存到binder进程的tsk
        
        INIT_LIST_HEAD(&proc->todo); //初始化todo列表
        if (binder_supported_policy(current->policy)) {
            proc->default_priority.sched_policy = current->policy;
            proc->default_priority.prio = current->normal_prio;
        } else {
            proc->default_priority.sched_policy = SCHED_NORMAL;
            proc->default_priority.prio = NICE_TO_PRIO(0); //将当前进程的nice值转换为进程优先级
        }

        binder_dev = container_of(filp->private_data, struct binder_device, miscdev); // 获取binder设备结构体
        proc->context = &binder_dev->context;
        binder_alloc_init(&proc->alloc); // 用于 mmap 分配空间

        binder_stats_created(BINDER_STAT_PROC); //BINDER_PROC对象创建数加1
        proc->pid = current->group_leader->pid;
        INIT_LIST_HEAD(&proc->delivered_death); //初始化已分发的死亡通知列表
        INIT_LIST_HEAD(&proc->waiting_threads); //初始化wait队列
        filp->private_data = proc; //file文件指针的private_data变量指向binder_proc数据

        hlist_add_head(&proc->proc_node, &binder_procs); // 将当前 binder_proc 节点添加到列表中

        return 0;
    }

vma : 用户虚拟内存空间
1,在内核虚拟内存空间中申请大小和用户虚拟内存大小相同的空间。
2,申请1 page的物理内存,将此内存映射到内核虚拟地址空间和用户虚拟地址空间,实现内核和用户空间buffer的同步操作

user_buffer_offset 是虚拟进程地址与虚拟内核地址的差值(该值为负数)。
同一物理地址,当内核地址为kernel_addr,则进程地址为proc_addr = kernel_addr + user_buffer_offset。

    // 
    static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
    {
        struct binder_proc *proc = filp->private_data;

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

        vma->vm_flags |= VM_DONTCOPY | VM_MIXEDMAP;
        vma->vm_flags &= ~VM_MAYWRITE;

        vma->vm_ops = &binder_vm_ops; // 注册虚拟内存操作函数
        vma->vm_private_data = proc;

        // 初始化vma描述的空间,用于之后binder buffer的分配
        ret = binder_alloc_mmap_handler(&proc->alloc, vma);
        
        proc->files = get_files_struct(current);
    }

    int binder_alloc_mmap_handler(struct binder_alloc *alloc, struct vm_area_struct *vma)
    {
        struct vm_struct *area;
        struct binder_buffer *buffer;
        
        // 采用IOREMAP方式,分配一个连续的内核虚拟空间,与进程虚拟空间大小一致
        area = get_vm_area(vma->vm_end - vma->vm_start, VM_ALLOC);
        
        alloc->buffer = area->addr; // 指向内核虚拟空间的地址
        
        // 地址偏移量 = 用户虚拟地址空间 - 内核虚拟地址空间
        alloc->user_buffer_offset = vma->vm_start - (uintptr_t)alloc->buffer;
        
        // 分配物理页的指针数组,数组大小为vma的等效page个数;
        alloc->pages = kzalloc(sizeof(alloc->pages[0]) *
                       ((vma->vm_end - vma->vm_start) / PAGE_SIZE),
                       GFP_KERNEL);
        // 记录buffer大小
        alloc->buffer_size = vma->vm_end - vma->vm_start;

        buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);

        buffer->data = alloc->buffer;
        list_add(&buffer->entry, &alloc->buffers);
        buffer->free = 1;
        binder_insert_free_buffer(alloc, buffer); // 空闲的buffer
        alloc->free_async_space = alloc->buffer_size / 2; // 异步可用空间大小为buffer总大小的一半。
        barrier();
        alloc->vma = vma;
        alloc->vma_vm_mm = vma->vm_mm;
        /* Same as mmgrab() in later kernel versions */
        atomic_inc(&alloc->vma_vm_mm->mm_count);

    }

负责在两个进程间收发IPC数据和IPC reply数据。

ioctl(文件描述符,ioctl命令,数据类型)

ioctl 命令及相关类型

#define BINDER_WRITE_READ       _IOWR('b', 1, struct binder_write_read)
#define BINDER_SET_IDLE_TIMEOUT     _IOW('b', 3, __s64)
#define BINDER_SET_MAX_THREADS      _IOW('b', 5, __u32)
#define BINDER_SET_IDLE_PRIORITY    _IOW('b', 6, __s32)
#define BINDER_SET_CONTEXT_MGR      _IOW('b', 7, __s32)
#define BINDER_THREAD_EXIT      _IOW('b', 8, __s32)
#define BINDER_VERSION          _IOWR('b', 9, struct binder_version)
#define BINDER_GET_NODE_DEBUG_INFO  _IOWR('b', 11, struct binder_node_debug_info)

struct binder_write_read {
    binder_size_t       write_size; /* bytes to write */
    binder_size_t       write_consumed; /* bytes consumed by driver */
    binder_uintptr_t    write_buffer;
    binder_size_t       read_size;  /* bytes to read */
    binder_size_t       read_consumed;  /* bytes consumed by driver */
    binder_uintptr_t    read_buffer;
};

binder_get_thread
从 binder_proc 中查找 binder_thread ,如果当前线程已经加入到proc的线程队列则直接返回,
如果不存在则创建binder_thread,并将当前线程添加到当前的proc

    static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
    {
        struct binder_proc *proc = filp->private_data;
        struct binder_thread *thread;
        
        unsigned int size = _IOC_SIZE(cmd);
        void __user *ubuf = (void __user *)arg;
        
        // 进入休眠状态,直到中断唤醒
        ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);

        // 获取 binder_thread
        thread = binder_get_thread(proc);
    
        // 处理binder命令
        switch (cmd) {
            case BINDER_WRITE_READ:         // 进行binder的读写操作,Binder读写交互场景,IPC.talkWithDriver
                ret = binder_ioctl_write_read(filp, cmd, arg, thread);
                break;
            case BINDER_SET_MAX_THREADS: {  // 设置binder最大支持的线程数
                int max_threads;
                copy_from_user(&max_threads, ubuf,  sizeof(max_threads));
                proc->max_threads = max_threads;
                break;
            }
            case BINDER_SET_CONTEXT_MGR: // 成为binder的上下文管理者,也就是ServiceManager成为守护进程
                ret = binder_ioctl_set_ctx_mgr(filp);
                break;
            case BINDER_THREAD_EXIT:  // 当binder线程退出,释放binder线程
                binder_thread_release(proc, thread);
                thread = NULL;
                break;
            case BINDER_VERSION: {  // 获取binder的版本号
                struct binder_version __user *ver = ubuf;
                put_user(BINDER_CURRENT_PROTOCOL_VERSION, &ver->protocol_version)
                break;
            }
            default:
                ret = -EINVAL;
                goto err;
        }
    }
上一篇 下一篇

猜你喜欢

热点阅读