文章目录
我的微信公众号“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驱动初始化就先讲到这里,只是说了个大概,后面章节介绍通信实现流程时继续讲解。