Android Binder进程间通讯原理分析

Binder IPC原理

        Android系统是基于Linux内核开发的。Linux开发提供了丰富的进程间通讯机制,例如管道、信号、消息队列、共享内存、插口(Socket) 。而Binder是一套新的通讯工具。

Binder通信采用了c/s架构,所以我们包含了 Client,Server,ServiceManager以及binder驱动,其中ServiceManager用于管理系统中的各种服务。  

注意:图片来源于网络

        从进程进度来看IPC机制,  每个Android的进程只能运行在质疑进程所拥有的虚拟地址空间。对于一个虚拟地址空间包含了用户空间和内核空间。 (内核空间大小可以通过参数配置调整)对于用户空间不同进程之间彼此之间不能共享的,而内核空间时可以共享的。Client进程向Server进程通信,切切是利用了进程间可共享内核空间来完成底层通信的工作,Client端与Server端进程采用iotcl等方法跟内核空间驱动进行交互。 

如何启动Service Manager

         可以看出无论是注册服务还是获取服务的过程都需要ServiceManager,是整个Binder通信机制的大管家, 是Android进程间通信机制Binder的守护进程,那么它是如何被启动㩐? 

ServiceManager是由init进程通过解析init.rc文件而创建的,对应得可执行程序是 system/bin/servicemanageer , 源文件是 /frameworks/native/cmds/servicemanager/service_manager.c

  int main(int argc, char** argv){struct binder_state *bs;union selinux_callback cb;char *driver;if (argc > 1) {driver = argv[1];} else {driver = "/dev/binder";}bs = binder_open(driver, 128*1024);if (!bs) {#ifdef VENDORSERVICEMANAGERALOGW("failed to open binder driver %s\n", driver);while (true) {sleep(UINT_MAX);}#elseALOGE("failed to open binder driver %s\n", driver);#endifreturn -1;}if (binder_become_context_manager(bs)) {ALOGE("cannot become context manager (%s)\n", strerror(errno));return -1;}cb.func_audit = audit_callback;selinux_set_callback(SELINUX_CB_AUDIT, cb);#ifdef VENDORSERVICEMANAGERcb.func_log = selinux_vendor_log_callback;#elsecb.func_log = selinux_log_callback;#endifselinux_set_callback(SELINUX_CB_LOG, cb);#ifdef VENDORSERVICEMANAGERsehandle = selinux_android_vendor_service_context_handle();#elsesehandle = selinux_android_service_context_handle();#endifselinux_status_open(true);if (sehandle == NULL) {ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");abort();}if (getcon(&service_manager_context) != 0) {ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");abort();}binder_loop(bs, svcmgr_handler);return 0;}

通过源码我们看到 binder_open(128*1024) ,通过mmap实现对驱动128k大小的内存映射,所以我们经常看到binder传输过大数据会导致异常。 

struct binder_state *binder_open(const char* driver, size_t mapsize){struct binder_state *bs;struct binder_version vers;bs = malloc(sizeof(*bs));if (!bs) {errno = ENOMEM;return NULL;}bs->fd = open(driver, O_RDWR | O_CLOEXEC);if (bs->fd < 0) {fprintf(stderr,"binder: cannot open %s (%s)\n",driver, strerror(errno));goto fail_open;}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;}bs->mapsize = 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;}return bs;fail_map:close(bs->fd);fail_open:free(bs);return NULL;}

binder_open函数源码上我们可以看到有几个关键函数,ioctl  和 mmap  。

打开binder驱动后,注册成为binder服务的管家:binder_become_context_manager()  

 int binder_become_context_manager(struct binder_state *bs){struct flat_binder_object obj;memset(&obj, 0, sizeof(obj));obj.flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX;int result = ioctl(bs->fd, BINDER_SET_CONTEXT_MGR_EXT, &obj);// fallback to original methodif (result != 0) {android_errorWriteLog(0x534e4554, "121035042");result = ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);}return result;}

最后进入looper_binder无限循环,处理client端发来的请求。 

如何获取Service Manager

当进程注册服务或者获取服务的过程之前都需要调用defaultServiceManager()方法来获取gDefaultServiceManager对象。 

 sp<IServiceManager> defaultServiceManager(){if (gDefaultServiceManager != nullptr) return gDefaultServiceManager;{AutoMutex _l(gDefaultServiceManagerLock);while (gDefaultServiceManager == nullptr) {gDefaultServiceManager = interface_cast<IServiceManager>(ProcessState::self()->getContextObject(nullptr));if (gDefaultServiceManager == nullptr)sleep(1);}}return gDefaultServiceManager;}

这个过程中做了3个工作,ProcessState::self() 获取了ProcessState对象单利,每个进程只有一个ProcessState对象。 getContextObject()获取率BpBinder对象,handle=0的BpBinder对象。 

interface_cast<IServiceManager>()用于获取BpServiceManager对象 。 

注册服务

注册服务时候由defaultServiceManager()返回BpServcieManager同时创建了ProcessState对象和BpBinder对象 。 实际就是BpServcieManager调用addService() 

 virtual status_t addService(const String16& name, const sp<IBinder>& service,bool allowIsolated, int dumpsysPriority) {Parcel data, reply;data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());data.writeString16(name);data.writeStrongBinder(service);data.writeInt32(allowIsolated ? 1 : 0);data.writeInt32(dumpsysPriority);status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);return err == NO_ERROR ? reply.readExceptionCode() : err;}

Parcel:writeStrongBinder()  函数

大致通讯流程如下 : 

获取服务

请求服务getService()过程,就是向ServiceManager进程查询指定服务,当执行binder_transaction函数会区分请求服务所属进程情况。 

1. 请求服务进程与服务属于不同进程,则为请求服务所在进程创建binder_ref对象,指向服务进程中的binder_node ; 最终通过readStrongBinder()返回BpBinder对象。 

2. 请求服务的进程与服务属于同一进程,则不再创建新对象,只是引用计数加1,并且虚拟改type为BINDER_TYPE_BINDER或BINDER_TYPE_WEAK_BINDER . 。 最终通过readStrongBinder()返回BBinder对象的真实之类。 

virtual sp<IBinder> getService(const String16& name) const{unsigned n;for (n = 0; n < 5; n++){sp<IBinder> svc = checkService(name); if (svc != NULL) return svc;sleep(1);}return NULL;}

通过BpServiceManager来获取服务:检索服务是否存在,当服务存在则返回相应的服务,当服务不存在则休眠1s再继续检索服务如果每次都无法获取服务,循环5次,每次循环休眠1s。

参考: 彻底理解Android Binder通信架构 - Gityuan博客 | 袁辉辉的技术博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/15796.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LwIP系列(5):TCP 3次握手+4次挥手+状态机转换

前言 TCP的3次握手、4次挥手以及TCP状态机&#xff0c;是TCP的核心概念&#xff0c;我们在分析LwIp中TCP相关代码流程&#xff0c;也需要熟悉这些流程&#xff0c;本文就详细介绍这些概念。 TCP 3次握手、应用数据交互、4次挥手完整流程 TCP 为什么是3次握手&#xff0c;而不…

【设计模式】第十九章:访问者模式详解及应用案例

系列文章 【设计模式】七大设计原则 【设计模式】第一章&#xff1a;单例模式 【设计模式】第二章&#xff1a;工厂模式 【设计模式】第三章&#xff1a;建造者模式 【设计模式】第四章&#xff1a;原型模式 【设计模式】第五章&#xff1a;适配器模式 【设计模式】第六章&…

删除有序链表中的重复元素II——牛客24

题目描述 法一&#xff09;直接删除法 class Solution{ public:ListNode* deleteDuplicates(ListNode* head) {if(headNULL) return NULL;ListNode* dummy new ListNode(0);dummy->next head;ListNode* cur dummy;while(cur->next!NULL && cur->next->n…

raid5两块磁盘掉线导致阵列崩溃的服务器数据恢复案例

服务器数据恢复环境&#xff1a; DELL PowerVault系列某型号存储&#xff0c;15块硬盘搭建了一组RAID5磁盘阵列。 服务器故障&检测&#xff1a; 存储设备raid5阵列中一块磁盘由于未知原因离线&#xff0c;管理员对该磁盘阵列进行了同步操作。在同步的过程中又有一块磁盘指示…

【算法与数据结构】232、LeetCode用栈实现队列

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;这道题要求我们用栈模拟队列&#xff08;工作上一定没人这么搞&#xff09;。程序当中&#xff0c;pus…

岛屿数量 (力扣) dfs + bfs JAVA

给你一个由 ‘1’&#xff08;陆地&#xff09;和 ‘0’&#xff08;水&#xff09;组成的的二维网格&#xff0c;请你计算网格中岛屿的数量。 岛屿总是被水包围&#xff0c;并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 此外&#xff0c;你可以假设该网格的…

Spring Boot原理分析(一):项目启动(上)——@SpringBootApplication

文章目录 〇、准备工作一、SpringBootApplication.java源码解析1.源码2.自定义注解3.组合注解4.注解ComponentScan过滤器 5.注解SpringBootConfigurationConfiguration 6.注解EnableAutoConfiguration 本文章是Spring Boot源码解读与原理分析系列博客的第一篇&#xff0c;将会介…

阐述kubernetes部署:基础设施安装

基础设施部署 持久卷的建立 请参考&#xff1a;《持久卷的建立》 elasticsearch部署 一、设置远程扩展字典 不使用自定义字典请忽略此步骤 首先更改ES中IK插件的配置&#xff1a; vi/opt/kubernetes/es/IKAnalyzer.cfg.xml 按您的实际设置的秘钥配置secret_value&#xff1a; …

springboot+echarts +mysql制作数据可视化大屏(四图)

作者水平低&#xff0c;如有错误&#xff0c;恳请指正&#xff01;谢谢&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 项目简单&#xff0c;适合大学生参考 分类专栏还有其它的可视化博客哦&#xff01; 专栏地址&#xff1a;https://blog.csdn.net/qq_559…

操作系统12:I/O系统的功能、模型、接口及 I/O 设备和设备控制器

目录 1、I/O系统的功能、模型和接口 &#xff08;1&#xff09;I/O系统的基本功能 1.1 - 隐藏物理设备的细节 1.2 - 与设备的无关性 1.3 - 提高处理机和I/O设备的利用率 1.4 - 对 I/O 设备进行控制 1.5 - 确保对设备的正确共享 1.6 - 错误处理 &#xff08;2&#xff…

TCP/IP出现的背景及其历史【图解TCP/IP(笔记八)】

文章目录 TCP/IP出现的背景及其历史从军用技术的应用谈起ARPANET的诞生TCP/IP的诞生UNIX系统的普及与互联网的扩张商用互联网服务的启蒙 TCP/IP出现的背景及其历史 从军用技术的应用谈起 20世纪60年代&#xff0c;很多大学和研究机构都开始着力于新的通信技术。其中有一家以美…

DeepSpeed-Chat 打造类ChatGPT全流程 笔记二之监督指令微调

文章目录 系列文章0x0. 前言0x1. &#x1f415; Supervised finetuning (SFT) 教程翻译&#x1f3c3; 如何训练模型&#x1f3c3; 如何对SFT checkpoint进行评测?&#x1f481; 模型和数据☀️来自OPT-1.3B及其SFT变体&#xff08;使用不同微调数据&#xff09;的提示示例☀️…