Binder机制,从Java到C (9. IPC通信过程)
1.一次IPC通信過程的幾個步驟
一次通信过程简单的说有下面5个步骤,第一眼看上去,肯定不知道什么玩意,多看几遍,慢慢看,其实是能理解的。

1. Client将数据封装成Parcel。 (前面已经讲过啦)
2. Client process 发送 BC_TRANSACTION 命令协议到kernel,(Client跟kernel说,我要开始远程通信啦)
kernel找到Service process後,向Client process发送一个BR_TRANSACTION_COMPLETE返回协议,表示通信请求已被接受。(kernel说,准了)
Client处理后,开始等待service的返回结果。(client收到“准了”,然后默默等待,或者也可以干点其他什么事情)
3. kernal同時發送 BR_TRANSACTION 返回协议到Service process,请求service处理。(kernel就去找client要找的人,跟他说,有人在请求你的服务)
4. Service process 处理了 BR_TRANSACTION 返回协议,发送BC_REPLY命令协议到kernel,(server跟kernel说,我做完了,这是结果)
kernel找到client process後,向service发送 BR_TRANSACTION_COMPLETE返回协议,表示返回的通信就结果收到了,(kernel说,结果我收到啦)
service处理后,就开始等待下一个请求。(service做完事情,又默默的等着了)
5 kernel同時发送 BR_REPLY 返回协议到Client process,并将结果返回。(kernel把结果返回给client)
Client process 处理BR_REPLAY返回协议,获得reply数据,结束通信。(client收到结果,大功告成!)
下面就开始一步步详细的分析每一步的流程,会有一点烦躁
2.一次IPC通信過程的代码流程
Service需要注册到ServiceManager,也就是addService的IPC通信过程:
1.将数据封装成Parcel:
看一下MediaPlayerService的注册代码
./frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
void MediaPlayerService::instantiate() {
defaultServiceManager()->addService(String16("media.player"), new MediaPlayerService());
}
这个的defaultServiceManager()返回的其实是Service Manager代理对象BpServiceManager,实际上是调用了BpServiceManager的addService。
那就看一下这个addService():
./frameworks/native/libs/binder/IServiceManager.cpp
class BpServiceManager : public BpInterface<IServiceManager>
{
Public:
…
virtual status_t addService(const String16& name, const sp<IBinder>& service,
bool allowIsolated)
{
Parcel data, reply;
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name); //service的名称
data.writeStrongBinder(service); //将IBinder object 封装成一个flat_binder_object写进parcel,传递給Binder驱动。
data.writeInt32(allowIsolated ? : );
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
return err == NO_ERROR ? reply.readExceptionCode() : err;
}
…
};
Parcel內部有mData和mObjects两个缓冲区
mData:包含整数,字符串或者Binder对象(flat_binder_object)
mObjects:记录了mData中Binder对象的位置。
我们看一下怎么把一个Binder对象写进结构体flat_binder_object的:
writeStrongBinder的实现:
./frameworks/native/libs/binder/Parcel.cpp
status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
return flatten_binder(ProcessState::self(), val, this);
} status_t flatten_binder(const sp<ProcessState>& proc,
const sp<IBinder>& binder, Parcel* out)
{
flat_binder_object obj;//定义一个结构体 obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; //0x7f表示Server线程的优先级不能低于0x7f
if (binder != NULL) {
IBinder *local = binder->localBinder();
if (!local) {
...
} else {//我们要注册MediaPlayerService,传进来的是Binder本地对象
obj.type = BINDER_TYPE_BINDER; //所以把type写成Binder本地对象
obj.binder = local->getWeakRefs(); //weekref
obj.cookie = local; //Binder本地对象local的地址值
}
} else {
...
} return finish_flatten_binder(binder, obj, out); //将flat_binder_object写到Parcel中。
}
2.发送和处理BC_TRANSACTION命令协议
remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
前面说过,这里的remote()是一个BpBinder对象,调用的代码是:
./frameworks/native/libs/binder/BpBinder.cpp
status_t BpBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
// Once a binder has died, it will never come back to life.
if (mAlive) {
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
//mHandle 是这个Binder代理对象的句柄值,现在Binder代理对象是ServiceManager代理对象,so mHandle = 0;
//code :ADD_SERVCIE_TRANSACTION
//data: 要传递给Binder驱动的通信数据
//reply: 输出參數,保存通信結果
//flags:描述是同步还是异步,默认是同步
if (status == DEAD_OBJECT) mAlive = ;
return status;
} return DEAD_OBJECT;
}
下面就要调用transact()了:
.framework/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
status_t err = data.errorCheck();
...
if (err == NO_ERROR) {
...
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL); (1)
}
...
if ((flags & TF_ONE_WAY) == ) { //判断是否是同步通信,如果是,reply不为null。
...
if (reply) {
err = waitForResponse(reply); //发送BC_TRANSACTION命令协议。
} else {
...
}
...
return err;
} status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
int32_t cmd;
int32_t err; while () {
if ((err=talkWithDriver()) < NO_ERROR) break; //循环调用talkWithDriver()和Binder驱动程序交互
...
}
…
Return err;
}
(1) : BC_TRANSACTION命令协议后面跟的数据是使用一个struct binder_transaction_data描述的。这里要把parcel写入到这个struct中。这个struct添加到IPCThreadState的成员变量mOut中,表示有命令需要发送。
下面图是mOut里的一条命令的储存格式:

target.handle 就是代表了servicemanager
code 在后面用来区分到底是要调用那个函数
data_size 不用说,就是数据大小
data.ptr.buffer 是存储的数据,有一般数据和binder数据
offsets_size 偏移数组的大小
data.ptr.offsets binder数据在整个数据中的偏移位置,加上上面偏移数组的大小,就可以知道读出每个binder对象了。
接着看,和Binder驱动开始交互了:
.framework/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::talkWithDriver(bool doReceive) //mIn保存返回协议,mOut保存命令命令
{
...
binder_write_read bwr; //BC_TRANSACTION是通过IO控制命令BINDER_WRITE_READ发送到Binder驱动的,所以上文中的binder_transaction_data
要写入binder_write_read结构体中。 里面的read_buffer/write_buffer对应mIn/mOut
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : ; bwr.write_size = outAvail;
bwr.write_buffer = (long unsigned int)mOut.data(); if (doReceive && needRead) {
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (long unsigned int)mIn.data();
} else {
bwr.read_size = ;
bwr.read_buffer = ;
}
...
if ((bwr.write_size == ) && (bwr.read_size == )) return NO_ERROR; bwr.write_consumed = ;
bwr.read_consumed = ;
status_t err;
do {
...
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= ) //这里会调用kernel binder module的binder_thread_write()
和binder_thread_read()來处理命令和返回結果。
err = NO_ERROR;
else
err = -errno;
...
mOut.remove(, bwr.write_consumed); //將已经处理的命令移除
...
mIn.setDataSize(bwr.read_consumed); //将读取出來的返回协议保存在mIn中,在返回到前面的waitForResponse時,就可以解析mIn的內容了。
mIn.setDataPosition();
…
}
上面的 ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) 。
代码实现是在 .kernel/.../android/binder.c,不再android代码范畴了,就不详细列出了,下面是在kernel里处理的步骤:
------------------------------------------------------------------------------------
1.从参数中读取BC_TRANSACTION命令协议和binder_transaction_data。
2.根据binder_transaction_data中的target.handle找到target Binder实体对象(kernel中记录binder的形式,实体对象是用binder_node记录)。
根据target Binder实体对象找到Server process。
3.处理数据中的binder对象(flat_binder_object),凡是传递的binder对象,都会在kernel留下蛛丝马迹。
如果这个binder对象是在kernel中第一次遇见,那么会为它创建Binder实体对象(binder_node)或者Binder引用对象(binder_ref)。
如果kernel中可以找到这个binder对象,就获取它。
4.将一个BINDER_WORK_TRANSACTION_COMPLETE工作项添加到Client process(media player)队列中处理。
同时将一个BINDER_WORK_TRANSACTION工作项添加到Server process(service manager)队列中处理。
这两个工作项是并行处理的。
5.处理BINDER_WORK_TRANSACTION_COMPLETE工作项,将一个BR_TRANSACTION_COMPLETE返回协议发送到Client process中,把返回协议写到mIn中。
-------------------------------------------------------------------------------------------------------------
现在已经返回到IPCThreadState的talkWithDriver()。又返回到waitForResponse()中,处理BR_TRANSACTION_COMPLETE。
然后开始等待Server process返回结果。
3.发送和处理BR_TRANSACTION返回協議
在前面处理BC_TRANSACTION中,会并行将一个BINDER_WORK_TRANSACTION工作项添加到Server process(service manager)队列中处理。
接着kernel中会处理上面的工作项:
1.kernel根据BINDER_WORK_TRANSACTION工作项,向Server process发送了一个BR_TRANSACTION返回协议。
2.Server process,现在就是servicemanager,被唤醒后,会调用binder_parse()处理返回协议。
3.servicemanager分局参数,判断执行操作SVC_MGR_ADD_SERVICE。调用对应的函數do_add_service(),为要注册的Service创建svcinfo结构提,并添加到全局队列svclist中。以后要查找这个service的时候,就从svclist中找了。
binder_parse()之后的函数在下面的文件中:
./frameworks/base/cmds/servicemanager/binder.c
./frameworks/base/cmds/servicemanager/service_manager.c
4.发送和处理BC_REPLY命令协议
servicemanager进程注册成功后,就会通过IO控制命令BINDER_WRITE_READ将BC_REPLY发送到kernel
/frameworks/base/cmds/servicemanager/binder.c
int binder_write(struct binder_state *bs, void *data, unsigned len)
{
struct binder_write_read bwr;
int res;
bwr.write_size = len;
bwr.write_consumed = ;
bwr.write_buffer = (unsigned) data;
bwr.read_size = ;
bwr.read_consumed = ;
bwr.read_buffer = ;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
if (res < ) {
fprintf(stderr,"binder_write: ioctl failed (%s)\n",
strerror(errno));
}
return res;
}
之后的,都是在Kernel中完成的操作,在kernel中的处理:
1.读取BC_REPLY命令协议。
2.找到target process(现在是Client process)。
3.将一个BINDER_WORK_TRANSACTION_COMPLETE工作项添加到servicemanager process队列中处理。
同时将一个BINDER_WORK_TRANSACTION工作项添加到Client process队列中处理。
这两个工作项是并行处理的。
4.处理BINDER_WORK_TRANSACTION_COMPLETE工作项,将一个BR_TRANSACTION_COMPLETE返回协议发送到servicemanager中。
servicemanager处理完BR_TRANSACTION_COMPLETE后,就又开始等待新的进程间通信请求了。
5.发送和处理BR_REPLY返回协议
在前面处理BC_REPLY中,会并行将一个BINDER_WORK_TRANSACTION工作项添加到Client process队列中处理。
接着kernel中会处理上面的工作項:
1.kernel根据BINDER_WORK_TRANSACTION工作項,向Client process發送了一個BR_REPLY返回协议。
Client process返回到IPCThreadState的talkWithDriver(),再回到waitForResponse中处理BR_REPLY。
waitForResponse()中將返回的結果取出,保存在Parcel對象reply中。之後,就跳出循环,結束該方法。Service的註冊過程就執行完了。
Service註冊之後,就会启动Binder thread pool,等待client的請求。
此時Service不是在注册的時候作为Client请求servicemanager的服务了。而是作为Service处理收到的请求。收到请求后会通过executeCommand()來处理。
Binder机制,从Java到C (9. IPC通信过程)的更多相关文章
- Android Binder机制详解:手写IPC通信
想要掌握一样东西,最好的方式就是阅读理解它的源码.想要掌握Android Binder,最好的方式就是写一个AIDL文件,然后查看其生成的代码.本文的思路也是来自于此. 简介 Binder是Andro ...
- Binder机制,从Java到C (大纲)
转载请标注:张小燕:http://www.cnblogs.com/zhangxinyan/p/3487381.html 前段时间一直在看有关Binder机制的内容,觉得受益匪浅,整理记录于此,大家请随 ...
- Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制?
目录: Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制? Android : 跟我学Binder --- (2) AIDL分析及手动实现 ...
- Android Binder机制简单了解
Binder -- 一种进程间通信(IPC)机制, 基于OpenBinder来实现 毫无疑问, 老罗的文章是不得不看的 Android进程间通信(IPC)机制Binder简要介绍和学习计划 浅谈Ser ...
- 从mediaserver入手快速理解binder机制(最简单理解binder)【转】
本文转载自;https://blog.csdn.net/u010164190/article/details/53015194 Android的binder机制提供一种进程间通信的方法,使一个进程可以 ...
- Android Binder机制(一) Binder的设计和框架
这是关于Android中Binder机制的一系列纯技术贴.花了一个多礼拜的时间,才终于将其整理完毕.行文于此,以做记录:也是将自己所得与大家分享.和以往一样,介绍Binder时,先讲解框架,然后再从设 ...
- Binder机制,从Java到C (1. IPC in Application Remote Service)
转载请标注:张小燕:http://www.cnblogs.com/zhangxinyan 1. Application 中的 service 我们知道Android中Service有三种类型:Loca ...
- Binder机制,从Java到C (2. IPC in System Service :AMS)
1.建立Activity和Service的IPC之前 在上一篇 Binder机制,从Java到C (1. IPC in Application Remote Service) 里面有说到Activi ...
- Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6642463 在前面几篇文章中,我们详细介绍了A ...
随机推荐
- Linux centos 主机名颜色设置 和 别名设置
方便和乐趣写今天.至于为什么主机名颜色设置 和 别名设置放在一起写.这是因为他们的设置是在一个文件中..bashrc. .bashrc放在cd /root 这个文件夹下! 这个文件主要保存个人的一些个 ...
- 修改Oracle XE Listener 占用的1521、8080端口
修改Oracle XE Listener 占用的1521.8080端口今天在帮开发人员Demon安装oracleXE时,有这么一段提示: [sql] Destination Folder: D:\ ...
- handlebar的一些用法——个人使用总结
handlebar的一些用法 概述与介绍 Handlebars 是 JavaScript 一个语义模板库,通过对view和data的分离来快速构建Web模板.它采用"Logic-less t ...
- java maven quartz exampe 实用指南
pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w ...
- 贪心算法(Greedy Algorithm)最小生成树 克鲁斯卡尔算法(Kruskal's algorithm)
克鲁斯卡尔算法(Kruskal's algorithm)它既是古典最低的一个简单的了解生成树算法. 这充分反映了这一点贪心算法的精髓.该方法可以通常的图被表示.图选择这里借用Wikipedia在.非常 ...
- ASP.NET AJAX简明教程
当我们谈论Ajax时,首先想到的就是JavaScript下的Ajax,用来完成网页的交互,局部刷新工作,Microsoft的ASP.NET AJAX框架在Web的开发中承担着类似的角色,并简化了Ja ...
- Javascript 继承 call与prototype
function Parent(hello){ this.hello = hello; this.sayHello = function(){ alert(this.hello); } } Paren ...
- win7或windows server 2008 R2 被远程登录日志记录 系统日志
事件查看器 → Windows 日志 → 安全 (win7 事件查看器 打开方式 :计算机 右键 → 管理 → 计算机管理 → 系统工具 → 事件查看器 windows server 2008 ...
- unity脚本运行顺序具体的解释
unity脚本自带函数执行顺序例如以下:将以下脚本挂在随意物体执行就可以得到 Awake ->OnEable-> Start ->-> FixedUpdate-> Upd ...
- Linux 解决文件删除,但并没有改变磁盘可用性
昨天收到zabbix警报邮件,有一个server的 /home 文件夹的使用达成90%以上.检查,发现MongoDB数据文件到这个文件夹.高.而这个MongoDB的数据如今又都不用了.于是就直接把它的 ...