Binder驱动

数据结构

binder_proc

// 描述使用binder IPC的进程
struct binder_proc {
        struct hlist_node proc_node; // binder_proc在全局binder_procs哈希链表中的节点
        struct rb_root threads; // binder线程红黑树
        struct rb_root nodes; // binder实体对象红黑树
        struct rb_root refs_by_desc; // binder引用对象红黑树,以binder引用描述符为key
        struct rb_root refs_by_node; // binder引用对象红黑树,以binder_node地址为key
        int pid; // binder进程pid
        struct vm_area_struct *vma; ![binder_buffer.png](https://upload-images.jianshu.io/upload_images/1945694-a28b4712fba2afab.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
// binder buffer对应的用户地址空间
        struct mm_struct *vma_vm_mm; // vma所属的进程地址空间
        struct task_struct *tsk; // 进程的task_struct
        struct files_struct *files;
        struct hlist_node deferred_work_node;
        int deferred_work;
        void *buffer; // binder buffer的内核地址空间地址
        ptrdiff_t user_buffer_offset; // 映射binder buffer的内核地址空间与用户地址空间地址差

        struct list_head buffers; // binder buffer链表
        struct rb_root free_buffers; // 空闲的binder buffer红黑树
        struct rb_root allocated_buffers; // 正在使用的binder buffer红黑树
        size_t free_async_space;

        struct page **pages; // 存储binder buffer的物理页
        size_t buffer_size; // binder buffer的总大小
        uint32_t buffer_free; // 空闲的binder buffer大小
        struct list_head todo; // 进程待处理的binder_work链表
        wait_queue_head_t wait; // 等待队列头
        struct binder_stats stats; // binder状态
        struct list_head delivered_death;
        int max_threads; // 用户态设定的最大的binder线程数
        int requested_threads; // 标示binder驱动正在请求创建binder thread
        int requested_threads_started; // binder驱动请求创建的binder thread数
        int ready_threads; // 空闲的binder线程数
        long default_priority;
        struct dentry *debugfs_entry;
};

binder_thread

// 描述参与binder IPC的binder线程
struct binder_thread {
        struct binder_proc *proc; // binder线程所属进程的binder_proc
        struct rb_node rb_node; // binder线程红黑树节点
        int pid;
        int looper;
        struct binder_transaction *transaction_stack; // binder线程事务栈
        struct list_head todo; // binder线程待处理的binder_work链表
        uint32_t return_error; /* Write failed, return error code in read buf */
        uint32_t return_error2; /* Write failed, return error code in read */
                /* buffer. Used when sending a reply to a dead process that */
                /* we are also waiting on */
        wait_queue_head_t wait;
        struct binder_stats stats;
};

binder_transaction

// 描述一次binder IPC
struct binder_transaction {
        int debug_id; // 调试用
        struct binder_work work; // 对应的binder_work
        struct binder_thread *from; // 发送方binder_thread
        struct binder_transaction *from_parent;
        struct binder_proc *to_proc; // 接收方binder_proc
        struct binder_thread *to_thread; // 接收方binder_thread
        struct binder_transaction *to_parent;
        unsigned need_reply:1;
        /* unsigned is_dead:1; */       /* not used at the moment */

        struct binder_buffer *buffer; // binder_transaction使用的binder buffer
        unsigned int    code; // binder IPC命令编码
        unsigned int    flags; // binder IPC标志,如TF_ONE_WAY等
        long    priority;
        long    saved_priority;
        kuid_t  sender_euid;
};

binder_buffer

// 接收方用于存储binder IPC数据
struct binder_buffer {
        struct list_head entry; /* free and allocated entries by address */
        struct rb_node rb_node; /* free entry by size or allocated entry */
                                /* by address */
        unsigned free:1;
        unsigned allow_user_free:1;
        unsigned async_transaction:1;
        unsigned debug_id:29;

        struct binder_transaction *transaction;

        struct binder_node *target_node; // 接收方binder_node或者null(reply)
        size_t data_size;
        size_t offsets_size;
        uint8_t data[0]; // data起始地址
};
  • binder_buffer内存布局


flat_binder_object

// 用于进程间传递binder对象,binder驱动会修改该结构体的成员
struct flat_binder_object {
        /* 8 bytes for large_flat_header. */
        __u32           type; // binder对象类型
        __u32           flags;

        // binder对象的类型决定下面的变量值
        /* 8 bytes of data. */
        union {
                binder_uintptr_t        binder; /* local object */ 
                __u32                   handle; /* remote object */
        };

        /* extra data associated with local object */
        binder_uintptr_t        cookie;
};

binder_transaction_data

// 描述binder IPC数据
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;
                /* target descriptor of return transaction */
                binder_uintptr_t ptr;
        } target;
        binder_uintptr_t        cookie; /* target object cookie */
        __u32           code;           /* transaction command */

        /* General information about the transaction. */
        __u32           flags;
        pid_t           sender_pid;
        uid_t           sender_euid;
        binder_size_t   data_size;      /* number of bytes of data */
        binder_size_t   offsets_size;   /* number of bytes of offsets */

        /* 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;
                } ptr;
                __u8    buf[8];
        } data;
};

binder_node

// 描述binder实体对象
struct binder_node {
        int debug_id;
        struct binder_work work;
        union {
                struct rb_node rb_node;
                struct hlist_node dead_node;
        };
        struct binder_proc *proc;
        struct hlist_head refs; // binder实体对应的binder引用的hash链表
        int internal_strong_refs;
        int local_weak_refs;
        int local_strong_refs;
        binder_uintptr_t ptr; // binder实体对应的服务的弱引用
        binder_uintptr_t cookie; // binder实体对应的服务的地址
        unsigned has_strong_ref:1;
        unsigned pending_strong_ref:1;
        unsigned has_weak_ref:1;
        unsigned pending_weak_ref:1;
        unsigned has_async_transaction:1;
        unsigned accept_fds:1;
        unsigned min_priority:8;
        struct list_head async_todo;
};

binder_ref

// 描述binder引用对象
struct binder_ref {
        /* Lookups needed: */
        /*   node + proc => ref (transaction) */
        /*   desc + proc => ref (transaction, inc/dec ref) */
        /*   node => refs + procs (proc exit) */
        int debug_id;
        struct rb_node rb_node_desc;
        struct rb_node rb_node_node;
        struct hlist_node node_entry;
        struct binder_proc *proc; // 所属进程的binder_proc
        struct binder_node *node; // 对应的binder实体
        uint32_t desc; // 描述符(用户空间handle)
        int strong;
        int weak;
        struct binder_ref_death *death;
};

binder_work

// 描述binder工作项
struct binder_work {
        struct list_head entry;
        enum {
                BINDER_WORK_TRANSACTION = 1,
                BINDER_WORK_TRANSACTION_COMPLETE,
                BINDER_WORK_NODE,
                BINDER_WORK_DEAD_BINDER,
                BINDER_WORK_DEAD_BINDER_AND_CLEAR,
                BINDER_WORK_CLEAR_DEATH_NOTIFICATION,
        } type; // 工作类型
};

常用的binder_driver_return_protocol(binder驱动返回用户态)

协议名称 含义
BR_ERROR binder通信发生错误
BR_OK binder通信正常
BR_TRANSACTION binder驱动向服务端发送数据
BR_REPLY binder驱动向客户端发送回复数据
BR_DEAD_REPLY 通信失败(目标进程/线程死亡或服务binder实体为空)
BR_TRANSACTION_COMPLETE binder驱动向客户端(服务端)发送传输成功
BR_NOOP 空操作
BR_SPAWN_LOOPER binder驱动请求创建binder线程
BR_DEAD_BINDER 服务死亡通知
BR_FAILED_REPLY 通信失败

常用的binder_driver_command_protocol(用户态发送命令到binder驱动)

协议名称 含义
BC_TRANSACTION 客户端向binder驱动发送数据
BC_REPLY 服务端向binder驱动发送回复数据
BC_FREE_BUFFER 客户端(服务端)请求binder驱动释放binder buffer
BC_REGISTER_LOOPER binder线程运行
BC_ENTER_LOOPER binder主线程运行
BC_EXIT_LOOPER binder线程退出
BC_REQUEST_DEATH_NOTIFICATION 注册服务死亡通知
BC_CLEAR_DEATH_NOTIFICATION 注销服务死亡通知
BC_DEAD_BINDER_DONE 服务死亡通知处理完成

binder_init

static int __init binder_init(void)
{
        int ret;

        // 创建单线程工作队列
        binder_deferred_workqueue = create_singlethread_workqueue("binder");
        if (!binder_deferred_workqueue)
                return -ENOMEM;
        // 在/sys/kernel/debug下创建binder调试目录
        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);
        // 注册binder设备
        ret = misc_register(&binder_miscdev);
        if (binder_debugfs_dir_entry_root) {
                // 创建 /sys/kernel/debug/binder/state
                debugfs_create_file("state",
                                    S_IRUGO,
                                    binder_debugfs_dir_entry_root,
                                    NULL,
                                    &binder_state_fops);
                // 创建/sys/kernel/debug/binder/stats
                debugfs_create_file("stats",
                                    S_IRUGO,
                                    binder_debugfs_dir_entry_root,
                                    NULL,
                                    &binder_stats_fops);
                // 创建/sys/kernel/debug/binder/transactions
                debugfs_create_file("transactions",
                                    S_IRUGO,
                                    binder_debugfs_dir_entry_root,
                                    NULL,
                                    &binder_transactions_fops);
                // 创建/sys/kernel/debug/binder/transaction_log
                debugfs_create_file("transaction_log",
                                    S_IRUGO,
                                    binder_debugfs_dir_entry_root,
                                    &binder_transaction_log,
                                    &binder_transaction_log_fops);
                // 创建/sys/kernel/debug/binder/failed_transaction_log
                debugfs_create_file("failed_transaction_log",
                                    S_IRUGO,
                                    binder_debugfs_dir_entry_root,
                                    &binder_transaction_log_failed,
                                    &binder_transaction_log_fops);
        }
        return ret;
}

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);
        // 创建binder_proc对象
        proc = kzalloc(sizeof(*proc), GFP_KERNEL);
        if (proc == NULL)
                return -ENOMEM;
        // 增加当前进程task_struct的引用计数
        get_task_struct(current->group_leader);
        proc->tsk = current->group_leader;
        // 初始化待处理工作项链表
        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);
        // binder_proc添加到全局binder_procs哈希链表中
        hlist_add_head(&proc->proc_node, &binder_procs);
        proc->pid = current->group_leader->pid;
        INIT_LIST_HEAD(&proc->delivered_death);
        // binder_proc放到文件的私有数据中
        filp->private_data = proc;

        binder_unlock(__func__);

        if (binder_debugfs_dir_entry_proc) {
                char strbuf[11];

                snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
                // 在/sys/kernel/debug/binder/proc下为binder进程创建调试节点
                proc->debugfs_entry = debugfs_create_file(strbuf, S_IRUGO,
                        binder_debugfs_dir_entry_proc, proc, &binder_proc_fops);
        }

        return 0;
}

binder_ioctl

用户态通常通过ioctl系统调用访问binder驱动,最终调用binder_ioctl。

  • ioctl命令
ioctl命令名称 含义
BINDER_WRITE_READ 用户态读写binder驱动(最常用)
BINDER_SET_MAX_THREADS 设置最大的binder线程数
BINDER_SET_CONTEXT_MGR ServiceManager注册为上下文管理者
BINDER_THREAD_EXIT binder线程退出
BINDER_VERSION 获取binder驱动协议版本

以后结合具体场景再来看binder_ioctl的实现。

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;

        if (proc->tsk != current->group_leader)
                return -EINVAL;
        // vma表示用户态分配的虚拟内存区域(通常1M - 8K),不能超过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;
        }
        // 用于分配binder_buffer的内核地址空间地址
        proc->buffer = area->addr;
        // 用户虚拟地址空间与内核虚拟地址空间的差
        proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
        mutex_unlock(&binder_mmap_lock);
        ......
        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;

        // 分配物理页,用户虚拟地址空间与内核虚拟地址空间映射到相同的物理地址
        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
        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;

        /*pr_info("binder_mmap: %d %lx-%lx maps %p\n",
                 proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/
        return 0;
        ......
}

下面看binder_update_page_range

static int binder_update_page_range(struct binder_proc *proc, int allocate,
                                    void *start, void *end,
                                    struct vm_area_struct *vma)
{
        // allocate为1,end - start = PAGE_SIZE暂时分配一个物理页,以后按需分配以及释放
        void *page_addr;
        unsigned long user_page_addr;
        struct page **page;
        struct mm_struct *mm;

        binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
                     "%d: %s pages %p-%p\n", proc->pid,
                     allocate ? "allocate" : "free", start, end);

        if (end <= start)
                return 0;

        trace_binder_update_page_range(proc, allocate, start, end);
        ......
        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);
                // 为binder_buffer分配物理页
                *page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
                if (*page == NULL) {
                        pr_err("%d: binder_alloc_buf failed for page at %p\n",
                                proc->pid, page_addr);
                        goto err_alloc_page_failed;
                }
                // 物理页映射到内核地址空间
                ret = map_kernel_range_noflush((unsigned long)page_addr,
                                        PAGE_SIZE, PAGE_KERNEL, page);
                flush_cache_vmap((unsigned long)page_addr,
                                (unsigned long)page_addr + PAGE_SIZE);
                if (ret != 1) {
                        pr_err("%d: binder_alloc_buf failed to map page at %p in kernel\n",
                               proc->pid, page_addr);
                        goto err_map_kernel_failed;
                }
                
                user_page_addr =
                        (uintptr_t)page_addr + proc->user_buffer_offset;
                // 物理页映射到用户地址空间
                ret = vm_insert_page(vma, user_page_addr, page[0]);
                ......
        }
        ......
}
  • 执行binder_mmap后的内存布局


binder_flush

binder_release

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容