Binder 驱动和 ServiceManager 通信流程

前言

本文基于 linux 3.18 和 Android 9.0 版本源码,涉及的源码文件路径为:

  • Binder 驱动
    http://androidxref.com/kernel_3.18/xref/drivers/staging/android/binder.c
  • ServiceManager
    • http://androidxref.com/9.0.0_r3/xref/frameworks/native/cmds/servicemanager/service_manager.c
    • http://androidxref.com/9.0.0_r3/xref/frameworks/native/cmds/servicemanager/binder.c

背景

整理 Android Binder IPC 知识点,记录阅读 Binder 源码的理解

是什么

Binder 驱动和 ServiceManager 通信流程_第1张图片
Binder IPC 四大模块
  • Binder 驱动是 Binder IPC 架构中内核层程序,负责处理应用层发送过来的请求,完成多进程间传输数据功能
  • ServiceManager 是应用层程序,本身是一个 Binder Server,负责记录和查找系统中其他 Binder Server,完成 Binder 标识符到 Binder 实体的转换功能
  • 本文主要记录这两者之间的通信流程

时序图

Binder 驱动和 ServiceManager 通信流程_第2张图片
Binder 驱动和 ServiceManager 通信时序图
  • 系统启动时执行 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

你可能感兴趣的:(Binder 驱动和 ServiceManager 通信流程)