Binder 驱动和 ServiceManager 通信流程

前言

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

背景

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

是什么

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

时序图

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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,519评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,842评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,544评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,742评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,646评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,027评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,169评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,324评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,268评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,299评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,996评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,591评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,911评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,288评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,871评论 2 341

推荐阅读更多精彩内容