Binder系列1-Binder Driver

本文详细介绍了Android系统中的Binder通信机制,包括Binder驱动如何在Linux内核与用户空间之间实现通信,以及Binder驱动的初始化、打开、内存映射和ioctl操作。Binder是基于C/S架构,利用mmap技术实现一次拷贝的数据传输,降低了进程间通信的成本。文章还探讨了Binder如何避免GPL协议的约束,成为开源与商业化的成功案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Binder系列1-Binder Driver

1. Android采用Binder

  1. Linux Kernel是开源系统,所开放源代码许可协议GPL保护,Binder Driver运行在Linux Kernel是GPL协议。用户空间采用Apache-2.0协议, 内核空间用户空间 (即在GPL协议与Apache-2.0协议)之间的Lib库中采用BSD证授权方法,GPL协议限定在Linux Kernel,有效隔断了GPL的传染性,仍有较大争议。开源与商业化共存的一个成功典范。【Android,在争议中逃离 Linux 内核的 GPL 约束】

  2. Binder 是基于开源的OpenBinder实现的,而OpenBinder的作者在Google工作,直接采用 Binder 作为核心的IPC机制。

传统的 Linux 通信机制,比如 Socket,管道等都是内核支持的;但是 Binder 并不是 Linux 内核的一部分,它是怎么做到访问内核空间的呢? Linux 的动态可加载内核模块(Loadable Kernel Module, LKM )机制解决了这个问题;模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。它在运行时被链接到内核作为内核的一部分在 内核空间 运行。这样,Android系统可以通过添加一个内核模块运行在 内核空间 ,用户进程之间的通过这个模块作为桥梁,就可以完成通信了。

所以,在 Android 系统中,这个运行在 内核空间 的,负责各个用户进程通过 Binder 通信的内核模块叫做 Binder 驱动

在这里插入图片描述

2. Binder Driver实现

Binder驱动 是一个misc device,是虚拟字符设备,主设备号是10,其设备节点是/dev/binder(该节点并不对应真实的硬件设备)。misc类型驱动只需要调用misc_deregister

在Linux系统中,系统调用(System Call)是用户空间访问内核的唯一手段,也是惟一的合法入口。用户进程与内核之间使用系统调用进行沟通。

主要是驱动设备的初始化(binder_init),打开 (binder_open),映射(binder_mmap),数据操作(binder_ioctl)。

linux/drivers/android/binder.c
linux/include/uapi/linux/android/binder.h

方法描述定义位置
binder_init初始化虚拟字符设备,init_binder_device()\init_binderfs()kernel/drivers/android/binder.c
binder_open打开Binder驱动设备, binder_procs_lock同步锁kernel/drivers/android/binder.c
binder_mmap申请内存空间, binder_alloc_mmap_lock同步锁kernel/drivers/android/binder.c
binder_ioctl对设备文件实现读、写等一些扩展功能 ioctl 操作, binder_procs_lock同步锁;当处于binder_thread_read过程,read_buffer无数据则释放同步锁kernel/drivers/android/binder.c

2.1 binder_init 注册misc设备节点/dev/binder

  • binder_init() -> init_binder_device(device_name) -> misc_register(&binder_device->miscdev) 注册misc设备节点
  • CONFIG_ANDROID_BINDERFSbinder_devices_param = CONFIG_ANDROID_BINDER_DEVICESkernel/configs/xxx/android-base.config配置
    在这里插入图片描述
  • binder_fops Binder驱动支持的文件操作,其中使用做多的binder_ioctl \ binder_mmap \ binder_open
  • init_binderfs() 注册register_filesystem(&binder_fs_type)(linux/drivers/android/binderfs.c)
    在这里插入图片描述
const struct file_operations binder_fops = {
	.owner = THIS_MODULE,
	.poll = binder_poll,
	.unlocked_ioctl = binder_ioctl,
	.compat_ioctl = compat_ptr_ioctl,
	.mmap = binder_mmap,
	.open = binder_open,
	.flush = binder_flush,
	.release = binder_release,
};

static int __init init_binder_device(const char *name)
{
	// ... ...
	binder_device->miscdev.fops = &binder_fops;
	binder_device->miscdev.minor = MISC_DYNAMIC_MINOR;
	binder_device->miscdev.name = name;
	// ... ...
	ret = misc_register(&binder_device->miscdev);
	// ... ...
}

static int __init binder_init(void)
{
	// ... ...
	if (!IS_ENABLED(CONFIG_ANDROID_BINDERFS) &&
	    strcmp(binder_devices_param, "") != 0) {
		/*
		* Copy the module_parameter string, because we don't want to
		* tokenize it in-place.
		 */
		device_names = kstrdup(binder_devices_param, GFP_KERNEL);
		if (!device_names) {
			ret = -ENOMEM;
			goto err_alloc_device_names_failed;
		}

		device_tmp = device_names;
		while ((device_name = strsep(&device_tmp, ","))) {
			ret = init_binder_device(device_name);
			if (ret)
				goto err_init_binder_device_failed;
		}
	}

	ret = init_binderfs();
	if (ret)
		goto err_init_binder_device_failed;

	return ret;

err_init_binder_device_failed:
	// ... ...
err_alloc_device_names_failed:
	// ... ...
}

device_initcall(binder_init);

2.2 binder_open 打开Binder驱动设备

  • binder_proc binder进程,每个进程都有独立的记录;proc->todo todo链表
  • binder_stats_created(BINDER_STAT_PROC) BINDER_PROC对象创建数加1,binder_stats是binder中统计数据载体
  • filp->private_data = proc 把binder_proc对象保存到文件指针filp
  • hlist_add_head(&proc->proc_node, &binder_procs) 把binder_proc加入到全局链表binder_procs
static int binder_open(struct inode *nodp, struct file *filp)
{
	struct binder_proc *proc, *itr;
	struct binder_device *binder_dev;
	struct binderfs_info *info;
	struct dentry *binder_binderfs_dir_entry_proc = NULL;
	// ... ...
	proc = kzalloc(sizeof(*proc), GFP_KERNEL);
	// ... ...
	INIT_LIST_HEAD(&proc->todo);
	init_waitqueue_head(&proc->freeze_wait);
	proc->default_priority = task_nice(current);
	// ... ...
	binder_stats_created(BINDER_STAT_PROC);
	proc->pid = current->group_leader->pid;
	INIT_LIST_HEAD(&proc->delivered_death);
	INIT_LIST_HEAD(&proc->waiting_threads);
	filp->private_data = proc;

	mutex_lock(&binder_procs_lock);
	hlist_for_each_entry(itr, &binder_procs, proc_node) {
		if (itr->pid == proc->pid) {
			existing_pid = true;
			break;
		}
	}
	hlist_add_head(&proc->proc_node, &binder_procs);
	mutex_unlock(&binder_procs_lock);
	// ... ...
}

2.3 binder_mmap 申请内存空间

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

	if (proc->tsk != current->group_leader)
		return -EINVAL;

	binder_debug(BINDER_DEBUG_OPEN_CLOSE,
		     "%s: %d %lx-%lx (%ld K) vma %lx pagep %lx\n",
		     __func__, 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) {
		pr_err("%s: %d %lx-%lx %s failed %d\n", __func__,
		       proc->pid, vma->vm_start, vma->vm_end, "bad vm_flags", -EPERM);
		return -EPERM;
	}
	vma->vm_flags |= VM_DONTCOPY | VM_MIXEDMAP;
	vma->vm_flags &= ~VM_MAYWRITE;

	vma->vm_ops = &binder_vm_ops;
	vma->vm_private_data = proc;

	return binder_alloc_mmap_handler(&proc->alloc, vma);
}

2.4 binder_ioctl 执行相应的ioctl操作

  • binder_ioctl 函数负责在两个进程间收发IPC数据和IPC reply数据处理
  • 核心方法binder_ioctl_write_read() -> binder_thread_write()\binder_thread_read()
    1. binder_thread_write() :get_user(cmd, (uint32_t __user *)ptr) 获取IPC数据BC请求码
    2. binder_thread_read():根据w->type(即binder_work->type)响应相应处理。
      在这里插入图片描述
ioctl命令cmd说明
BINDER_WRITE_READ收发Binder IPC数据
BINDER_SET_MAX_THREADS设置Binder线程最大个数
BINDER_SET_CONTEXT_MGR设置Service Manager节点
BINDER_SET_CONTEXT_MGR_EXT
BINDER_THREAD_EXIT释放Binder线程
BINDER_VERSION获取Binder版本信息
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);
	// ... ...
	trace_binder_ioctl(cmd, arg);
	ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
	// ... ...
	thread = binder_get_thread(proc);
	// ... ...
	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://......
	case BINDER_SET_CONTEXT_MGR_EXT://......
	case BINDER_SET_CONTEXT_MGR://......
	case BINDER_THREAD_EXIT://......
	case BINDER_VERSION://......
	case BINDER_GET_NODE_INFO_FOR_REF://......
	case BINDER_GET_NODE_DEBUG_INFO://......
	case BINDER_FREEZE://......
	case BINDER_GET_FROZEN_INFO://......
	case BINDER_ENABLE_ONEWAY_SPAM_DETECTION://......
	case BINDER_GET_EXTENDED_ERROR://......
	default:
		ret = -EINVAL;
		goto err;
	}
	// ... ...
	if (thread)
		thread->looper_need_return = false;
	wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
	// ... ...
}

3. Binder通信模型

Binder是基于C/S架构的,简单解释下C/S架构,是指客户端(Client)和服务端(Server)组成的架构,Client端有什么需求,直接发送给Server端去完成。Android系统中对外只暴露Client端,Client端将任务发送给Server端。
在这里插入图片描述
在这里插入图片描述

4、mmap和一次拷贝

binder作为Android系统中最常用的IPC技术,其中比较重要的一个优势在于进程间通信时数据的 一次拷贝
由于系统中进程隔离的原因,用户进程之间不能拷贝数据,所以只能依赖内核空间做为辅助,间接传递数据,如下步骤:

  1. 数据从client端拷贝到 内核
  2. 数据从 内核 拷贝到 server端

在这里插入图片描述

mmap是操作系统中成熟的技术,Android binder使用了内存映射(memory map)技术,只拷贝一次就可以达到用户进程之间传递数据的目的:

  1. client进程通过copy_from_user()将数据从用户空间拷贝到了内核空间。
  2. server进程通过mmap将内核空间的内存地址映射到了用户空间。
  3. 只要client将数据拷贝到内核空间,那么server进程就可以直接读取到内核空间的数据,无需再次拷贝。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xhBruce

佛系随缘,共同探讨

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值