Android Binder 机制初步学习 笔记(二)—— Binder 设备基本操作实现


NOTE

  • 源码版本:Android 7.1.2。
  • 内核版本:android-goldfish-3.4
  • 内核下载:git clone https://aosp.tuna.tsinghua.edu.cn/kernel/goldfish.git (清华镜像站)
  • 以下分析思路均来自老罗的《Android 系统源代码情景分析(修订版)》

Binder 设备基本操作实现

  • 从这里开始分析 binder.c 中关于 binder 设备操作的实现:
    • init:初始化。
    • open:打开设备。
    • mmap:内存映射。
    • ioctlI/O 管理。
  • 没有特别说明时,文件位置均为:
    • kernel/goldfish/drivers/staging/android/binder.c

1. Binder 设备初始化

1.1 binder_init( )

  1. 调用 create_singlethread_workqueue() 创建一个工作队列。
  2. 调用 debugfs_create_dir() 创建目录 binder 作为根目录
  3. 根目录下创建一个目录 proc
    • 每个使用了 Binder 机制的进程都在此有一个对应的文件。
    • 这些文件以进程 ID 命名。
    • 通过文件可读取到对应的 Binder 线程池、实体、引用对象以及内核缓冲区等信息。
  4. 调用 misc_register() 创建一个 Binder 设备。
  5. 在根目录下创建五个文件:
    • state
    • stats
    • transactions
    • transaction_log
    • failed_transaction_log
    • 通过它们读取驱动运行状况,如协议请求次数、日志记录信息等。
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;
}

1.2 binder_fops

  • 全局变量 binder_fops 指定了设备文件的操作方法列表:
    • 文件打开:binder_open()
    • 内存映射:binder_mmap()
    • IO 控制:binder_ioctl()
static const struct file_operations binder_fops = {
        .owner = THIS_MODULE,
        .poll = binder_poll,
        .unlocked_ioctl = binder_ioctl,
        .mmap = binder_mmap,
        .open = binder_open,
        .flush = binder_flush,
        .release = binder_release,
};

static struct miscdevice binder_miscdev = {
        .minor = MISC_DYNAMIC_MINOR,
        .name = "binder",
        .fops = &binder_fops
};

2. Binder 设备文件打开

  • 进程使用 Binder 机制之前,要先打开设备文件以获得一个文件描述符
  • 通过文件描述符,进程可以与驱动交互,从而可使用 Binder 通信。

2.1 binder_open( )

  1. 创建一个 binder_proc 结构体 proc,并对它进行初始化:
    • 申请必要的内存空间kzalloc()
    • 初始化任务控制块proc->tsk = current
    • 初始化工作项队列INIT_LIST_HEAD(&proc->todo)
    • 初始化等待队列init_waitqueue_head(&proc->wait)
    • 设置默认优先级proc->default_priority = task_nice(current)
    • 创建统计信息binder_stats_created()
    • 将 proc 加入全局 hash 队列hlist_add_head(&proc->proc_node, &binder_procs)
    • 设置进程号proc->pid = current->group_leader->pid
    • 初始化死亡通知工作项队列INIT_LIST_HEAD(&proc->delivered_death)
  2. 将初始化成功的 proc 保存
    • filp 指向一个打开文件结构体。
    • proc 保存在其成员 private_data 中。
    • 进程调用 open 打开设备后,内核会返回一个文件描述符给进程,而这个描述符与 filp 指向的结构体是关联在一起的。
    • 进程以描述符为参数调用 mmapioctl 与驱动交互时,驱动可通过 private_data 获取 proc
  3. 读取调试相关的信息
    • 在目标设备上的 /proc/binder/proc 目录下创建以进程 ID 为名的只读文件。
    • 通过这个文件,我们可以获得相应进程的线程池、实体对象、引用对象、缓冲区等信息。
static HLIST_HEAD(binder_procs);

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

3. Binder 设备文件的内存映射

  • 打开设备文件后,需要调用 mmap 函数将这个设备文件映射到进程的地址空间,然后才可以使用 Binder 进程间通信机制。
  • 内存映射目的是给进程分配内核缓冲区,以便进行数据通信。

3.1 binder_mmap( )

  • 该函数比较长,根据参考书所述,可以分成两段来分析。
  • 第一段
    1. 参数 vma 指向结构体 vm_area_struct,用于描述一段虚拟地址空间。
    2. 变量 area 指向一个结构体 vm_struct,这也是用来描述虚拟地址空间的。
    3. 它们描述的虚拟地址是连续的,但对应的物理页面可以不连续。
    4. vm_area_structvm_struct 区别
      • 在 Linux 内核中,一个进程可占虚拟地址空间为 4G,其中 0G ~ 3G 为用户地址空间,剩下为内核地址空间。
      • vm_area_struct
        • 描述用户地址空间。
        • 空间范围 0G ~ 3G
      • vm_struct
        • 描述内核地址空间。
        • 空间范围 (3G + 896M + 8M)~ 4G
      • 注意到 4G 的空间中有一段空出来的地址:
        • 3G ~(3G + 896M):映射物理内存的前 896M,它们之间是简单线性关系。
        • (3G + 896M)~(3G + 896M + 8M):安全保护区,用于检测非法指针。
    5. filp 指向一个打开文件结构体
      • 成员变量 private_data 指向一个进程结构体 binder_proc
      • private_data 转为 proc,即获取了对应的进程。
    6. vma 的成员变量 vma_startvma_end 指定了要映射的用户地址空间范围
      • 若范围大于 4M,则将其截断为 4M(断尾)。
      • 从这可以知道,Binder 驱动最多可以为进程分配 4M 内核缓冲区
    7. 驱动分配的内核缓冲区在用户空间只读
      • #define FORBIDDEN_MMAP_FLAGS (VM_WRITE)
      • 若检查出指定要映射的用户地址空间可写,则返回。
    8. 驱动分配的内核缓冲区在用户空间不可拷贝
      • VM_DONTCOPY 位置 1,表示不可拷贝。
      • VM_MAYWRITE 位置 0,禁止设置可能会执行写操作标志位的操作。
    9. 检查 buffer 是否已经指向一块内核缓冲区,若是则出错返回。
    int ret;
    struct vm_struct *area;
    struct binder_proc *proc = filp->private_data;
    const
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值