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

2024-06-06 00:04
文章标签 驱动 初始化 binder

本文主要是介绍从0到1学Binder-Binder驱动初始化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 1. binder_alloc_shrinker_init
  • 2. debugfs_create_dir/debugfs_create_file
  • 3. init_binder_device
  • 4. init_binderfs

我的微信公众号“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;// 1ret = binder_alloc_shrinker_init();if (ret)return ret;atomic_set(&binder_transaction_log.cur, ~0U);atomic_set(&binder_transaction_log_failed.cur, ~0U);// 2binder_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);}// 3if (!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;}}// 4ret = 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驱动初始化就先讲到这里,只是说了个大概,后面章节介绍通信实现流程时继续讲解。

这篇关于从0到1学Binder-Binder驱动初始化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1034544

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

c++的初始化列表与const成员

初始化列表与const成员 const成员 使用const修饰的类、结构、联合的成员变量,在类对象创建完成前一定要初始化。 不能在构造函数中初始化const成员,因为执行构造函数时,类对象已经创建完成,只有类对象创建完成才能调用成员函数,构造函数虽然特殊但也是成员函数。 在定义const成员时进行初始化,该语法只有在C11语法标准下才支持。 初始化列表 在构造函数小括号后面,主要用于给

驱动(RK3588S)第七课时:单节点设备树

目录 需求一、设备树的概念1、设备树的后缀名:2、设备树的语法格式3、设备树的属性(重要)4、设备树格式举例 二、设备树所用函数1、如何在内核层种获取设备树节点:2、从设备树上获取 gpio 口的属性3、获取节点上的属性只针对于字符串属性的4、函数读取 np 结点中的 propname 属性的值,并将读取到的 u32 类型的值保存在 out_value 指向的内存中,函数的返回值表示读取到的

驱动安装注册表指令

HKCR: HKEY_CLASSES_ROOT HKCU: HKEY_CURRENT_USER HKLM: HKEY_LOCAL_MACHINE HKU: HEKY_USER HER: 相对根键

UMDF驱动安装

VS2013 + WDF8.1,UMDF驱动选择User Mode Driver,不要选User Mode Driver 2.0,否则Win7安装有问题,如图 另外,在驱动安装时不要忘记WUDFUpdate_<主版本号><次版本号>.dll文件,具体文件名在INF中查找。此文件可在WDF的安装目录中找到。注意:在WDF的安装目录中会有3个WUDFUpdate_xxx.dll文件,x86,x6

电脑驱动分类

电脑驱动程序(驱动程序)是操作系统与硬件设备之间的桥梁,用于使操作系统能够识别并与硬件设备进行通信。以下是常见的驱动分类: 1. 设备驱动程序 显示驱动程序:控制显卡和显示器的显示功能,负责图形渲染和屏幕显示。 示例:NVIDIA、AMD 显示驱动程序。打印机驱动程序:允许操作系统与打印机通信,控制打印任务。 示例:HP、Canon 打印机驱动程序。声卡驱动程序:管理音频输入和输出,与声卡硬件

麒麟系统安装GPU驱动

1.nvidia 1.1显卡驱动 本机显卡型号:nvidia rtx 3090 1.1.1下载驱动 打开 https://www.nvidia.cn/geforce/drivers/ 也可以直接使用下面这个地址下载 https://www.nvidia.com/download/driverResults.aspx/205464/en-us/ 1.1.3安装驱动 右击,

@postconstruct初始化的操作

从Java EE 5规范开始,Servlet中增加了两个影响Servlet生命周期的注解(Annotion);@PostConstruct和@PreDestroy。这两个注解被用来修饰一个非静态的void()方法 。写法有如下两种方式: @PostConstruct Public void someMethod() {}

spring和tomcat初始化的类和注解

1.InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候会执行该方法。 spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中同过init-method指定,两种方式可以同时使用 实