02. Android Binder图解 小米系统专家 解析 ServiceManager和binder通信 (安卓12)

很多BAT也不一定能懂的binder机制!

我同事从小米跳槽过来,干安卓framework层10年,是小米的专家级别

然后他把binder驱动层全部和我讲解了一遍,然后我这边做个笔记分享给大家。

因为搞懂binder需要会c,linux内核知识。看java根本就看不懂!

分4篇文字讲解:

01. Android Binder图解 小米系统专家 解析Service 的addService注册过程 (安卓12)

02. Android Binder图解 小米系统专家 解析 ServiceManager和binder通信 (安卓12)

03. Android Binder图解 小米系统专家 解析binder驱动层解析binder通信过程 (安卓12)

04. Android Binder图解 小米系统专家 从binder java层解析binder整个流程 (安卓12)

0111(03).jpg

问题:

binder驱动是如何和serviceManager进行通信的?数据是如何传递的?

打开驱动,然后进行读取文件

相关源码文件:

/system/core/rootdir/init.rc
/frameworks/native/cmds/servicemanager/service_manager.c
/frameworks/native/cmds/servicemanager/binder.c

ServiceManager 进程是由 init 进程通过解析 init.rc 文件而创建的。

ServiceManager 进程启动的三个阶段:

  • 打开 binder 驱动:binder_open;
  • 调用binder_become_context_manager函数,将servicemanager注册成为Binder机制的上下文管理者。
  • 进入无限循环,binder_loop函数,循环等待和处理client端发来的请求。

最终会通过 binder 驱动跨进程执行** ServiceMananger 的 do_add_service **方法。


servicemanager的入口函数在service_manager.c中: 是c文件哦,不是cpp。
frameworks/native/cmds/servicemanager/service_manager.c

**```
int main(int argc, char **argv) {
struct binder_state bs;
// 打开 binder 驱动,申请 128k 字节大小的内存空间
bs = binder_open(128
1024);
...

// 成为上下文管理者
if (binder_become_context_manager(bs)) {
    return -1;
}
// selinux 权限是否使能
selinux_enabled = is_selinux_enabled(); 
sehandle = selinux_android_service_context_handle();
selinux_status_open(true);

if (selinux_enabled > 0) {
    if (sehandle == NULL) {  
        // 无法获取 sehandle
        abort(); 
    }
    if (getcon(&service_manager_context) != 0) {
        // 无法获取 service_manager 上下文
        abort(); 
    }
}
...

// 进入无限循环,处理 client 端发来的请求 
binder_loop(bs, svcmgr_handler);
return 0;

}

1. 打开 binder 驱动,具体的代码都在binder.c里面

<wiz_code_mirror><pre class=" CodeMirror-line " role="presentation">struct binder_state *binder_open(size_t mapsize)</pre>

<pre class=" CodeMirror-line " role="presentation">{</pre>

<pre class=" CodeMirror-line " role="presentation"> struct binder_state *bs;</pre>

<pre class=" CodeMirror-line " role="presentation"> struct binder_version vers;</pre>

<pre class=" CodeMirror-line " role="presentation"> bs = malloc(sizeof(*bs));</pre>

<pre class=" CodeMirror-line " role="presentation"> if (!bs) {</pre>

<pre class=" CodeMirror-line " role="presentation"> errno = ENOMEM;</pre>

<pre class=" CodeMirror-line " role="presentation"> return NULL;</pre>

<pre class=" CodeMirror-line " role="presentation"> }</pre>

<pre class=" CodeMirror-line " role="presentation"> // 打开 binder 驱动,调用的是驱动层的 binder_open</pre>

<pre class=" CodeMirror-line " role="presentation"> bs->fd = open("/dev/binder", O_RDWR);</pre>

<pre class=" CodeMirror-line " role="presentation"> if (bs->fd < 0) {</pre>

<pre class=" CodeMirror-line " role="presentation"> fprintf(stderr,"binder: cannot open device (%s)\n",</pre>

<pre class=" CodeMirror-line " role="presentation"> strerror(errno));</pre>

<pre class=" CodeMirror-line " role="presentation"> goto fail_open;</pre>

<pre class=" CodeMirror-line " role="presentation"> }</pre>

<pre class=" CodeMirror-line " role="presentation"> // 获取 binder 驱动的版本,调用的是 binder_ioctl 方法获取</pre>

<pre class=" CodeMirror-line " role="presentation"> if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||</pre>

<pre class=" CodeMirror-line " role="presentation"> (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {</pre>

<pre class=" CodeMirror-line " role="presentation"> fprintf(stderr,</pre>

<pre class=" CodeMirror-line " role="presentation"> "binder: kernel driver version (%d) differs from user space version (%d)\n",</pre>

<pre class=" CodeMirror-line " role="presentation"> vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);</pre>

<pre class=" CodeMirror-line " role="presentation"> goto fail_open;</pre>

<pre class=" CodeMirror-line " role="presentation"> }</pre>

<pre class=" CodeMirror-line " role="presentation"> // 调用的是驱动层的 binder_mmap 方法</pre>

<pre class=" CodeMirror-line " role="presentation"> bs->mapsize = mapsize;</pre>

<pre class=" CodeMirror-line " role="presentation"> bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);</pre>

<pre class=" CodeMirror-line " role="presentation"> if (bs->mapped == MAP_FAILED) {</pre>

<pre class=" CodeMirror-line " role="presentation"> fprintf(stderr,"binder: cannot map device (%s)\n",</pre>

<pre class=" CodeMirror-line " role="presentation"> strerror(errno));</pre>

<pre class=" CodeMirror-line " role="presentation"> goto fail_map;</pre>

<pre class=" CodeMirror-line " role="presentation"> }</pre>

<pre class=" CodeMirror-line " role="presentation"> return bs;</pre>

<pre class=" CodeMirror-line " role="presentation">fail_map:</pre>

<pre class=" CodeMirror-line " role="presentation"> close(bs->fd);</pre>

<pre class=" CodeMirror-line " role="presentation">fail_open:</pre>

<pre class=" CodeMirror-line " role="presentation"> free(bs);</pre>

<pre class=" CodeMirror-line " role="presentation"> return NULL;</pre>

<pre class=" CodeMirror-line " role="presentation">}</pre></wiz_code_mirror>

调用mmap函数进行内存映射

2. 成为 binder 驱动管理者

<wiz_code_mirror><pre class=" CodeMirror-line " role="presentation">int binder_become_context_manager(struct binder_state *bs)</pre>

<pre class=" CodeMirror-line " role="presentation">{</pre>

<pre class=" CodeMirror-line " role="presentation"> // 告诉驱动,当前进程成为驱动的上下文管理,也是调用的驱动层的 binder_ioctl 方法</pre>

<pre class=" CodeMirror-line " role="presentation"> return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);</pre>

<pre class=" CodeMirror-line " role="presentation">}</pre></wiz_code_mirror>

[图片上传失败...(image-fca94f-1641890572101)]

3. 循环等待处理 client 请求

<wiz_code_mirror><pre class=" CodeMirror-line " role="presentation">for (;;) {</pre>

<pre class=" CodeMirror-line " role="presentation"> bwr.read_size = sizeof(readbuf);</pre>

<pre class=" CodeMirror-line " role="presentation"> bwr.read_consumed = 0;</pre>

<pre class=" CodeMirror-line " role="presentation"> bwr.read_buffer = (uintptr_t) readbuf;</pre>

<pre class=" CodeMirror-line " role="presentation"> res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);</pre>

<pre class=" CodeMirror-line " role="presentation"> if (res < 0) {</pre>

<pre class=" CodeMirror-line " role="presentation"> ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));</pre>

<pre class=" CodeMirror-line " role="presentation"> break;</pre>

<pre class=" CodeMirror-line " role="presentation"> }</pre>

<pre class=" CodeMirror-line " role="presentation"> res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);</pre>

<pre class=" CodeMirror-line " role="presentation"> if (res == 0) {</pre>

<pre class=" CodeMirror-line " role="presentation"> ALOGE("binder_loop: unexpected reply?!\n");</pre>

<pre class=" CodeMirror-line " role="presentation"> break;</pre>

<pre class=" CodeMirror-line " role="presentation"> }</pre>

<pre class=" CodeMirror-line " role="presentation"> if (res < 0) {</pre>

<pre class=" CodeMirror-line " role="presentation"> ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));</pre>

<pre class=" CodeMirror-line " role="presentation"> break;</pre>

<pre class=" CodeMirror-line " role="presentation"> }</pre>

<pre class=" CodeMirror-line " role="presentation"> }</pre></wiz_code_mirror>

<wiz_code_mirror><pre class=" CodeMirror-line " role="presentation">int do_add_service(struct binder_state *bs,</pre>

<pre class=" CodeMirror-line " role="presentation"> const uint16_t *s, size_t len,</pre>

<pre class=" CodeMirror-line " role="presentation"> uint32_t handle, uid_t uid, int allow_isolated,</pre>

<pre class=" CodeMirror-line " role="presentation"> pid_t spid)</pre>

<pre class=" CodeMirror-line " role="presentation">{</pre>

<pre class=" CodeMirror-line " role="presentation"> struct svcinfo *si;</pre>

<pre class=" CodeMirror-line " role="presentation"> //ALOGI("add_service('%s',%x,%s) uid=%d\n", str8(s, len), handle,</pre>

<pre class=" CodeMirror-line " role="presentation"> // allow_isolated ? "allow_isolated" : "!allow_isolated", uid);</pre>

<pre class=" CodeMirror-line " role="presentation"> if (!handle || (len == 0) || (len > 127))</pre>

<pre class=" CodeMirror-line " role="presentation"> return -1;</pre>

<pre class=" CodeMirror-line " role="presentation"> // 判断当前进程的 pid 是不是有权限</pre>

<pre class=" CodeMirror-line " role="presentation"> if (!svc_can_register(s, len, spid)) {</pre>

<pre class=" CodeMirror-line " role="presentation"> ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n",</pre>

<pre class=" CodeMirror-line " role="presentation"> str8(s, len), handle, uid);</pre>

<pre class=" CodeMirror-line " role="presentation"> return -1;</pre>

<pre class=" CodeMirror-line " role="presentation"> }</pre>

<pre class=" CodeMirror-line " role="presentation"> si = find_svc(s, len);</pre>

<pre class=" CodeMirror-line " role="presentation"> if (si) {</pre>

<pre class=" CodeMirror-line " role="presentation"> if (si->handle) {</pre>

<pre class=" CodeMirror-line " role="presentation"> ALOGE("add_service('%s',%x) uid=%d - ALREADY REGISTERED, OVERRIDE\n",</pre>

<pre class=" CodeMirror-line " role="presentation"> str8(s, len), handle, uid);</pre>

<pre class=" CodeMirror-line " role="presentation"> svcinfo_death(bs, si);</pre>

<pre class=" CodeMirror-line " role="presentation"> }</pre>

<pre class=" CodeMirror-line " role="presentation"> si->handle = handle;</pre>

<pre class=" CodeMirror-line " role="presentation"> } else {</pre>

<pre class=" CodeMirror-line " role="presentation"> si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));</pre>

<pre class=" CodeMirror-line " role="presentation"> if (!si) {</pre>

<pre class=" CodeMirror-line " role="presentation"> ALOGE("add_service('%s',%x) uid=%d - OUT OF MEMORY\n",</pre>

<pre class=" CodeMirror-line " role="presentation"> str8(s, len), handle, uid);</pre>

<pre class=" CodeMirror-line " role="presentation"> return -1;</pre>

<pre class=" CodeMirror-line " role="presentation"> }</pre>

<pre class=" CodeMirror-line " role="presentation"> si->handle = handle;</pre>

<pre class=" CodeMirror-line " role="presentation"> si->len = len;</pre>

<pre class=" CodeMirror-line " role="presentation"> memcpy(si->name, s, (len + 1) * sizeof(uint16_t));</pre>

<pre class=" CodeMirror-line " role="presentation"> si->name[len] = '\0';</pre>

<pre class=" CodeMirror-line " role="presentation"> si->death.func = (void*) svcinfo_death;</pre>

<pre class=" CodeMirror-line " role="presentation"> si->death.ptr = si;</pre>

<pre class=" CodeMirror-line " role="presentation"> si->allow_isolated = allow_isolated;</pre>

<pre class=" CodeMirror-line " role="presentation"> si->next = svclist;</pre>

<pre class=" CodeMirror-line " role="presentation"> svclist = si;</pre>

<pre class=" CodeMirror-line " role="presentation"> }</pre>

<pre class=" CodeMirror-line " role="presentation"> // 观察这个服务所在的进程是否死了</pre>

<pre class=" CodeMirror-line " role="presentation"> binder_acquire(bs, handle);</pre>

<pre class=" CodeMirror-line " role="presentation"> binder_link_to_death(bs, handle, &si->death);</pre>

<pre class=" CodeMirror-line " role="presentation"> return 0;</pre>

<pre class=" CodeMirror-line " role="presentation">}</pre></wiz_code_mirror>

<wiz_code_mirror><pre class=" CodeMirror-line " role="presentation">struct binder_transaction_data {</pre>

<pre class=" CodeMirror-line " role="presentation"> union {</pre>

<pre class=" CodeMirror-line " role="presentation"> __u32 handle; //binder_ref(即 handle )</pre>

<pre class=" CodeMirror-line " role="presentation"> binder_uintptr_t ptr; // Binder_node 的内存地址</pre>

<pre class=" CodeMirror-line " role="presentation"> } target; //RPC目标</pre>

<pre class=" CodeMirror-line " role="presentation"> binder_uintptr_t cookie; // BBinder 指针</pre>

<pre class=" CodeMirror-line " role="presentation"> __u32 code; //RPC代码,代表 Client 与 Server 双方约定的命令码</pre>

<pre class=" CodeMirror-line " role="presentation"> __u32 flags; //标志位,比如 TF_ONE_WAY 代表异步,即不等待 Server 端回复</pre>

<pre class=" CodeMirror-line " role="presentation"> pid_t sender_pid; //发送端进程的 pid</pre>

<pre class=" CodeMirror-line " role="presentation"> uid_t sender_euid; //发送端进程的 uid</pre>

<pre class=" CodeMirror-line " role="presentation"> binder_size_t data_size; //data 数据的总大小</pre>

<pre class=" CodeMirror-line " role="presentation"> binder_size_t offsets_size; //IPC 对象的大小</pre>

<pre class=" CodeMirror-line " role="presentation"> union {</pre>

<pre class=" CodeMirror-line " role="presentation"> struct {</pre>

<pre class=" CodeMirror-line " role="presentation"> binder_uintptr_t buffer; //数据区起始地址</pre>

<pre class=" CodeMirror-line " role="presentation"> binder_uintptr_t offsets; //数据区 IPC 对象偏移量</pre>

<pre class=" CodeMirror-line " role="presentation"> } ptr;</pre>

<pre class=" CodeMirror-line " role="presentation"> __u8 buf[8];</pre>

<pre class=" CodeMirror-line " role="presentation"> } data; //RPC数据</pre>

<pre class=" CodeMirror-line " role="presentation">};</pre></wiz_code_mirror>

bind.c

/dev/binder

[图片上传失败...(image-f85809-1641890572095)]

copy_from_user函数:

里面是for循环!!

servicemanager成功注册成为Binder机制的上下文管理者后,servicemanager就是Binder机制的“总管”了,它需要在系统运行期间处理client端的请求,由于client端的请求不确定何时发送,因此需要通过无限循环来实现,实现这一需求的函数就是binder_loop。

————————————————

servicemanager是系统中的关键服务,关键服务是不会退出的,如果退出了,系统就会重启,当系统重启时就会启动用onrestart关键字修饰的进程,比如zygote、media、surfaceflinger等等。

ServiceManager本身的工作很简单:注册服务、查询服务、列出所有服务,启动一个死循环来解析Binder驱动读写动作,进行事务处理

问题:ServiceManager 是如何管理service信息的?

通过前面的一些信息,我们了解到,ServiceManager用来管理服务信息,那么它是如何进行管理的呢?

在ServiceMnager的进程里有一个全局性的svclist变量,服务信息都存在于这里

ServiceManager存在的意义

为何需要一个这样的东西呢?

原来,Android系统中Service信息都是先add到ServiceManager中,由ServiceManager来集中管理,这样就可以查询当前系统有哪些服务。而且,Android系统中某个服务例如MediaPlayerService的客户端想要和MediaPlayerService通讯的话,必须先向ServiceManager查询MediaPlayerService的信息,然后通过ServiceManager返回的东西再来和MediaPlayerService交互。

毕竟,要是MediaPlayerService身体不好,老是挂掉的话,客户的代码就麻烦了,就不知道后续新生的MediaPlayerService的信息了,所以只能这样:

  1. MediaPlayerService向SM注册

  2. MediaPlayerClient查询当前注册在SM中的MediaPlayerService的信息

  3. 根据这个信息,MediaPlayerClient和MediaPlayerService交互

另外,ServiceManager的handle标示是0,所以只要往handle是0的服务发送消息了,最终都会被传递到ServiceManager中去。


0111.jpg
0111(02).jpg
1641780705473-ti3.png

ServiceManager的整体流程如下:

1.打开设备,mmap一个128K的内存空间,进行用户空间和内核空间的内存绑定

2.注册成为上下文的管理者--守护进程

3.陷入死循环,标注线程的looper状态为enter

4.不停的操作binder读写过程

5.解析binder数据,进行查询服务、注册服务、列出服务的核心操作

由于在进行服务注册的时候,把svcinfo_death()注册到了binder_death的中,当收到BR_DEAD_BINDER时,表明应用层向Binder驱动发送Binder调用时,Binder应用层的另一个端已经死亡,进行死亡通知操作,调用svcinfo_death(),最终调用binder_release()发送BC_RELEASE 到内核进行处理。

0111(03).jpg

————————————————

最后:Binder通信流程如下:

1)首先服务端需要向ServiceManager进行服务注册,ServiceManager有一个全局的service列表svcinfo,用来缓存所有服务的handler和name。

2)客户端与服务端通信,需要拿到服务端的对象,由于进程隔离,客户端拿到的其实是服务端的代理,也可以理解为引用。客户端通过ServiceManager从svcinfo中查找服务,ServiceManager返回服务的代理。

3)拿到服务对象后,我们需要向服务发送请求,实现我们需要的功能。通过 BinderProxy 将我们的请求参数发送给 内核,通过共享内存的方式使用内核方法 copy_from_user() 将我们的参数先拷贝到内核空间,这时我们的客户端进入等待状态。然后 Binder 驱动向服务端的 todo 队列里面插入一条事务,执行完之后把执行结果通过 copy_to_user() 将内核的结果拷贝到用户空间(这里只是执行了拷贝命令,并没有拷贝数据,binder只进行一次拷贝),唤醒等待的客户端并把结果响应回来,这样就完成了一次通讯

参考博客:

https://blog.csdn.net/yiranfeng/article/details/105210069

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

推荐阅读更多精彩内容