该系列文章总纲链接:专题分纲目录 android 系统核心机制 binder


本章关键点总结 & 说明:

android 系统核心机制binder(01)C语言简谈binder_第1张图片

上面的导图描述了Binder框架中主要几个部分,同时本章节主要通过Bctest的案例 对Binder原理进行简单说明。

binder系统概述:Binder系统是一种基于IPC的RPC (远程过程调用) 通信机制

android为什么要引入binder通信机制?

  1. Binder使用Client-Server通信方式
  2. 安全性好,简单高效
  3. 面向对象的设计
  4. 独特的接收缓存管理和线程池管理方式

这里从C层的binder进行分析,因为这是android原生系统给出的案例,因此就从这里开始分析。实际上无论从哪里分析,原理都是一样的,从servicemanager启动,到服务端注册服务,再到客户端获取服务,无非是在这里面添加了框架,以及C++和Java层,让我们感觉很多地方云里来雾里去。但实际上Binder就是一个通信机制。本节 抛开框架,简单说下binder的原理。

1 从C测试程序开始Binder系统的学习

首先从Binder系统的C程序开始说起,文件位置在frameworks\native\cmds\servicemanager

1.1 Android Binder系统整体简要框架

android 系统核心机制binder(01)C语言简谈binder_第2张图片

这里关注了整体流程,下面是servicemanager运行流程:

//service_manager运行流程,service_manager.c :a. binder_openb. binder_become_context_managerc. binder_loop(bs, svcmgr_handler);   c.1 res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);   c.2 binder_parse          // 解析          // 处理  : svcmgr_handler                       SVC_MGR_GET_SERVICE/SVC_MGR_CHECK_SERVICE : 获取服务                       SVC_MGR_ADD_SERVICE : 注册服务                    // 回复      

 下面是 服务端 服务注册流程:

//bctest.c,注册服务的过程:a. binder_openb. binder_call(bs, &msg, &reply, 0, SVC_MGR_ADD_SERVICE)                   // 含有服务的名字                         // 它会含有servicemanager回复的数据                                  // 0表示servicemanager                                    // code: 表示要调用servicemanager中的"addservice函数"

 下面是 客户端获取服务流程:

//获取服务的过程:a. binder_openb. binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE)                   // 含有服务的名字                         // 它会含有servicemanager回复的数据, 表示提供服务的进程                                  // 0表示servicemanager                                    // code: 表示要调用servicemanager中的"getservice函数"

1.2 明确2个概念IPC与RPC

@1 IPC : Inter-Process Communication, 进程间通信

三个关键要素:源(source)、数据(data)、目的(target),如下所示:

android 系统核心机制binder(01)C语言简谈binder_第3张图片

对于binder系统来讲

假设源是服务端A,目的是ServiceManager;A向ServiceManager发送数据,表示注册服务的IPC流程

假设源是客户端B,目的是ServiceManager;B向ServiceManager发送数据,表示查询并获取服务handle的IPC流程

IPC表示进程间通信机制

@2 RPC : Remote Procedure Call, 远程过程调用

关注server端获取的数据中含有的是哪个函数的编号,即传递的什么参数,客户端得到的返回值是什么。

假设客户端B获取服务A成功,得到A的handle,那么使用服务端时就是实现RPC远程过程调用的过程

RPC是基于IPC的远程过程调用,如下所示:

android 系统核心机制binder(01)C语言简谈binder_第4张图片

1.3 关注C框架实现Binder的过程,分析binder.c中关键方法binder_call

binder_call的实现,代码与注解如下:

int binder_call(struct binder_state *bs,                struct binder_io *msg, struct binder_io *reply,                void *target, uint32_t code){    int res;    struct binder_write_read bwr;    struct {        uint32_t cmd;        struct binder_txn txn;    } writebuf;    unsigned readbuf[32];    if (msg->flags & BIO_F_OVERFLOW) {        fprintf(stderr,"binder: txn buffer overflow\n");        goto fail;    }    //根据binder_io、target、code来构造writebuf    writebuf.cmd = BC_TRANSACTION;    writebuf.txn.target = target;    writebuf.txn.code = code;    writebuf.txn.flags = 0;    writebuf.txn.data_size = msg->data - msg->data0;    writebuf.txn.offs_size = ((char*) msg->offs) - ((char*) msg->offs0);    writebuf.txn.data = msg->data0;    writebuf.txn.offs = msg->offs0;    //binder_io转换成binder_write_read        bwr.write_size = sizeof(writebuf);    bwr.write_consumed = 0;    bwr.write_buffer = (unsigned) &writebuf;        hexdump(msg->data0, msg->data - msg->data0);    for (;;) {        bwr.read_size = sizeof(readbuf);        bwr.read_consumed = 0;        bwr.read_buffer = (unsigned) readbuf;        //通过ioctl下发给驱动程序        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);        if (res < 0) {            fprintf(stderr,"binder: ioctl failed (%s)\n", strerror(errno));            goto fail;        }        //解析数据,ioctl写入数据给驱动的同时也从驱动中读取数据,这里将读到的binder_write_read结构转换成binder_io结构的reply发送给对端        res = binder_parse(bs, reply, readbuf, bwr.read_consumed, 0);        if (res == 0) return 0;        if (res < 0) goto fail;    }fail:    memset(reply, 0, sizeof(*reply));    reply->flags |= BIO_F_IOERROR;    return -1;}

binder_call的功能是将binder_io结构体转换成binder_write_read并通过ioctl发送给驱动程序,并调用binder_parse[说明:将binder_write_read结构体转换为binder_io结构体]回复对端。

2 C实现的Binder测试程序(这里仅说明实现思路与框架)

2.1 代码文件目录说明:

├── Android.mk

├── Makefile 

├── README.md

├── binder.c //系统文件

├── binder.h //系统文件

├── include

│   ├── linux

│   │   └── binder.h //系统文件

│   └── private

│       ├── android_filesystem_capability.h //系统文件

│       └── android_filesystem_config.h 系统文件

├── service_manager.c //系统修改文件,删减了selinux相关代码

├── test_client.c //参考bctest.c代码

├── test_server.c //参考bctest.c代码

└── test_server.h //仅定义一些宏

这里关键实现的是test_server.c和test_client.c,即服务端和客户端的代码实现

2.2 源码实现框架

@1 测试程序的客户端和服务端 逻辑流程解读(这里做了简化,忽略了ServiceManager)

android 系统核心机制binder(01)C语言简谈binder_第5张图片

@2 RPC机制的调用流程解读(这里做了简化,忽略了ServiceManager)

这里以test_client中sayhello_to的参数传递为例进行RPC机制调用流程的说明,如下图所示:

android 系统核心机制binder(01)C语言简谈binder_第6张图片

2.3 关键源码说明

@1 这里对关键源码进行说明:test_server.c实现如下:

#include #include #include #include #include#include #include #include "binder.h"#include "test_server.h"void sayhello(void){static int cnt = 0;fprintf(stderr, "say hello : %d\n", ++cnt);}int sayhello_to(char *name){static int cnt = 0;fprintf(stderr, "say hello to %s : %d\n", name, ++cnt);return cnt;}int hello_service_handler(struct binder_state *bs,                   struct binder_transaction_data *txn,                   struct binder_io *msg,                   struct binder_io *reply){    uint16_t *s;char name[512];    size_t len;    uint32_t handle;    uint32_t strict_policy;int i;    strict_policy = bio_get_uint32(msg);    switch(txn->code) {    case HELLO_SVR_CMD_SAYHELLO:sayhello();bio_put_uint32(reply, 0); /* no exception */        return 0;    case HELLO_SVR_CMD_SAYHELLO_TO:s = bio_get_string16(msg, &len);  //"IHelloService"s = bio_get_string16(msg, &len);  // nameif (s == NULL) {return -1;}for (i = 0; i < len; i++)name[i] = s[i];name[i] = '\0';i = sayhello_to(name);bio_put_uint32(reply, 0); /* no exception */bio_put_uint32(reply, i);        break;    default:        fprintf(stderr, "unknown code %d\n", txn->code);        return -1;    }    return 0;}int main(int argc, char **argv){    int fd;    struct binder_state *bs;    uint32_t svcmgr = BINDER_SERVICE_MANAGER;    uint32_t handle;int ret;    bs = binder_open(128*1024);    ...    /* add service, bctest.c实现,这里调用了binder_call*/    ret = svcmgr_publish(bs, svcmgr, "hello", hello_service_handler);    ...    binder_set_maxthreads(bs, 10);    binder_loop(bs, hello_service_handler);    return 0;}

服务端主要提供了sayhello与sayhello_to的功能实现

@2 test_client.c实现如下:

#include #include #include #include #include#include #include #include "binder.h"#include "test_server.h"struct binder_state *g_bs;uint32_t g_hello_handle;uint32_t g_goodbye_handle;void sayhello(void){    unsigned iodata[512/4];    struct binder_io msg, reply;    bio_init(&msg, iodata, sizeof(iodata), 4);    bio_put_uint32(&msg, 0);  // strict mode header    bio_put_string16_x(&msg, "IHelloService");    if (binder_call(g_bs, &msg, &reply, g_hello_handle, HELLO_SVR_CMD_SAYHELLO))        return ;    binder_done(g_bs, &msg, &reply);}int sayhello_to(char *name){    unsigned iodata[512/4];    struct binder_io msg, reply;    int ret;    int exception;    bio_init(&msg, iodata, sizeof(iodata), 4);    bio_put_uint32(&msg, 0);  // strict mode header    bio_put_string16_x(&msg, "IHelloService");    bio_put_string16_x(&msg, name);    if (binder_call(g_bs, &msg, &reply, g_hello_handle, HELLO_SVR_CMD_SAYHELLO_TO))return 0;    exception = bio_get_uint32(&reply);    if (exception)ret = -1;    ret = bio_get_uint32(&reply);    binder_done(g_bs, &msg, &reply);    return ret;}/* ./test_client hello * ./test_client hello  */int main(int argc, char **argv){    int fd;    struct binder_state *bs;    uint32_t svcmgr = BINDER_SERVICE_MANAGER;    uint32_t handle;    int ret;    if (argc < 2){        fprintf(stderr, "Usage:\n");        fprintf(stderr, "%s  \n", argv[0]);        return -1;    }    bs = binder_open(128*1024);    if (!bs) {        fprintf(stderr, "failed to open binder driver\n");        return -1;    }    g_bs = bs;    /* get service ,bctest.c实现*/    handle = svcmgr_lookup(bs, svcmgr, "hello");    if (!handle) {        fprintf(stderr, "failed to get hello service\n");        return -1;    }    g_hello_handle = handle;    fprintf(stderr, "Handle for hello service = %d\n", g_hello_handle);    /* send data to server */    if (!strcmp(argv[1], "hello"))    {if (argc == 2) {    sayhello();} else if (argc == 3) {    ret = sayhello_to(argv[2]);    fprintf(stderr, "get ret of sayhello_to = %d\n", ret);}    }    binder_release(bs, handle);    return 0;}

@3 test_server.h关键实现如下:

#define HELLO_SVR_CMD_SAYHELLO     1#define HELLO_SVR_CMD_SAYHELLO_TO  2

2.4 执行与测试

@1 执行编译后,在android系统上执行流程如下所示:

# Android系统中已经有service_manager, 所以不要再次执行它  ./test_server &  ./test_client hello  ./test_client hello stringbalabala   

@2 嵌入式开发环境中执行make即可,在非android系统上执行流程如下所示:

./service_manager &./test_server &./test_client hello./test_client hello stringbalabala

总结下:改测试程序本身就是一个client,一个server。这是在C的层面来看binder,从C++和java层看就是我们熟悉的BpXXX BnXXX。。。等等。

3 最后简单 谈谈 Binder的历史

Android的这套基于Binder的IPC机制,源自于传奇性但又比较悲催的OpenBinderIPC框架。

OpenBinder是由一家叫Be的公司开发,这家传奇性的法国软件公司制造了传奇性的BeOS,当年苹果公司在操作系统研发上遇到困境时,可以用来拯救苹果的操作系统方案,一个是NeXT,另一个便是BeOS。BeOS在构架上和成熟度上比NeXT更具优势,但或许是有优势的东西就会有更高的姿态,要价更高,于是机会便被留给了NeXT,于是有了今天的MacOSX和iOS。BeOS在构架上设计思路上在当年还是很先进的,就比如延用至今的OpenBinder,如果仔细看BeOS的编程文档,会发现整个系统交互与今天的Android有很大的类似之处。可惜后来BeOS最终没有避免破产的命运,BeOS就被作为软件资产,在2001年被Palm收购。

OpenBinder在Palm也曾风光一时。Palm以简洁低功耗设备迅速成长起来之后,也需要一种高效的,类似于Corba的系统级消息互通机制,以构造更复杂的系统。这样的尝试,得到了一个悲情的操作系统,Palm OS Cobalt(Palm OS 6),本来作为Palm OS 5的后继者,这一操作系统被寄予很大期望,但没有产商愿意生产基于它的设备。但得到的好处是,OpenBinder在这种商业应用前景不明的情况最终还是选择了开源,OpenBinder见OpenBinder链接 ,这个网站是OpenBinder创造者Dianne Kyra Hackborn的个人网站。而OpenBinder所依存的操作系统环境很不稳定,需要考虑兼容性又被迫随着市场需求在多种操作系统内核上移植,历经BeOS、Windows、PalmOS Cobalt的微内核、Linux内核,最终使OpenBinder具备强大的可移植性。

虽然OpenBinder在技术上是一种很优秀的方案,其命运却是一再如此悲催,使用OpenBinder技术的操作系统,都没有走入主流然后就销声匿迹。在今天的操作系统世界里,使用OpenBinder的并不多,仅ALP(ACESS Linux Platform)在使用OpenBinder作为其IPC机制,但ALP所占市场份额实在太小,发展前景很不明朗。但革命性的Android操作系统,最终选择了这套方案,使OpenBinder终于发挥了其强大潜力。Android使用OpenBinder的基本构架,但并非完整的OpenBinder,所有只称其为BinderIPC。在Android系统的设计者眼中,Android跟OpenBinder并无直接联系,会强调Android世界里的Binder是独特设计过的,可能是出于法律上的顾虑。但我们对比OpenBinder和Android里的Binder实现,就会发现这两者在本质上是一样的。相对而言,Android的Binder机制是OpenBinder的一种简化版本,学习Android底层开发,如果Binder本身不容易理解,可以参考OpenBinder的文档。

至于Android为什么会选择Binder作为其底层通信机制而不是重新设计或是借用已有方案,坊间谣传是由于Android底层开发人员大都曾是BeOS或是Palm的开发人员,更熟悉这套开发框架。但如果不是Binder机制足够优秀,可能也会在Android系统的发展中被抛弃。 Binder提供了一种功能强大、简洁、高效、面向对象的跨进程传递方式,而到目前为此,还只是Binder能够提供这样的能力。

更多相关文章

  1. android电池管理系统
  2. Android系统手机通讯录
  3. android系统架构图及各层介绍
  4. 系统安全
  5. Android中资源管理机制详解

随机推荐

  1. 什么是分布式文件系统?为什么要学习分布式
  2. mysql查询指定字段以","拼接字符串作为结
  3. Emmet学习
  4. markdown的常用语法
  5. 第一节课作业
  6. HMS Core . Sparkle金融创新沙龙,邀您参加
  7. 循环双链表(C语言,使用头节点)
  8. 如何在桌面版Chrome浏览器中启用实时字幕
  9. 中国人寿自动化运维自主研发之路
  10. postgresql copy使用