Binder系统-C程序示例_框架分析

news2024/12/26 15:30:54

IPC:进程间的通信,远程调用,比如我们的A进程需要打开LED灯,调用led_open/led_ctl方法,但是他是没有权限去操作的,所以进程A通过:1.首先构造一些数据,2.通过IPC发送数据到进程B,然后B进程:1.取出数去,2.调用本地的led_open/led_ctl。表面看起来,我们是通过进程A直接操控LED。

我们可以分析出框架大致如下:
数据的传输有三大要素,分别为源,目的已经数据。找上述的例子中可以如下描述:
源(进程A):发送数据,A向serviceManager查询led服务,获取一个handle(对硬件操作的服务),该handle指向进程B。
目的(进程B):B向serviceManager注册LED服务,以便A进程获取
数据:点亮,或者熄灭闪烁等。在这里插入图片描述
源码总体分析
相关问价你主要集中在SDK/frameworks\native\cmds\servicemanager目录下,其中存在文件service_manager.c,bctest.c(半成品),binder.c(封装好的C库)。根据前面的框图,我们分别来了解client,servicemanager,service分别做了,或者需要做什么。前面提到需要注册服务,那么肯定最先运行的是servicemanager,其主做了以下工作:

在这里插入图片描述
首先先打开驱动,并且告诉驱动他是servicemanager,这样他就有了驱动的操作权限,并且他是是一个while(1)循环,不停的读取数据,解释数据,如果有需要注册服务,则保存注册的服务到链表中,如果有需要获取服务,则从链表查询服务返回,在没有事情做的时候会处于休眠状态。

下面我们看看service:
在这里插入图片描述
最后我们在来看看client:
在这里插入图片描述
首先也是open打开驱动,然后向servicemanager查询获取服务,最后向handle(service服务)发送数据。

从上面简单的分析可以看出,service在注册服务的时候没事需要向进程servicemanage发送消息的,client也会向servicemanage进程发送消息获取服务,那么他们怎么知道哪个是servicemanage呢?在servicemanage分析中,他会告诉驱动他是servicemanage(上面已经提及过)。下面我们开始追踪andriod源码,也就是上面提到的那几个文件,看到具体是怎么回事

源码追踪
首先我们打开SDK\frameworks\native\cmds\servicemanager\service_manager.c,我们找到最末尾的main函数:

service_manager.c
service_manager.c(从main函数开始分析):

//txn->code参数是一个编码,这个编码包含呢很多信息,比如是查找,还是添加服务等等信息,都包含在这个编码中。
svcmgr_handler(struct binder_state *bs,struct binder_transaction_data *txn,struct binder_io *msg,struct binder_io *reply)
	handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);//根据传入的txn->code,在链表中查找服务,查找到之后返回服务
	do_add_service(bs, s, len, handle, txn->sender_euid,
            allow_isolated, txn->sender_pid) //如果传入的是注册服务,则加入本地链表
            

main()
	binder_open(128*1024); 
		open("/dev/binder", O_RDWR | O_CLOEXEC); //打开驱动binde(前面提到过,进程间的通信都是通过binder)
	binder_become_context_manager(bs); //告诉驱动他是service_manager
	binder_loop(bs, svcmgr_handler); //一个循环,读取处理数据,其中的svcmgr_handler为服务处理函数
		ioctl(bs->fd, BINDER_WRITE_READ, &bwr);  //读取数据
		binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func); //解析数据
			func(bs, txn, &msg, &reply);//处理数据,此处的func就是前面的传入的svcmgr_handler

bctest.c
bctest.c中主要实现了两个功能,注册服务和获取服务,首先我们来看看他的注册服务,我们依旧从main函数开始

main()
	binder_open(128*1024);
		open("/dev/binder", O_RDWR | O_CLOEXEC); //打开驱动binde(前面提到过,进程间的通信都是通过binder)
	svcmgr_publish(bs, svcmgr, argv[1], &token);/*注册服务,其中的svcmgr = BINDER_SERVICE_MANAGER(0),打开驱动binde,进行进程间的通信,
	那么你当然需要告诉binder你是和谁通信,要发给谁,这里的BINDER_SERVICE_MANAGER即为service_manager进程,前面的ervice_manager告诉binder他是service_manager就起到了作用*/
		binder_call(bs, &msg, &reply, target, SVC_MGR_ADD_SERVICE) //构建数据之后,调用该函数,msg:含有服务的名字,reply:service_manager返回的数据 ,target:service_manager进程(0),SVC_MGR_ADD_SERVICE添加服务

以上是注册服务的过程,下面分获取服务过程:

main()
	binder_open(128*1024);
		handle = svcmgr_lookup(bs, svcmgr, "alt_svc_mgr");
			binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE) /*构造数据之后调用该函数,msg:含有服务的名字,reply:service_manager返回的数据(即提供服务的进程) ,
			target:service_manager进程(0),SVC_MGR_CHECK_SERVICE:获取服务*/

分析了注册服务和获取服务的大致过程,我们现在来分析其细致原理,即分析binder_call与之前提到svcmgr_handler函数参数txn中的txn->code,binder_call函数时在binder.c中提供的。

binder.c
我们打开binder.c文件,对binder_call进行分析;

int binder_call(struct binder_state *bs,
                struct binder_io *msg, struct binder_io *reply,
                uint32_t target, uint32_t code)

在这里插入图片描述
binder_call为远程调用,那么我们至少要知道调用那个进程,向那个价进程发送数据,调用那个函数,传递什么参数 ,返回值信息保存在哪里。在上图中已经进行了描述。

那我们这个函数怎么用呢?

1. 构造参数:放在buf中,这buf使用struct binder_io *msg进行描述

2. 调用ioctl发送数据:
		res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);/*其中bwr:struct binder_write_read bwr;
		struct binder_io
		{
		    char *data;            /* pointer to read/write from */
		    binder_size_t *offs;   /* array of offsets */
		    size_t data_avail;     /* bytes available in data buffer */
		    size_t offs_avail;     /* entries available in offsets array */
		
		    char *data0;           /* start of data buffer */
		    binder_size_t *offs0;  /* start of offsets buffer */
		    uint32_t flags;
		    uint32_t unused;
		};*/
			
3. ioctl也会接受数据,收到一个binder_write_read。

不知道大家发现一个问题没有,我们构建的是struct binder_io,但是ioctl需要的是struct binder_write_read bwr,那么他们之间是怎么进行转换的呢?那么他们肯定有一个转换过程。

这些都是大致的一个过程,下面我们开始分析源码:

svcmgr_lookup(struct binder_state *bs, uint32_t target, const char *name)//bctest.c文件
	bio_init(&msg, iodata, sizeof(iodata), 4);
    bio_put_uint32(&msg, 0);  // strict mode header
    bio_put_string16_x(&msg, SVC_MGR_NAME);
    bio_put_string16_x(&msg, name);
	binder_call(struct binder_state *bs,struct binder_io *msg, 	struct binder_io *reply,uint32_t target, uint32_t code)
		------------------------------------------------------
		//对writebu进行构造
		writebuf.cmd = BC_TRANSACTION;
	    writebuf.txn.target.handle = target;
	    writebuf.txn.code = code;
	    writebuf.txn.flags = 0;
	    writebuf.txn.data_size = msg->data - msg->data0;
	    writebuf.txn.offsets_size = ((char*) msg->offs) - ((char*) msg->offs0);
	    writebuf.txn.data.ptr.buffer = (uintptr_t)msg->data0;
	    writebuf.txn.data.ptr.offsets = (uintptr_t)msg->offs0;
	    --------------------------------------------------------
	    --------------------------------------------------------
		struct binder_write_read bwr;
		bwr.write_size = sizeof(writebuf);
	    bwr.write_consumed = 0;
	    bwr.write_buffer = (uintptr_t) &writebuf;
		--------------------------------------------------------

我们发现调用了很多bio_put去构建&msg,放入使用bio_put,那个获取我们可以使用bio_get。构建好msg然后调用binder_call,在该函数中从把msg中的相关成员赋值给writebuf,最后在让bwr.write_buffer指向writebuf,这样就完成了binder_io ms msg到binder_write_read bwr之间的转换,就能通过ioctl驱动binder。
其中write_buffer定义如下:

struct {
        uint32_t cmd;
        struct binder_transaction_data txn;
    } __attribute__((packed)) writebuf;
	//其中txn包括了:handle,code,参数。

小节结语
分析其内部机制之后,我们归纳一下,应该如何去写应用程序,实现进程之间的通信。

client:
	a.binder_open打开驱动,
	b.获取服务handle,
	c.构造参数binder_io,
	d.调用binder_call(handle,code,binder_io),
	e.分析返回的binder_io,取出返回值。
server:
	a.binder_open打开驱动,
	b.注册服务(service)
	c.ioctrl(read)
	d.解析数据binder_write_read,其中存在成员readbuf.binder_transaction_data.txn(前面提到其中包含handle,code,参数)
	e.根据code,决定调用哪个函数,从binder_io取出需要传递的参数,
	f.把返回值转换为binder_io发送给client。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LPC4357JET256/LPC4337FET256/LPC4337JET256 32位MCU 204MHz 1MB

【详情】LPC4300系列微控制器(MCU)拥有全世界首款非对称双核数字信号控制器体系结构,配有ARM Cortex-M4和Cortex-M0处理器。这些NXP Cortex-M4 MCU配有Cortex-M0协处理器,优势在于,可在单一体系结构、开发环境中,开发数字信号处理…

画栋雕梁:定制投资体系2——规划开发能力圈

接上一篇,选择了适合自己“买”的方法,接下来就是要规划买的范围。这个范围一般受个人眼界、认知和理解的限制,也即是价值投资中的一个重要概念——能力圈。每个人的能力圈不可能一样,这和个人过往的学习、工作、成长经历相关。若…

PyQGIS开发 -- 基础学习笔记

1、自主学习QGIS开发虽然QGIS本身功能强大,但还是架不住我们要编写新的功能、新的业务流程、新的算法。前文中我们提到,扩展QGIS有2种方法,一是用Python、C来写QGIS的插件;另一种就是基于QGIS的C API开发独立应用程序。然而后者资…

前端食堂技术周刊第 72 期:Signals 是前端框架的未来、Chrome Headless、ts-reset、magic-regexp、Bun 新文档

美味值:🌟🌟🌟🌟🌟 口味:草莓番茄 食堂技术周刊仓库地址:https://github.com/Geekhyt/weekly 本期摘要 Signals 是前端框架的未来Chrome Headless 进化成完全体Next.js 13.2Deno…

纯x86汇编实现的多线程操作系统实践 - 第二章 BSP的启动

本章主要讲解BSP的初始化过程,对应的代码为mbr.asm。系统加电启动后,BIOS进行必要的初始化,并将硬盘的0扇区512字节的数据搬运到内存地址0x7c00处。之后,BSP的IP被置为0x7c00,开始运行。初始化代码mbr.asm将顺序执行以…

服务网格领域的百花齐放

服务网格是一种技术架构,它用于管理微服务系统中各个服务之间的通信,旨在处理微服务间的流量(也称为东西向流量)。 ​ 在云原生应用中,一个应用的背后可能存在着成百上千个服务,各个服务可能又有着若干个实…

机器学习——集成学习

引言 集成学习:让机器学习效果更好,单个不行,群殴走起。 分类 1. Bagging:训练多个分类器取平均(m代表树的个数)。 2.Boosting(提升算法):从弱学习器开始加,通过加权来进行训练。…

这样在管理后台里实现 403 页面实在是太优雅了

前言403 页面通常表示无权限访问,与 404 页面代表着不同含义。而大部分管理后台框架仅提供了 404 页面的支持,但却忽略了对 403 页面的处理,有的框架虽然也有对 403 页面的处理,但处理效果却不尽人意。那怎么样的 403 页面才是即好…

异步交互的关键——Ajax

文章目录1,Ajax 概述1.1 作用1.2 同步和异步1.3 案例1.3.1 分析1.3.2 后端实现1.3.3 前端实现2,axios2.1 基本使用2.2 快速入门2.2.1 后端实现2.2.2 前端实现2.3 请求方法别名最后说一句1,Ajax 概述 AJAX (Asynchronous JavaScript And XML):异步的 Jav…

【Fastdfs实战】在本地如何将文件上传到Linux虚拟机

作者:狮子也疯狂 专栏:《Fastdfs连续剧》 坚持做好每一步,幸运之神自然会驾凌在你的身上 目录一. 🦁 前言二. 🦁 上传原理Ⅰ. 🐇 原理图解Ⅱ. 🐇 传输原理三. 🦁 实战演示Ⅰ. &…

TryHackMe-CVE-2021-41773/42013(Apache2.4.49/50)

CVE-2021-41773/42013 对 Apache 路径遍历错误的简短解释和不完整的修复 介绍 tryhackme: 在 Apache HTTP Server 2.4.49 中对路径规范化所做的更改中发现一个缺陷。攻击者可以使用路径遍历攻击将 URL 映射到预期文档根目录之外的文件。如果文档根目录之外的文件不受“要求全…

第三阶段-03MyBatis 中使用XML映射文件详解

MyBatis 中使用XML映射文件 什么是XML映射 使用注解的映射SQL的问题: 长SQL需要折行,不方便维护动态SQL查询拼接复杂源代码中的SQL,不方便与DBA协作 MyBatis建议使用XML文件映射SQL才能最大化发挥MySQL的功能 统一管理SQL, 方…

【工程实践】python 去除\xa0、\u3000、\n、\u2800、\t等字符

1.问题描述 在处理数据时,会遇到\xa0、\u3000、\u2800、\t等Unicode字符串。需要对其进行处理。 2.空格类型说明 空格可以分为两类,一类为普通文本空格,另一类为html实体空格。普通文本空格介绍普通半角空格和普通全角空格。html实体空格介绍…

【反序列化漏洞-01】为什么要序列化

为什么要序列化百度百科上关于序列化的定义是,将对象的状态信息转换为可以存储或传输的形式(字符串)的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区(非关系型键值对形式的数据库Redis,与数组类似)。以后,可以通过…

浅谈信安文章搜索引擎

一个搜索引擎的实现流程大概为:首先获取海量的数据,整理成统一的格式,然后交给索引程序建立索引,当索引建立好后,就可以进行搜索。简而言之就是:数据获取->数据检索->数据搜索 0x1数据获取 数据获取…

RK3399+FPGA+MIPI 方案细节之subLVDS to MIPI处理

#CROSSLINK系列 #CROSSLINK vs XO3L 总的来说XO3L的灵活性更强,更近似于一片通用的CPLD;CROSSLINK专用性更强。 针对subLVDS转换到MIPI的需求,CROSSLINK比较有优势,因为集成度更高,所以稳定性也更高。 #要点 #crossl…

死锁的总结

哲学家死锁造成的原因:我有你需要的,但你已经有了 饥饿与死锁的区别 死锁一旦发生一定又饥饿现象,但是饥饿现象产生不一定是死锁 历史上对于死锁的声音 死锁的方案 前面两个都是不允许死锁出现 前面都是概念性的东西 后面我们研究如何破坏…

【vue】vuex数据丢失怎么办?盘一盘vue传值的方法

【问题描述】 vuex的 store 中的数据是保存在运行内存中的,当页面刷新时,页面会重新加载 vue 实例,vuex 里面的数据就会被重新赋值,这样就会出现页面刷新vuex中的数据丢失的问题。 如何解决浏览器刷新数据丢失问题呢? …

Java-Springboot整合支付宝接口

文章目录一、创建支付宝沙箱二、使用内网穿透 nat app三、编写java程序四、访问一、创建支付宝沙箱 跳转 : 支付宝沙箱平台 1、进入控制台 2、创建小程序,编写名称和绑定商家即可 3、返回第一个页面,往下滑进入沙箱 4、进行相关的配置&a…

git push -u参数是什么意思?

背景 git push的时候,有时候会用-u参数,有时候不适用。这是为什么呢? 官方解释 -u--set-upstreamFor every branch that is up to date or successfully pushed, add upstream (tracking) reference, used by argument-less git-pull(1) a…