binder通信(Android之Binder通信篇)
本文目录
- Android之Binder通信篇
- Android里用 C 语言编写的应用程序怎么通过 binder 节点通信
- Binder机制的原理
- Android-zygote进程通信为什么不使用Binder
- binder通信过程中,服务端和客户端异常处理
Android之Binder通信篇
Binder跨进程通信的本质是依赖内核驱动将属于不同Binder进程的数据,从原始进程复制到目标进程,这样就完成了跨进程通信了。
Binder通过独特的内存映射机制,在跨进程通信时,可以做到一次拷贝,两个空间同时使用!如下图:
Binder跨进程通信是要传递数据的,既然有数据必然要占用内存空间,Android系统规定每一个进程都有一块Binder内存区,也就是图1中的 共享内存 ,系统最多只能给该区域分配4M的物理内存,由于申请这块内存是通过系统的mmap函数完成的,所以整个映射机制又被称为mmap机制 为了把这部分说明白,就再盗图一张,命名图2吧!
对于Binder驱动,通过 binder_procs 链表记录所有创建的 binder_proc 结构体,binder 驱动层的每一个 binder_proc 结构体都与用户空间的一个用于 binder 通信的进程一一对应,且每个进程有且只有一个 ProcessState 对象,这是通过单例模式来保证的。在每个进程中可以有很多个线程,每个线程对应一个 IPCThreadState 对象,IPCThreadState 对象也是单例模式,即一个线程对应一个 IPCThreadState 对象,在 Binder 驱动层也有与之相对应的结构,那就是 Binder_thread 结构体。在 binder_proc 结构体中通过成员变量 rb_root threads,来记录当前进程内所有的 binder_thread。
Binder 线程池:每个 Server 进程在启动时创建一个 binder 线程池,并向其中注册一个 Binder 线程;之后 Server 进程也可以向 binder 线程池注册新的线程,或者 Binder 驱动在探测到没有空闲 binder 线程时主动向 Server 进程注册新的的 binder 线程。对于一个 Server 进程有一个最大 Binder 线程数限制,默认为16个 binder 线程,例如 Android 的 system_server 进程就存在16个线程。对于所有 Client 端进程的 binder 请求都是交由 Server 端进程的 binder 线程来处理的。
Android里用 C 语言编写的应用程序怎么通过 binder 节点通信
我不懂你的意思,什么叫C程序?你要直接call binder driver么?如果只是想在native layer里通过servicemanager 注册一个service,然后用client 去call,我过去用过这个github的project去测android binder的readwrite performance: mcr/Android-HelloWorldService · GitHub 然后你要想办法把service run 起来,我当时很hack,直接在zygote里改了代码,强行让helloworld在系统init的时候生成,但应该有命令行给你用的,你可以在看看。 而去直接去和binder driver做交互也没问题,只要做几个ioctl call,然后起两个process,一个注册一个节点,然后另一个去写message,但我没具体实现过,你玩玩应该就出来了,我感觉过程可能就像这个shmget的example差不多 IPC:Shared Memory。 此外,Binder这东西没什么独特的,它就是把最基本的message passing:一次传输要向kernel copy paste两次(一次sender 到kernel,一次kernel到receiver,每次都有context switch)和shared memory(kernel 和 userland share 一块 内存,不用context switch)合并起来了,就是receiver和kernel共享一块内存,而sender和kernel的交互必须要严格遵守message passing的原则,于是就取了一个折中,两次copy paste就变成了一次。 此外,service manager会在自己被生成的时候现将自己注册成binder里一个最特殊的service,其他程序想要进行ipc,就必须通过binder向service manager注册,在binder生成一个unique id,然后其他client向service manager查询时候就会得到那个id,于是就能通过binder与service process建立通讯。 我过去很喜欢玩这个,还很蛋疼的在minix上把binder原理实现了一遍,还有一个大神 老罗,他研究Android native非常透彻,你可以看他博客,能够对整个Android从init到Dalvik跑起来全部了解,我现在只知道他一个,因为他把Android source code全都读了一遍:老罗的Android之旅
Binder机制的原理
因为Binder机制是涉及到进程的通信,所以需要对操作系统的进程通信需要有所了解。 进程的相关知识: Linux进程的学习的笔记 Binder机制相比于其他的进程通信方法更加高效,是因为使用了内存映射的机制,数据只需要复制一次。 内存映射的具体内容: 操作系统——内存映射 在介绍Binder跨进程通信之前,需要去了解一个动态内核可加载模块。 根据进程空间划分,进程之间的通信需要依赖到内核空间,例如在传统的IPC机制中,管道,Socket都时内核的一部分,因此通过内核支持来实现进程间通信时没有问题的,但是Binder并不是内核的一部分,那么怎么办呢 ?这就得益于Linux的动态内核可载模块的机制。模块时具有独立功能的程序,它可以被单独编译,但是不能独立运行,它在运行时被链接到内核作为内核的一部分运行。这样,Android系统就可以通过动态添加一个模块运行在内核空间,用户进程之间通过这个内核模块做为桥梁来实现通信。在Android系统中,这个运行在内核空间,负责各个用户进程通过Binder实现通信的内核模块就叫Binder驱动。 有了上面的所说的运行在内核空间的模块,在Android系统中是通过 内存映射 的方式来实现通信,数据的拷贝只需要一次,相比于传统的IPC机制需要两次的数据拷贝,是更加高效的。 Binder IPC是基于内存映射来实现的,但是mmap()通常是用在有物理介质的文件系统上的。比如进程中的用户区域是不能直接核物理设备打交道的,如果想要把磁盘上的数据读取到进程的用户区域,需要两次拷贝(磁盘 》》 内核空间 》》 用户空间)。通常在这种场景下mmap()就能发挥作用,通过在物理介质核用户空间之间建立映射,减少数据的拷贝次数,用内存读写代替I/O读写,提高文件读取效率。而Binder并不存在物理介质,因此Binder驱动使mmap()并不是为了在物理介质和用户空间之间建立映射,而是用来在内核空间创建数据接收的缓存空间。 Binder IPC通信过程: 示意图: Binder是一套基于C/S架构的。由一系列的组件组成,主要包括:Client,Server,Service Manager和Binder驱动。其中Client,Service和Service Manager是在用户空间的,Binder驱动是在内核空间的。Client和Service是由用户是用户实现的,Binder驱动和Service Manager是系统实现的。Client,Server和Service Manager都可以通过系统调用open,mmap和ioctl来访问设备文件/dev/binder。从而实现与Binder驱动的交互间接实现进程间的通信。 其中 Android Bander设计与实现 - 设计篇 对上述的角色中有详细的讲解。 步骤1:使用 BINDERSETCONTEXT_MGR 命令通过Binder驱动将自己注册成为ServiceMannager。 步骤2:注册服务 步骤2:获取服务 关于Binder的原理学习,可参考 Android Binder 原理解析 和 Android跨进程通信:图文详解 Binder机制 原理 ,个人认为这两篇描述的比较详细。
Android-zygote进程通信为什么不使用Binder
首先,zygote进程在创建之后,才会创建SystemServer进程,而SystemServer进程是由zygote进程fock自身得到的,在fock自身的过程中,首先会结束自身的其他子线程,这样一来除了自身线程以外,其他线程都会被结束然后GC,而Binder是多线程模型,如果使用Binder进行进程间通信的话,则Binder线程也会被结束,而使用Binder进行进程间通信就无法做到。在fork新进程后,启动Zygote的4个Daemon线程,java堆整理,引用队列,以及析构线程。而在zygote通过fock自身创建子进程之后,如果该进程不是zygote进程,则会调用ZygoteInit.zygoteInit方法 而在zygoteInit方法中,才会对新创建的进程进行运行环境初始化工作以及启动Binder线程池。 其实Binder线程池的启动,是在SystemServer进程创建过程启动的,而在启动SystemServer的过程中,就需要传入ZygoteServer这个Zygote作为Server端的Socket,所以Zygote进程并不能使用Binder进程进行通信,而只能使用Socket。 而且,Binder线程池是在zygote进程启动之后启动的SystemServer进程中启动的,而SystemServer进程是由zygote进程fock自身得到的,所以zygote进程在启动之后,循环等待SystemServer进程的消息的时候,其实还没有Binder线程池。而且fock只支持当前线程的fock,而不支持多线程的fock,但是Binder又是一个多线程模型,在fock的时候会杀死多余的线程,这样一来,binder线程也就会被杀死,这样就没办法使用binder与SystemServer进程进行进程间通信。 fork 的行为是这样的:复制整个用户空间的数据(通常使用 copy-on-write 的策略,所以可以实现的速度很快)以及所有系统对象,然后仅复制当前线程到子进程。这里:所有父进程中别的线程,到了子进程中都是突然蒸发掉的
binder通信过程中,服务端和客户端异常处理
起因是在使用第三方app的时候,鼠标进入第三方app后,退出进入其他app卡顿的问题。通过分析log发现有大量的System.err: android.os.DeadObjectException产生。 这个异常的产生是因为app和鼠标服务组成的cs模型中,app client突然挂掉导致,服务端产生DeadObjectException。 这里涉及到binder的死亡监听机制对服务端和客户端的处理,客户端需要调用linkToDeath去获取服务端binder的生存状态,如果binder突然挂掉,客户端就要讲binder连接remove掉。同样,如果客户端突然挂掉,服务端也需要去及时的进行处理,否则就会出现DeadObjectException。网上大多介绍了如何去对服务端的binder进行死亡监听,但是没有简洁的服务端对客户端的死亡监听(这里相当于对服务端的listener进行处理)。 首先新建一个继承 implements IBinder.DeathRecipient的listener类,然后在服务端的registerAIDLListener函数内新建一个类,然后添加linkToDeath。 实际上,测试的时候,先进入app,然后开始kill掉app进程,采用 的方法,并没有成功kill掉进程,采用 的方法会让app不在自动起,因为项目中launch会自动起该app,disable后,就无法被唤起,需要再度唤起需要 同时,如果关联进程比较多,binder可能并不会挂掉,可以尝试多杀几个关联进程,来确保binder连接挂掉 第二部分 关于对服务器binder进行的监听,网上版本比较多,同时上诉方法中,没有考虑client列表的情况,如果有多个client的情况下,需要进行listener的添加和移除,可以考虑参看如下blog
更多文章:
replace函数怎么还原(python中replace函数是不是无法替换原函数)
2024年7月23日 12:33
interesting前面加a还是an(interesting,的定冠词是a还是an)
2023年5月3日 04:20
vmware虚拟机linux怎么联网(想用虚拟机的linux连接我家wifi,虚拟机连无线的地方在哪)
2024年6月23日 21:30
济南手机网站建设(专门做手机网站推广这块的,网站建设,优化推广一体化的,济南哪家网络优化公司好)
2024年6月26日 04:00
成员变量是什么意思(在声明final成员变量没有赋值时连编译都不过,那么下图的红色线表达的空白final是啥意思)
2024年7月15日 10:56
6元一年的云服务器(新睿云的云服务器包年和实时计费哪个划算)
2024年6月23日 05:39
js怎么用我的世界(《我的世界》手机版修改器JS使用方法讲解)
2024年7月22日 07:40
css图片居中自适应(css样式怎样让图片在不同分辨率的显示器都居中)
2024年7月28日 04:35
periodic是什么意思(periodic table什么意思)
2024年7月16日 19:43
bootstrap4 网站后台模板(如何利用bootstrap框架开发后台)
2024年5月8日 16:28
jscript script文件(javascript和jscript的文件后缀都是js吗)
2024年7月14日 08:06