前言
本文基于 linux 3.18 和 Android 9.0 版本源码,涉及的源码文件路径为:
- Binder 驱动
http://androidxref.com/kernel_3.18/xref/drivers/staging/android/binder.c - ServiceManager
背景
整理 Android Binder IPC 知识点,记录阅读 Binder 源码的理解
是什么
- Binder 驱动是 Binder IPC 架构中内核层程序,负责处理应用层发送过来的请求,完成多进程间传输数据功能
- ServiceManager 是应用层程序,本身是一个 Binder Server,负责记录和查找系统中其他 Binder Server,完成 Binder 标识符到 Binder 实体的转换功能
- 本文主要记录这两者之间的通信流程
时序图
- 系统启动时执行 device_initcall 函数完成驱动初始化
static int __init binder_init(void)
{
// 我运行在内核启动时加载驱动的线程
...
ret = misc_register(&binder_miscdev);
...
}
- Binder 驱动向系统注册名字为 “binder” 的 misc 设备。注册的目的是为了之后系统中可以查找到该驱动,否则打开 Binder 驱动失败
static struct miscdevice binder_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
// 注册的设备名称。接下来通过 open 系统调用可以查找到是我
.name = "binder",
.fops = &binder_fops
};
- 接下来系统执行 ServiceManager 启动的 main 函数
int main(int argc, char** argv)
{
...
if (argc > 1) {
driver = argv[1];
} else {
// 驱动名称。对应 binder driver 注册的名称
driver = "/dev/binder";
}
// 打开 binder driver,并映射虚拟内存 128K
bs = binder_open(driver, 128*1024);
...
}
- ServiceManager 通过 open 函数打开 binder 设备
struct binder_state *binder_open(const char* driver, size_t mapsize)
{
...
//系统调用 open,跳转到内核代码段执行
bs->fd = open(driver, O_RDWR | O_CLOEXEC);
...
}
- ServiceManager 通过 ioctl 函数发送命令 BINDER_VERSION 读取内核 Binder 驱动的版本号。如果应用层的 Binder 版本号和驱动层的 Binder 版本号不一致,则无法启动 ServiceManager 程序
struct binder_state *binder_open(const char* driver, size_t mapsize)
{
...
if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
(vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
fprintf(stderr,
"binder: kernel driver version (%d) differs from user space version (%d)\n",
vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);
goto fail_open;
}
...
}
- ServiceManager 通过 mmap 函数请求映射 128 KB 虚拟内存,目的是让 Binder 驱动和当前进程的虚拟内存映射到共享的物理页面。这样其他进程向内核空间地址写入数据之后,ServiceManger 用户空间也可以读取到相应的数据,完成通信
struct binder_state *binder_open(const char* driver, size_t mapsize)
{
...
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
if (bs->mapped == MAP_FAILED) {
fprintf(stderr,"binder: cannot map device (%s)\n",
strerror(errno));
goto fail_map;
}
...
}
- ServiceManager 通过 ioctl 系统调用发送 BINDER_SET_CTX_MANAGER 命令给 Binder 驱动,目的是在内核地址空间记录 ServiceManger 这个 Binder Server,这样接下来各个 Binder Client 可以从 Binder 驱动拿到 ServiceManager 这个 Binder Server
int binder_become_context_manager(struct binder_state *bs)
{
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
- SM 进入循环之前通知 BD 我要进入循环了
void binder_loop(struct binder_state *bs, binder_handler func)
{
int res;
// binder 调用方和 binder driver 调用参数结构体
struct binder_write_read bwr;
// 读取缓冲区大小 32 * 4 = 128 字节
uint32_t readbuf[32];
// 写入缓冲区大小为 0 字节,说明调用方不需要写入数据
bwr.write_size = 0;
// 写入缓冲区已经消费的字节长度为 0,说明为消费数据
bwr.write_consumed = 0;
// 写入缓冲区为空
bwr.write_buffer = 0;
// 读取缓冲区前 4 个字节写入命令字 BC_ENTER_LOOPER,通知 binder driver 我要进入
// 循环事件处理了
readbuf[0] = BC_ENTER_LOOPER;
// 向 binder driver 写入命令字
binder_write(bs, readbuf, sizeof(uint32_t));
...
}
- 最后 ServiceManager 进入一个事件驱动循环,循环通过 ioctl 系统调用读取 Binder Driver 写入的来自其他 Binder Client 发送的请求
void binder_loop(struct binder_state *bs, binder_handler func)
{
...
for (;;) {
// 读取缓冲区大小 128 字节
bwr.read_size = sizeof(readbuf);
// 可写入偏移量为 0
bwr.read_consumed = 0;
// 缓冲区地址/指针
bwr.read_buffer = (uintptr_t) readbuf;
// 调用 ioctl 系统调用进入内核代码读取内核 binder driver 收到的命令
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
...
// 解析读取到的命令
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
...
}
}
- ServiceManager 读取请求、解析请求、处理请求
- ServiceManager 通过 ioctl 系统调用发送响应给 Binder Driver