简介
接上文,想要了解binder驱动的工作原理,我们从binder驱动加载过程开始:
在android/kernel/msm-4.19/drivers/android/binder.c中,我们可以看到有这么一行:
1
| 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
1 2 3 4 5 6 7 8
| static int __ref kernel_init(void *unused) { int ret; kernel_init_freeable() ...... }
|
1.2 android/kernel/msm-4.9/init/main.c:kernel_init_freeable
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| static noinline void __init kernel_init_freeable(void) { do_basic_setup(); }
static void __init do_basic_setup(void) { do_initcalls(); }
static void __init do_initcalls(void) { int level; 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| static char *initcall_level_names[] __initdata = { "pure", "core", "postcore", "arch", "subsys", "fs", "device", "late", };
static void __init do_initcall_level(int level) { 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最后几行代码中有这么一行:
1
| device_initcall(binder_init);
|
而在android/kernel/msm-4.9/include/linux/init.h中可以看到定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #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)
|
所以binder驱动的加载时机是先于Android的init进程的,在Linux内核启动中是以level为6(device)的优先级加载的。
2.1 binder.c:binder_init
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| 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;
ret = binder_alloc_shrinker_init();
binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL); 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, &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); }
if (!IS_ENABLED(CONFIG_ANDROID_BINDERFS) && strcmp(binder_devices_param, "") != 0) { }
ret = init_binderfs(); if (ret) goto err_init_binder_device_failed;
return ret; }
|
这里主要是创建了sys/kernel/debug/binder目录,以及其子目录或文件:
- proc:记录调用Binder各个进程的内容
- state:记录状态信息,操作函数binder_state_fops
- stats:记录统计信息,操作函数binder_stats_fops
- transactions:记录transaction相关信息,操作函数binder_transactions_fops
- transaction_log:记录transaction日志,操作函数binder_transaction_log_fops
- 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中我们可以看到有如下函数的声明:
1 2 3 4 5 6 7 8 9 10 11
| 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
1 2 3 4 5 6 7 8 9 10 11 12 13
| #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, \ }
|
把这个宏展开就是:
1 2 3 4 5 6 7 8 9 10 11 12
| 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)申明了:
- 函数: binder_state_open
- 结构体: binder_state_fops
2.2 init_binderfs 初始化binder文件系统
在文件:android/kernel/msm-4.19/drivers/android/binder_internal.h#init_binderfs
1 2 3 4 5 6 7 8
| #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
1
| CONFIG_ANDROID_BINDERFS=y
|
y就是定义该宏。
这里是引用了外部文件的函数: init_binderfs, 在该头文件的同目录下的文件binderfs.c可以找到对应函数实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| int __init init_binderfs(void) { int ret; const char *name; size_t len;
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++; }
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; }
static struct file_system_type binder_fs_type = { .name = "binder", .mount = binderfs_mount, .kill_sb = binderfs_kill_super, .fs_flags = FS_USERNS_MOUNT, };
|
初始化binder文件系统,三件事:
- 验证binder_devices_param也就是编译配置文件中的:CONFIG_ANDROID_BINDER_DEVICES是否合法
- 分配binder设备编号
- 注册binder文件系统
在这一步之后我们就可以通过 cat proc/devices 查到binder驱动对应的设备号了:
1 2 3 4 5
| 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中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| 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); 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驱动会运作起来:
1 2 3 4 5 6
| 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
1 2 3 4 5 6 7
| static struct dentry *binderfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return mount_nodev(fs_type, flags, data, binderfs_fill_super); }
|
3.2 android/kernel/msm-4.19/fs/super.c#mount_nodev
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| struct dentry *mount_nodev(struct file_system_type *fs_type, int flags, void *data, int (*fill_super)(struct super_block *, void *, int)) { int error; struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL);
if (IS_ERR(s)) return ERR_CAST(s);
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| 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;
sb->s_blocksize = PAGE_SIZE; sb->s_blocksize_bits = PAGE_SHIFT;
sb->s_fs_info = kzalloc(sizeof(struct binderfs_info), GFP_KERNEL);
inode = new_inode(sb);
ret = binderfs_binder_ctl_create(sb); if (ret) return ret;
name = binder_devices_param;
for (len = strcspn(name, ","); len > 0; len = strcspn(name, ",")) { strscpy(device_info.name, name, len + 1); 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| 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;
device = kzalloc(sizeof(*device), GFP_KERNEL); if (!device) goto err; inode = new_inode(sb);
inode->i_fop = &binder_fops; inode->i_uid = info->root_uid; inode->i_gid = info->root_gid;
req->name[BINDERFS_MAX_NAME] = '\0'; name_len = strlen(req->name); name = kmemdup(req->name, name_len + 1, GFP_KERNEL); if (!name) goto err;
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; device->miscdev.minor = minor; mutex_init(&device->context.context_mgr_node_lock);
inode->i_private = device; d_instantiate(dentry, inode); fsnotify_create(root->d_inode, dentry); inode_unlock(d_inode(root));
return 0; }
|
创建binder_device的步骤分为:
- 给binder_device分配内核内存空间
- 根据超级块创建一个新的inode节点
- 加载文件操作集-binder_fops 至inode中
- 初始化binder_device的相关设置
- 将创建的binder_device保存在inode中
最重要的是关注步骤3,这里我们还要看看binder_fops的文件操作集具体指向的函数:
1 2 3 4 5 6 7 8 9 10 11
| 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这个函数!
参考资料
- module_init解析及内核initcall的初始化顺序 https://www.cnblogs.com/chaozhu/p/6410271.html
- 各种initcall的执行先后顺序 https://blog.csdn.net/fenzhikeji/article/details/6860143
- binder 驱动的操作 https://blog.csdn.net/qq_15893929/article/details/103965668
- Android的IPC机制Binder的各个部分 http://tech.it168.com/a2009/0331/2703/000000270388_all.shtml
- 字符设备驱动-使用alloc_chrdev_region+cdev注册设备驱动 https://blog.csdn.net/weixin_42314225/article/details/81112217
- linux文件系统 - 初始化(一) https://www.cnblogs.com/alantu2018/p/8447303.html
- mount过程分析之五(mount_bdev->fill_super) https://blog.csdn.net/ZR_Lang/article/details/40115013
- VFS四大对象之一 struct super_block https://www.cnblogs.com/linhaostudy/p/7427027.html
- Linux字符设备驱动file_operations https://www.cnblogs.com/chen-farsight/p/6181341.html