从0到1学Binder-Binder驱动初始化


我的微信公众号“ZZH的Android”,还有更多 Android 系统源码解析的干货文章等着你,欢迎关注加入交流群。

binder驱动启动入口如下

// android-kernel/common/drivers/android/binder.c
device_initcall(binder_init);

device_initcall是Binder驱动的入口,其最终调用的是__define_initcall(fn, 6),这里的fn就是传入的函数binder_init。

这里稍微拓展一下驱动的启动内容,不同的驱动启动顺序是不同的,系统会按照优先级依次启动。
不同的调用入口调用代表了不同的启动顺序,其定义如下:

// android-kernel/common/include/linux/init.h
/*
 * A "pure" initcall has no dependencies on anything else, and purely
 * initializes variables that couldn't be statically initialized.
 *
 * This only exists for built-in code, not for modules.
 * Keep main.c:initcall_level_names[] in sync.
 */
#define pure_initcall(fn)		__define_initcall(fn, 0)

#define core_initcall(fn)		__define_initcall(fn, 1)
#define core_initcall_sync(fn)		__define_initcall(fn, 1s)
#define postcore_initcall(fn)		__define_initcall(fn, 2)
#define postcore_initcall_sync(fn)	__define_initcall(fn, 2s)
#define arch_initcall(fn)		__define_initcall(fn, 3)
#define arch_initcall_sync(fn)		__define_initcall(fn, 3s)
#define subsys_initcall(fn)		__define_initcall(fn, 4)
#define subsys_initcall_sync(fn)	__define_initcall(fn, 4s)
#define fs_initcall(fn)			__define_initcall(fn, 5)
#define fs_initcall_sync(fn)		__define_initcall(fn, 5s)
#define rootfs_initcall(fn)		__define_initcall(fn, rootfs)
#define device_initcall(fn)		__define_initcall(fn, 6)
#define device_initcall_sync(fn)	__define_initcall(fn, 6s)
#define late_initcall(fn)		__define_initcall(fn, 7)
#define late_initcall_sync(fn)		__define_initcall(fn, 7s)

上面定义了0-7一共8个等级,数值越小越先启动,binder驱动使用的是device_initcall(fn) ,属于启动比较晚的了。

下面开始分析binder_init函数。

// android-kernel/common/drivers/android/binder.c
static int __init binder_init(void)
{
	int ret;
	char *device_name, *device_tmp;
	struct binder_device *device;
	struct hlist_node *tmp;
	char *device_names = NULL;

    // 1
	ret = binder_alloc_shrinker_init();
	if (ret)
		return ret;

	atomic_set(&binder_transaction_log.cur, ~0U);
	atomic_set(&binder_transaction_log_failed.cur, ~0U);

    // 2
	binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
	if (binder_debugfs_dir_entry_root) {
		const struct binder_debugfs_entry *db_entry;

		binder_for_each_debugfs_entry(db_entry)
			debugfs_create_file(db_entry->name,
					    db_entry->mode,
					    binder_debugfs_dir_entry_root,
					    db_entry->data,
					    db_entry->fops);

		binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
						 binder_debugfs_dir_entry_root);
	}

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

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

	return ret;

err_init_binder_device_failed:
	hlist_for_each_entry_safe(device, tmp, &binder_devices, hlist) {
		misc_deregister(&device->miscdev);
		hlist_del(&device->hlist);
		kfree(device);
	}

	kfree(device_names);

err_alloc_device_names_failed:
	debugfs_remove_recursive(binder_debugfs_dir_entry_root);
	binder_alloc_shrinker_exit();

	return ret;
}

1. binder_alloc_shrinker_init

该函数的作用是在 Android 系统中的 Binder 驱动程序中初始化一个 shrinker 结构,用于内存管理和内存回收。下面是对其作用的详细解释:

内存管理:Binder 是 Android 系统中用于进程间通信(IPC)的机制,它涉及到内核空间和用户空间之间的数据传输。在这个过程中,会涉及到内存的分配和释放。binder_alloc_shrinker_init 函数的主要作用是帮助管理 Binder 驱动程序中的内存分配和释放。

shrinker 结构:shrinker 结构是 Linux 内核中用于内存回收的一种机制。当系统内存不足时,内核会调用 shrinker 结构中的回调函数来释放一些不必要的内存,以便为系统提供更多可用内存。binder_alloc_shrinker_init 函数初始化了一个 shrinker 结构,使其能够在需要时被内核调用。

内存回收:通过初始化 shrinker 结构,binder_alloc_shrinker_init 函数为 Binder 驱动程序提供了一种内存回收的机制。当系统内存不足时,内核可以调用 Binder 驱动程序中的 shrinker 结构来释放一些不必要的内存,从而帮助系统更好地管理内存资源,提高系统的稳定性和性能。

总结成一句话就是,向Linux的内存管理模块shrinker注册回调函数,当内存不足时能够收到系统的回调并进行内存的释放和回收处理。

2. debugfs_create_dir/debugfs_create_file

下面这段代码在 debugfs 文件系统中创建了一个名为 “binder” 的目录,并在该目录下创建了一些调试文件和一个名为 “proc” 的子目录。这些文件和目录用于导出 Binder 驱动的调试信息,方便内核开发人员进行调试和分析。这些调试信息可以包括但不限于事务日志、性能统计和状态信息。

// 创建binder目录
binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
if (binder_debugfs_dir_entry_root) {


    const struct binder_debugfs_entry *db_entry;

    // 在binder目录下创建各种文件
    binder_for_each_debugfs_entry(db_entry)
        debugfs_create_file(db_entry->name,
                    db_entry->mode,
                    binder_debugfs_dir_entry_root,
                    db_entry->data,
                    db_entry->fops);

    // 在binder目录下创建proc目录
    binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
                     binder_debugfs_dir_entry_root);
}

可以关注一下binder_for_each_debugfs_entry的实现

// 其实就是个for循环
#define binder_for_each_debugfs_entry(entry)	\
	for ((entry) = binder_debugfs_entries;	\
	     (entry)->name;			\
	     (entry)++)       

下面就是在binder目录下创建的文件信息:

const struct binder_debugfs_entry binder_debugfs_entries[] = {
	{
		.name = "state",
		.mode = 0444,
		.fops = &state_fops,
		.data = NULL,
	},
	{
		.name = "stats",
		.mode = 0444,
		.fops = &stats_fops,
		.data = NULL,
	},
	{
		.name = "transactions",
		.mode = 0444,
		.fops = &transactions_fops,
		.data = NULL,
	},
	{
		.name = "transaction_log",
		.mode = 0444,
		.fops = &transaction_log_fops,
		.data = &binder_transaction_log,
	},
	{
		.name = "failed_transaction_log",
		.mode = 0444,
		.fops = &transaction_log_fops,
		.data = &binder_transaction_log_failed,
	},
	{} /* terminator */
};

调试信息的作用

**调试和开发:**开发人员可以通过这些调试文件查看和分析 Binder 驱动的运行状态,找出潜在的问题或优化点。

**性能监控:**通过这些文件,可以监控 Binder 驱动的性能指标,确保其在高负载下的稳定性和效率。

**故障诊断:**在系统发生故障时,这些调试信息可以帮助快速定位和解决问题。

但是我们在设备里面却发现,/sys/kernel/debug/目录是空的。原因是debugfs文件系统没有被挂载,执行如下命令进行挂载即可。

mount -t debugfs none /sys/kernel/debug

然后就能看到上面创建的文件信息了

vsoc_x86_64:/sys/kernel/debug/binder # ls -lh
total 0
-r--r--r-- 1 root root 0 2024-06-05 18:29 failed_transaction_log
drwxr-xr-x 2 root root 0 2024-06-05 18:39 proc
-r--r--r-- 1 root root 0 2024-06-05 18:29 state
-r--r--r-- 1 root root 0 2024-06-05 18:29 stats
-r--r--r-- 1 root root 0 2024-06-05 18:29 transaction_log
-r--r--r-- 1 root root 0 2024-06-05 18:29 transactions

3. init_binder_device

从代码里可以看到,如果没有配置CONFIG_ANDROID_BINDERFS,并且binder_devices_param不为空的话,就会执行init_binder_device来注册binder设备。

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

binder_devices_param的定义

// android-kernel/common/drivers/android/binder.c
char *binder_devices_param = CONFIG_ANDROID_BINDER_DEVICES;
// android-kernel/common/kernel/configs/android-base.config
CONFIG_ANDROID_BINDER_DEVICES=binder,hwbinder,vndbinder

这里看到会初始化三个binder驱动设备dev/binder,dev/hwbinder,dev/vnbinder,那么这三者有何不同呢?

binder是system分区的进程间通过binder通信的binder设备;

hwbinder是system分区进程和vendor分区进程间通过binder通信的binder设备;

vnbinder是vendor分区的进程间通过binder通信的binder设备;

核心函数是init_binder_device

static int __init init_binder_device(const char *name)
{
	int ret;
	struct binder_device *binder_device;

	binder_device = kzalloc(sizeof(*binder_device), GFP_KERNEL);
	if (!binder_device)
		return -ENOMEM;

    // binder_fops为binder设备操作函数
	binder_device->miscdev.fops = &binder_fops;
	binder_device->miscdev.minor = MISC_DYNAMIC_MINOR;
	binder_device->miscdev.name = name;

	refcount_set(&binder_device->ref, 1);
	binder_device->context.binder_context_mgr_uid = INVALID_UID;
	binder_device->context.name = name;
	mutex_init(&binder_device->context.context_mgr_node_lock);
    
    // 注册为一个杂项设备
	ret = misc_register(&binder_device->miscdev);
	if (ret < 0) {
		kfree(binder_device);
		return ret;
	}

	hlist_add_head(&binder_device->hlist, &binder_devices);

	return ret;
}

4. init_binderfs

之前我们看到在执行init_binder_device时有一个宏判断CONFIG_ANDROID_BINDERFS。

CONFIG_ANDROID_BINDERFS 是一个内核配置选项,用于启用 BinderFS 文件系统,这是 Android 的 Binder IPC 机制的一部分。BinderFS 提供了一种新的方法来管理和使用 Binder 设备文件。

作用与背景

Binder机制:
Android 的 Binder 是一个用于进程间通信(IPC)的机制,允许不同应用程序和系统服务之间进行高效、安全的数据交换。

传统的 Binder 设备管理:
传统上,Binder 设备文件(如 /dev/binder)在设备节点上管理。每个 Binder 设备文件都对应一个具体的设备,设备文件通常由 init 脚本或类似机制创建。

BinderFS 的引入:
BinderFS 是一个文件系统,用于管理和简化 Binder 设备文件的创建和使用。它为 Binder 设备文件提供了一个文件系统抽象,使得这些设备文件更容易管理和隔离。
BinderFS 通过挂载一个特殊的文件系统来动态创建 Binder 设备文件,而不是依赖静态的设备节点配置。

优点

隔离性:
BinderFS 提供了一种隔离不同 Binder 设备文件的方法。通过将不同的 Binder 设备文件挂载在不同的 BinderFS 文件系统上,可以实现更好的隔离性和安全性。

动态管理:
BinderFS 允许在运行时动态创建和删除 Binder 设备文件,而不需要修改 init 脚本或其他静态配置文件。

简化配置:
通过文件系统抽象,BinderFS 简化了 Binder 设备文件的管理,不需要预先定义所有设备文件。

如下代码可以看到,如果没有启用CONFIG_ANDROID_BINDERFS,则init_binderfs就是个空实现。

// android-kernel/common/drivers/android/binder_internal.h
#ifdef CONFIG_ANDROID_BINDERFS
extern int __init init_binderfs(void);
#else
static inline int __init init_binderfs(void)
{
	return 0;
}
#endif

如果启用了,则代码实现如下

// android-kernel/common/drivers/android/binderfs.c
int __init init_binderfs(void)
{
	int ret;
	const char *name;
	size_t len;

	/* Verify that the default binderfs device names are valid. */
	name = binder_devices_param;
	for (len = strcspn(name, ","); len > 0; len = strcspn(name, ",")) {
		if (len > BINDERFS_MAX_NAME)
			return -E2BIG;
		name += len;
		if (*name == ',')
			name++;
	}

	/* Allocate new major number for binderfs. */
	ret = alloc_chrdev_region(&binderfs_dev, 0, BINDERFS_MAX_MINOR,
				  "binder");
	if (ret)
		return ret;

	ret = register_filesystem(&binder_fs_type);
	if (ret) {
		unregister_chrdev_region(binderfs_dev, BINDERFS_MAX_MINOR);
		return ret;
	}

	return ret;
}

binder驱动初始化就先讲到这里,只是说了个大概,后面章节介绍通信实现流程时继续讲解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ZZH的Android

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值