Binder(4)--binder驱动的加载.md

参考资料

  1. module_init解析及内核initcall的初始化顺序 https://www.cnblogs.com/chaozhu/p/6410271.html
  2. 各种initcall的执行先后顺序 https://blog.csdn.net/fenzhikeji/article/details/6860143
  3. binder 驱动的操作 https://blog.csdn.net/qq_15893929/article/details/103965668
  4. Android的IPC机制Binder的各个部分 http://tech.it168.com/a2009/0331/2703/000000270388_all.shtml
  5. 字符设备驱动-使用alloc_chrdev_region+cdev注册设备驱动 https://blog.csdn.net/weixin_42314225/article/details/81112217
  6. linux文件系统 - 初始化(一) https://www.cnblogs.com/alantu2018/p/8447303.html
  7. mount过程分析之五(mount_bdev->fill_super) https://blog.csdn.net/ZR_Lang/article/details/40115013
  8. VFS四大对象之一 struct super_block https://www.cnblogs.com/linhaostudy/p/7427027.html
  9. Linux字符设备驱动file_operations https://www.cnblogs.com/chen-farsight/p/6181341.html

简介

接上文,想要了解binder驱动的工作原理,我们从binder驱动加载过程开始:

在android/kernel/msm-4.19/drivers/android/binder.c中,我们可以看到有这么一行:

device_initcall(binder_init);

在Linux内核的启动过程中,一个驱动的注册用module_init调用,即device_initcall,它的initcall 的level为6。

他可以将驱动设备加载进内核中,以供后续使用。

一. Linux内核init call过程

Android开机流程(一)一文中有阐述Android开机过程。

我们知道系统加电自检后,引导程序执行完毕,内核映像被加载到内存并获得控制权之后,会启动kernel_init。

1.1 android/kernel/msm-4.9/init/main.c


static int __ref kernel_init(void *unused)
{
	int ret;
    // 初始化kernel参数
	kernel_init_freeable()
    ......
}

1.2 android/kernel/msm-4.9/init/main.c:kernel_init_freeable


static noinline void __init kernel_init_freeable(void)
{
    // ......
    do_basic_setup();
    // ......
}

static void __init do_basic_setup(void)
{
    // ......
    // 调用各种initcall
	do_initcalls();
}


static void __init do_initcalls(void)
{
	int level;
    // initcall_levels 的大小是9,所以这里level是从0到7的
	for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
		do_initcall_level(level);
}

1.3 android/kernel/msm-4.9/init/main.c:do_initcall_level

/* Keep these in sync with initcalls in include/linux/init.h */
static char *initcall_level_names[] __initdata = {
	"pure",
	"core",
	"postcore",
	"arch",
	"subsys",
	"fs",
	"device",
	"late",
};

static void __init do_initcall_level(int level)
{
	// ......
    // 按顺序执行initcall_levels,其实就是initcall_level_names中的顺序。
	for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
		do_one_initcall(initcall_from_entry(fn));
}

其实Andorid开机过程中的init.rc中各种init进程和这里类似。

二. binder驱动的初始化

在了解Linux内核各种init call之后, 我们注意到binder.c最后几行代码中有这么一行:

device_initcall(binder_init);

而在android/kernel/msm-4.9/include/linux/init.h中可以看到定义:

#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)
// HERE!!!
#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)

所以binder驱动的加载时机是先于Android的init进程的,在Linux内核启动中是以level为6(device)的优先级加载的。

2.1 binder.c:binder_init

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;

	// 初始化binder缓冲区,其实就是一个list_lru结构体:binder_alloc_lru
	ret = binder_alloc_shrinker_init();

    // ......

    // 创建binder相关文件
    // 在sys/kernel/debug目录下,创建binder目录 - debugfs是一种用户内核调试的虚拟文件系统
	binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
    // 创建sys/kernel/debug/binder/proc目录
	if (binder_debugfs_dir_entry_root)
		binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
						 binder_debugfs_dir_entry_root);

    // 创建其余文件
	if (binder_debugfs_dir_entry_root) {
		debugfs_create_file("state",
				    0444,
				    binder_debugfs_dir_entry_root,
				    NULL,
                    // 2.1.1 操作函数的定义
				    &binder_state_fops);
		debugfs_create_file("stats",
				    0444,
				    binder_debugfs_dir_entry_root,
				    NULL,
				    &binder_stats_fops);
		debugfs_create_file("transactions",
				    0444,
				    binder_debugfs_dir_entry_root,
				    NULL,
				    &binder_transactions_fops);
		debugfs_create_file("transaction_log",
				    0444,
				    binder_debugfs_dir_entry_root,
				    &binder_transaction_log,
				    &binder_transaction_log_fops);
		debugfs_create_file("failed_transaction_log",
				    0444,
				    binder_debugfs_dir_entry_root,
				    &binder_transaction_log_failed,
				    &binder_transaction_log_fops);
	}

    // CONFIG_ANDROID_BINDERFS是编译配置,一般都是定义的,所以这个分支不进入
    if (!IS_ENABLED(CONFIG_ANDROID_BINDERFS) &&
	    strcmp(binder_devices_param, "") != 0) {
        // ......
	}

    // 2.2 初始化binder文件系统
	ret = init_binderfs();
	if (ret)
		goto err_init_binder_device_failed;

	return ret;
    // ......
}

这里主要是创建了sys/kernel/debug/binder目录,以及其子目录或文件:

  1. proc:记录调用Binder各个进程的内容
  2. state:记录状态信息,操作函数binder_state_fops
  3. stats:记录统计信息,操作函数binder_stats_fops
  4. transactions:记录transaction相关信息,操作函数binder_transactions_fops
  5. transaction_log:记录transaction日志,操作函数binder_transaction_log_fops
  6. failed_transaction_log:记录失败的transaction日志信息,操作函数binder_transaction_log_fops

这里比较奇怪的是没有找到创建文件(目录)时的操作函数的定义,比如binder_state_fops。

2.1.1 操作函数的定义

以binder_state_fops为例,其实这些函数的定义是通过宏定义统一实现的。

在文件android/kernel/msm-4.19/drivers/android/binder_internal.h中我们可以看到有如下函数的声明:

int binder_stats_show(struct seq_file *m, void *unused);
DEFINE_SHOW_ATTRIBUTE(binder_stats);

int binder_state_show(struct seq_file *m, void *unused);
DEFINE_SHOW_ATTRIBUTE(binder_state);

int binder_transactions_show(struct seq_file *m, void *unused);
DEFINE_SHOW_ATTRIBUTE(binder_transactions);

int binder_transaction_log_show(struct seq_file *m, void *unused);
DEFINE_SHOW_ATTRIBUTE(binder_transaction_log);

我们看binder_state_show这个函数下面的DEFINE_SHOW_ATTRIBUTE这个宏。

2.1.2 DEFINE_SHOW_ATTRIBUTE

这个宏的申明是在文件:android/kernel/msm-4.19/include/linux/seq_file.h#148

#define DEFINE_SHOW_ATTRIBUTE(__name)					\
static int __name ## _open(struct inode *inode, struct file *file)	\
{									\
	return single_open(file, __name ## _show, inode->i_private);	\
}									\
									\
static const struct file_operations __name ## _fops = {			\
	.owner		= THIS_MODULE,					\
	.open		= __name ## _open,				\
	.read		= seq_read,					\
	.llseek		= seq_lseek,					\
	.release	= single_release,				\
}

把这个宏展开就是:

static int __name_open(struct inode *inode, struct file *file)
{
	return single_open(file, __name_show, inode->i_private);
}

static const struct file_operations __name_fops = {
	.owner		= THIS_MODULE,
	.open		= __name_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
}

所以,当声明函数binder_state_show时,也就通过DEFINE_SHOW_ATTRIBUTE(binder_state)申明了:

  1. 函数: binder_state_open
  2. 结构体: binder_state_fops

2.2 init_binderfs 初始化binder文件系统

在文件:android/kernel/msm-4.19/drivers/android/binder_internal.h#init_binderfs

#ifdef CONFIG_ANDROID_BINDERFS
extern int __init init_binderfs(void);
#else
static inline int __init init_binderfs(void)
{
	return 0;
}
#endif

这里的CONFIG_ANDROID_BINDERFS就对应内核编译时的配置:android-base.config#20

CONFIG_ANDROID_BINDERFS=y

y就是定义该宏。

这里是引用了外部文件的函数: init_binderfs, 在该头文件的同目录下的文件binderfs.c可以找到对应函数实现:

int __init init_binderfs(void)
{
	int ret;
	const char *name;
	size_t len;

    // binder_devices_param是定义在binder.c文件中的:
    //  char *binder_devices_param = CONFIG_ANDROID_BINDER_DEVICES;
    // 而CONFIG_ANDROID_BINDER_DEVICES 是编译配置: 
    //  CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder,vndbinder"
	// 所以这里的name就是"binder,hwbinder,vndbinder"
    name = binder_devices_param;

    // 验证默认的binderfs设备名是否合法有效
    // 检验的方式是判断名字中是否含有字符','
	for (len = strcspn(name, ","); len > 0; len = strcspn(name, ",")) {
        // BINDERFS_MAX_NAME = 255
		if (len > BINDERFS_MAX_NAME)
			return -E2BIG;
		name += len;
		if (*name == ',')
			name++;
	}

	// 动态分配binder设备编号
	ret = alloc_chrdev_region(&binderfs_dev, 0, BINDERFS_MAX_MINOR,
				  "binder");
	if (ret)
		return ret;

    // 2.2.1 注册binder文件系统
	ret = register_filesystem(&binder_fs_type);
	if (ret) {
		unregister_chrdev_region(binderfs_dev, BINDERFS_MAX_MINOR);
		return ret;
	}

	return ret;
}

static struct file_system_type binder_fs_type = {
	.name		= "binder",
	.mount		= binderfs_mount,
	.kill_sb	= binderfs_kill_super,
	.fs_flags	= FS_USERNS_MOUNT,
};

初始化binder文件系统,三件事:

  1. 验证binder_devices_param也就是编译配置文件中的:CONFIG_ANDROID_BINDER_DEVICES是否合法
  2. 分配binder设备编号
  3. 注册binder文件系统

在这一步之后我们就可以通过 cat proc/devices 查到binder驱动对应的设备号了:

SS9805:/ # cat proc/devices
Character devices:
......
488 binder
......

比如这次我们机器中binder驱动的设备号为488,而且还可以看到这里是注册成字符设备的。

Linux 中的设备有2种类型:字符设备(无缓冲且只能顺序存取)、块设备(有缓冲且可以随机存取)。

在Linux下,一切皆文件,设备也不例外,为了管理这些设备,系统为它们各自都编了号,而每个设备号又分为主设备号和次设备号。主设备号用来区分不同类型的设备,而次设备号用来区分同一类型内的多个设备(及其设备分区)。

2.2.1 register_filesystem

定义在android/kernel/msm-4.19/fs/filesystems.c中:

int register_filesystem(struct file_system_type * fs)
{
	int res = 0;
	struct file_system_type ** p;

	BUG_ON(strchr(fs->name, '.'));
	if (fs->next)
		return -EBUSY;
	write_lock(&file_systems_lock);
    // 在file_systems链表中找到合适位置,插入
	p = find_filesystem(fs->name, strlen(fs->name));
	// 如果不为空,说明已经注册过了,不能重复注册
    if (*p)
		res = -EBUSY;
	else
		*p = fs;
	write_unlock(&file_systems_lock);
	return res;
}

EXPORT_SYMBOL(register_filesystem);

static struct file_system_type **find_filesystem(const char *name, unsigned len)
{
	struct file_system_type **p;
	for (p = &file_systems; *p; p = &(*p)->next)
		if (strncmp((*p)->name, name, len) == 0 &&
		    !(*p)->name[len])
			break;
	return p;
}

主要是将传入的fs插入file_systems链表的末尾,且不能重复注册。

三.binder设备创建及挂载-debugfs挂载

上面的步骤我们分析了binder驱动的初始化,最后会注册binder文件系统。这一过程都是在kernel初始化的device_initcall中完成的。

当kernel启动到一定程度,将文件系统挂载后,自然binder驱动会运作起来:

static struct file_system_type binder_fs_type = {
	.name		= "binder",
	.mount		= binderfs_mount, // 文件系统挂载后调用函数
	.kill_sb	= binderfs_kill_super,
	.fs_flags	= FS_USERNS_MOUNT,
};

文件系统挂载后,就会调用binderfs_mount函数.

3.1 binderfs.c#binderfs_mount

static struct dentry *binderfs_mount(struct file_system_type *fs_type,
				     int flags, const char *dev_name,
				     void *data)
{
    // 留意这个binderfs_fill_super
	return mount_nodev(fs_type, flags, data, binderfs_fill_super);
}

3.2 android/kernel/msm-4.19/fs/super.c#mount_nodev

struct dentry *mount_nodev(struct file_system_type *fs_type,
	int flags, void *data,
	int (*fill_super)(struct super_block *, void *, int))
{
	int error;
    // 获取超级块
    // 超级块代表了整个文件系统,超级块是文件系统的控制块,有整个文件系统信息
    // 一个文件系统所有的inode都要连接到超级块上,可以说,一个超级块就代表了一个文件系统。
	struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL);

	if (IS_ERR(s))
		return ERR_CAST(s);

    // 调用传入的函数fill_super
	error = fill_super(s, data, flags & SB_SILENT ? 1 : 0);
	if (error) {
		deactivate_locked_super(s);
		return ERR_PTR(error);
	}
	s->s_flags |= SB_ACTIVE;
	return dget(s->s_root);
}
EXPORT_SYMBOL(mount_nodev);

这些设计文件系统挂载知识,后续我们在研究一下。

调用mount_nodev后,获取超级块如果一切正常,会调用传入的fill_super函数,这里对应的就是binderfs_fill_super!

3.3 binderfs.c#binderfs_fill_super

static int binderfs_fill_super(struct super_block *sb, void *data, int silent)
{
	int ret;
	struct binderfs_info *info;
	struct inode *inode = NULL;
	struct binderfs_device device_info = { { 0 } };
	const char *name;
	size_t len;

    // 设置文件系统中数据块大小,以字节单位为一页(一般为4096)
	sb->s_blocksize = PAGE_SIZE;
	sb->s_blocksize_bits = PAGE_SHIFT;

    // ......

    // kmalloc用于内核空间申请内存,申请的内存位于物理内存映射区域,而且在物理上也是连续的
    // 与真实的物理地址只有一个固定的偏移,因为存在较简单的转换关系,所以对申请的内存大小有限制,不能超过128KB。
    // GFP_KERNEL —— 正常分配内存;
	sb->s_fs_info = kzalloc(sizeof(struct binderfs_info), GFP_KERNEL);

	// ......

    // inode 保存的其实是实际的数据的一些信息,这些信息称为“元数据”(也就是对文件属性的描述)。
    // 例如:文件大小,设备标识符,用户标识符,用户组标识符,文件模式,扩展属性,文件读取或
    // 修改的时间戳,链接数量,指向存储该内容的磁盘区块的指针,文件分类等等。
	inode = new_inode(sb);
	
    // ......

    // 此函数在@sb引用的binderfs中创建一个新的binderfs控制设备节点。
	ret = binderfs_binder_ctl_create(sb);
	if (ret)
		return ret;

	name = binder_devices_param;

    // 遍历并创建所有binder设备,三种:"binder,hwbinder,vndbinder"
    // 1. binder
    // 2. hwbinder
    // 3. vndbinder
	for (len = strcspn(name, ","); len > 0; len = strcspn(name, ",")) {
		strscpy(device_info.name, name, len + 1);
        // 3.4 创建对应binder设备
		ret = binderfs_binder_device_create(inode, NULL, &device_info);
		if (ret)
			return ret;
		name += len;
		if (*name == ',')
			name++;
	}

	if (info->mount_opts.stats_mode == STATS_GLOBAL)
		return init_binder_logs(sb);

	return 0;
}

3.4 binderfs.c#binderfs_binder_device_create

static int binderfs_binder_device_create(struct inode *ref_inode,
					 struct binderfs_device __user *userp,
					 struct binderfs_device *req)
{
	int minor, ret;
	struct dentry *dentry, *root;
	struct binder_device *device;
	char *name = NULL;
	size_t name_len;
	struct inode *inode = NULL;
	struct super_block *sb = ref_inode->i_sb;
	struct binderfs_info *info = sb->s_fs_info;
    // ......

    // 给binder_device分配内核内存空间
	device = kzalloc(sizeof(*device), GFP_KERNEL);
	if (!device)
		goto err;
    // 创建一个新的inode节点
	inode = new_inode(sb);
    // ......

    // 重点, 这里将加载文件操作集-binder操作
	inode->i_fop = &binder_fops;
	inode->i_uid = info->root_uid;
	inode->i_gid = info->root_gid;

	req->name[BINDERFS_MAX_NAME] = '\0'; /* NUL-terminate */
	name_len = strlen(req->name);
	
    // 分配一块长度为name_len + 1的内存(kmalloc+gfp)
    // 然后将req->name到(req->name + len)内存的内容copy到新分配的内存中
    // 最后后返回该内存的首地址
	name = kmemdup(req->name, name_len + 1, GFP_KERNEL);
	if (!name)
		goto err;

    // 初始化binder_device的相关设置
	refcount_set(&device->ref, 1);
	device->binderfs_inode = inode;
	device->context.binder_context_mgr_uid = INVALID_UID;
	device->context.name = name;
	device->miscdev.name = name;// 分别是binder, hwbinder, vndbinder
	device->miscdev.minor = minor;
	mutex_init(&device->context.context_mgr_node_lock);

    // ......
    // 将创建的binder_device保存在inode中
	inode->i_private = device;
	d_instantiate(dentry, inode);
	fsnotify_create(root->d_inode, dentry);
	inode_unlock(d_inode(root));

	return 0;
    // ......
}

创建binder_device的步骤分为:

  1. 给binder_device分配内核内存空间
  2. 根据超级块创建一个新的inode节点
  3. 加载文件操作集-binder_fops 至inode中
  4. 初始化binder_device的相关设置
  5. 将创建的binder_device保存在inode中

最重要的是关注步骤3,这里我们还要看看binder_fops的文件操作集具体指向的函数:

// android/kernel/msm-4.19/drivers/android/binder.c
const struct file_operations binder_fops = {
	.owner = THIS_MODULE,
	.poll = binder_poll,
	.unlocked_ioctl = binder_ioctl,
	.compat_ioctl = binder_ioctl,
	.mmap = binder_mmap,
	.open = binder_open,
	.flush = binder_flush,
	.release = binder_release,
};

哦偶, 终于看到ioctl了,这里先解释下文件操作集的含义及作用:

Linux使用file_operations结构访问驱动程序的函数,这个结构的每一个成员的名字都对应着一个调用。
用户进程利用在对设备文件进行诸如read/write操作的时候,系统调用通过设备文件的主设备号找到相应的设备
驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数,这是Linux的设备驱动程序工作的基本原理。

其实驱动程序可以理解为一个被动服务,当有上层调用时,才会进入,内核调用的具体实现后续分析。

比如我们在应用进程中调用ioctl,通过syscall进入内核态,但此时进程上下文还是该应用进程的上下文,对应进程状态为S状态。

所以我们在上一篇文章中 ##1.6 IPCThreadState#talkWithDriver 里面调用ioctl最后是调用了binder_ioctl这个函数!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值