文章目录

  • 参考资料
  • 简介
  • 一. IBinder.transact
    • 1.1 BinderProxy.transact
    • 1.2 android_util_Binder#android_os_BinderProxy_transact
    • 1.3 BpBinder#transact
    • 1.4 IPCThreadState#transact
      • 1.4.1 IPCThreadState#writeTransactionData
    • 1.5 IPCThreadState#waitForResponse
    • 1.6 IPCThreadState#talkWithDriver
  • 二. ioctl介绍
    • 2.1 API
    • 2.2 ioctl用户与驱动之间的协议
  • 总结

参考资料

  1. Android Binder详解 https://mr-cao.gitbooks.io/android/content/android-binder.html
  2. msm-4.14 Code https://github.com/android-linux-stable/msm-4.14/blob/9c4b6ed1b229cfc35e5c3e5815e297b7f519cf93/drivers/android/binder.c
  3. linux 内核 - ioctl 函数详解 https://blog.csdn.net/qq_19923217/article/details/82698787
  4. ioctl(2) — Linux manual page https://man7.org/linux/man-pages/man2/ioctl.2.html
  5. ioctl()分析——从用户空间到设备驱动 https://blog.csdn.net/zifehng/article/details/59576539

简介

接上文,首先回顾一下IBinder相关接口的类图:

我们知道在Client App中获取的IBinder实际上是BinderProxy类型的对象。那么在上一文中Client App调用sayHello方法过程的的#2.3.2中,我们卡住了,现在可以继续了:

virtual status_t        transact(   uint32_t code,const Parcel& data,Parcel* reply,uint32_t flags = 0) = 0;

一. IBinder.transact

  1. Service.onServiceConnected
  2. IDemoInterface.Stub.Proxy.sayHello
  3. BinderProxy.transact(Stub.TRANSACTION_sayHello, …)

1.1 BinderProxy.transact

public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {// ......try {// 不多废话,直接开始, 注意我们现在的进程环境是Client App哦return transactNative(code, data, reply, flags);}// ......
}

1.2 android_util_Binder#android_os_BinderProxy_transact

static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{// ......// 获取IBinder对象,这里我们已经知道了,是BinderProxy对应Native的IBinder对象// 也就是对应Server App存入的JavaBBinder, 是Parcel:flattenBinder中存入cookie的BBinder// 然后在通信过程中经过Binder驱动转成了对应的BpBinderIBinder* target = getBPNativeData(env, obj)->mObject.get();// ......// 1.3 上一篇文章我们就分析了,这个target就是BpBinderstatus_t err = target->transact(code, *data, reply, flags);// ......return JNI_FALSE;
}

1.3 BpBinder#transact

// NOLINTNEXTLINE(google-default-arguments)
status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{// 一旦Binder对象死掉,就不会复活if (mAlive) {// ......// 1.4 IPCThreadState!status_t status = IPCThreadState::self()->transact(mHandle, code, data, reply, flags);if (status == DEAD_OBJECT) mAlive = 0;return status;}return DEAD_OBJECT;
}

1.4 IPCThreadState#transact

status_t IPCThreadState::transact(int32_t handle,uint32_t code, const Parcel& data,Parcel* reply, uint32_t flags)
{status_t err;flags |= TF_ACCEPT_FDS;// 1.4.1 将数据写入mOut中存储, cmd 是BC_TRANSACTIONerr = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr);// ......if ((flags & TF_ONE_WAY) == 0) {// TF_ONE_WAY 是代表这个binder调用是one_way的,不需要等待回传// 这个分支表示需要回传数据// ......if (reply) {// 1.5 当传入的reply Parcel不为null时err = waitForResponse(reply);} else {// 当直接传入一个null的Parcel作为reply时// 创建一个假的Parcel接收可能的回写数据Parcel fakeReply;err = waitForResponse(&fakeReply);}// ......} else {// 不需要回传数据err = waitForResponse(nullptr, nullptr);}return err;
}

transaction的flag一共有四种:

name value function
TF_ONE_WAY 0x01 代表oneway的binder调用,不需要回传数据
TF_ROOT_OBJECT 0x04 内容是组件的根对象
TF_STATUS_CODE 0x08 内容是32位的状态代码
TF_ACCEPT_FDS 0x10 允许使用文件描述符答复

首先将需要传递的数据写入out中存储,然后去和binder driver通信。

这里我们先忽略IPCThreadState的初始化过程。

1.4.1 IPCThreadState#writeTransactionData

status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{binder_transaction_data tr;tr.target.ptr = 0; /* Don't pass uninitialized stack data to a remote process */tr.target.handle = handle;tr.code = code;tr.flags = binderFlags;tr.cookie = 0;tr.sender_pid = 0;tr.sender_euid = 0;const status_t err = data.errorCheck();if (err == NO_ERROR) {tr.data_size = data.ipcDataSize();tr.data.ptr.buffer = data.ipcData();tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);tr.data.ptr.offsets = data.ipcObjects();} // ......// cmd为BC_TRANSACTIONmOut.writeInt32(cmd);// 将data保存在mOut中mOut.write(&tr, sizeof(tr));return NO_ERROR;
}

这里折后就是将数据保存在mOut中,这里是怎么保证多线程并发的时的处理呢?稍后我们研究IPC的初始化就知道了。

1.5 IPCThreadState#waitForResponse

从名字也可以猜出来,这里应该就是与binder.c驱动通信。

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{uint32_t cmd;int32_t err;while (1) {// 1.6 显而易见, 与驱动通信if ((err=talkWithDriver()) < NO_ERROR) break;// ......// #1.6中已经通过ioctl与binder驱动通信了,回传的输入也写入了mIn中// 读取的第一个uint32_t的数据代表binder通信类型cmd = (uint32_t)mIn.readInt32();// ......switch (cmd) {case BR_TRANSACTION_COMPLETE:if (!reply && !acquireResult) goto finish;break;case BR_DEAD_REPLY:err = DEAD_OBJECT;goto finish;case BR_FAILED_REPLY:err = FAILED_TRANSACTION;goto finish;case BR_ACQUIRE_RESULT:{ALOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT");const int32_t result = mIn.readInt32();if (!acquireResult) continue;*acquireResult = result ? NO_ERROR : INVALID_OPERATION;}goto finish;// 一般非oneway的通信就是走的这里case BR_REPLY:{binder_transaction_data tr;// 读取从binder驱动回传的数据err = mIn.read(&tr, sizeof(tr));ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");if (err != NO_ERROR) goto finish;if (reply) {if ((tr.flags & TF_STATUS_CODE) == 0) {// 将回传的数据存入reply中reply->ipcSetDataReference(reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),tr.data_size,reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),tr.offsets_size/sizeof(binder_size_t),freeBuffer, this);} else {err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);freeBuffer(nullptr,reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),tr.data_size,reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),tr.offsets_size/sizeof(binder_size_t), this);}} else {freeBuffer(nullptr,reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),tr.data_size,reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),tr.offsets_size/sizeof(binder_size_t), this);continue;}}goto finish;default:err = executeCommand(cmd);if (err != NO_ERROR) goto finish;break;}}finish:if (err != NO_ERROR) {if (acquireResult) *acquireResult = err;if (reply) reply->setError(err);mLastError = err;}return err;
}

1.6 IPCThreadState#talkWithDriver

status_t IPCThreadState::talkWithDriver(bool doReceive)
{if (mProcess->mDriverFD < 0) {return -EBADF;}// binder_write_read是一个保存了传输数据以及回传数据信息的结构体binder_write_read bwr;// 判断读缓冲区是否为空const bool needRead = mIn.dataPosition() >= mIn.dataSize();// 仍在从输入缓冲区中剩余的数据中读取数据,并且调用者已请求读取下一个数据,则不编写任何内容const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;// 将待传入的数据存入bwr中,以便通过binder驱动传输bwr.write_size = outAvail;bwr.write_buffer = (uintptr_t)mOut.data();// 如果需要回传的数据时if (doReceive && needRead) {// 读缓冲区大小设置为可接受的最大大小bwr.read_size = mIn.dataCapacity();// 将bwr中读缓冲区指针指向mIn中的data,后续驱动直接将数据填充到这里bwr.read_buffer = (uintptr_t)mIn.data();} else {bwr.read_size = 0;bwr.read_buffer = 0;}// ......do {// .....// 使用ioctl与binder驱动通信, 将bwr存储的信息传输给binder驱动if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)err = NO_ERROR;elseerr = -errno;// .....if (mProcess->mDriverFD < 0) {err = -EBADF;}IF_LOG_COMMANDS() {alog << "Finished read/write, write size = " << mOut.dataSize() << endl;}} while (err == -EINTR);// ......if (err >= NO_ERROR) {// ......if (bwr.read_consumed > 0) {// 如果存在回传的数据,则标记mIn.setDataSize(bwr.read_consumed);mIn.setDataPosition(0);}// ......return NO_ERROR;}return err;
}

ok, 到这里我们对Binder通信已经有了一个初步的认知,最核心跨进程的通信手段是通过ioctl这个东东。

二. ioctl介绍

本身对Linux内核驱动不太了解,可以参考这篇博文: https://blog.csdn.net/qq_19923217/article/details/82698787

ioctl() 系统调用操作特殊文件的底层设备参数。特别是,字符特殊文件(例如终端)的许多操作特性可以通过ioctl() 请求来控制。

ioctl 是设备驱动程序中设备控制接口函数,一个字符设备驱动通常会实现设备打开、关闭、读、写等功能,在一些需要细分的情境下,如果需要扩展新的功能,通常以增设 ioctl() 命令的方式实现。

参数 描述
fd 文件描述符
cmd 交互协议,设备驱动将根据 cmd 执行对应操作
可变参数 arg,依赖 cmd 指定长度以及类型

2.1 API

  1. 头文件: #include <sys/ioctl.h>, 用于指定ioctl()请求的宏和定义位于文件<sys/ioctl.h>中。
  2. 参数: int ioctl(int fd, unsigned long request, …);
    1. fd 必须是打开文件描述符。
    2. cmd 是依赖于设备的请求代码, 即交互协议,设备驱动将根据 cmd 执行对应操作
    3. argp(…) 是指向内存的非类型指针, 它传统上是char*argp, ioctl()请求在其中编码了参数是in参数还是out参数,参数argp的大小以字节为单位。
  3. 返回值: ioctl() 函数执行成功时返回 0,失败则返回 -1 并设置全局变量 errorno 值
    1. EBADF fd不是有效的文件描述符。
    2. EFAULT 默认argp引用不可访问的内存区域。
    3. EINVAL 请求或argp无效。
    4. ENOTTY fd与字符专用设备不关联。
    5. ENOTTY 指定的请求不适用于文件描述符fd引用的对象类型。

2.2 ioctl用户与驱动之间的协议

参考:https://blog.csdn.net/zifehng/article/details/59576539

总结

通过这个sayHello的过程,我们对binder通信有了基本概念。知道BBinder, BpBinder, IBinder, IInterface等等类的作用。

接下来我们先看看Binder驱动的加载过程,之后在继续分析ioctl接下来的流程:比如Client发送请求后,binder驱动是怎么找到对应Server的。

Binder(3)--sayHello之ioctl.md相关推荐

  1. Android 核心分析 之六 -----IPC框架分析 Binder,Service,Se...

    2019独角兽企业重金招聘Python工程师标准>>> 我首先从宏观的角度观察 Binder,Service,Service Manager,并阐述 各自的概念.从 Linux 的概 ...

  2. 红茶一杯话Binder

    1 什么是Binder? 简单地说,Binder是Android平台上的一种跨进程交互技术.该技术最早并不是由Google公司提出的,它的前身是Be Inc公司开发的OpenBinder,而且在Pal ...

  3. Binder源码分析之Native层(原)

    前面两节中介绍了Binder机制中的 ServiceManager 和 Binder驱动 ,在这一节中,我们来介绍Native中的Binder通讯. 为了更好的理解过程,我们挑选Native中的Med ...

  4. Binder源码分析之ServiceManager(原)

    ServiceManager作为Native层Service的管理员,有着极其重要的作用,主要表现两个方面:         1.对于服务端来说,系统所有的服务提供者都需要向ServiceManage ...

  5. 浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路

    原文地址: http://blog.csdn.net/luoshengyang/article/details/6621566 上一篇文章Android进程间通信(IPC)机制Binder简要介绍和学 ...

  6. Android Binder机制(二) ------- 服务的实现

    服务分析 所谓服务,简单点就是不断的监听客户端的请求,然后处理并向客户端返回处理的结果.要实现这一功能,至少需要以下几点: 循环,我们的服务就是一个大循环,不断的监听客户发来的请求.(线程循环) 通讯 ...

  7. Android源码分析 - Framework层的Binder(客户端篇)

    开篇 本篇以aosp分支android-11.0.0_r25作为基础解析 我们在之前的文章中,从驱动层面分析了Binder是怎样工作的,但Binder驱动只涉及传输部分,待传输对象是怎么产生的呢,这就 ...

  8. Android binder简介

    原文地址https://www.nds.rub.de/media/attachments/files/2011/10/main.pdf 4 Binder 本章介绍什么是Binder以及Binder的能 ...

  9. Binder—获取服务

    Binder-获取服务 获取服务 一.BC_TRANSACTION binder_ioctl 二.BR_TRANSACTION 三.BC_REPLY 四.BR_REPLY 五.Client 收到服务句 ...

最新文章

  1. Linux shell日常使用
  2. 服务器安全股v4.0正式版发布 防火墙效能更强
  3. aho-corasick php,GitHub - coralh/php-akm: Ahocorasick keyword match. Supports php7 and php5
  4. [linux]在使用rsync时需要注意的小细节
  5. 汇编语言--微机CPU的指令系统(五)(字符串操作指令)
  6. Bootstrap4+MySQL前后端综合实训-Day02-AM【Bootstrap4(入门、环境搭建、文字排版、颜色、表格、图片、进度条、折叠、输入框组、模态框)、Font Awesome字体图标】
  7. success 已正常处理 hide_最新微信小程序授权的详细处理思路(一)
  8. linux df命令无反馈,Linux-df命令
  9. 虹软安卓人脸识别初学
  10. 计算机隐藏用户设置,Win10电脑怎么设置隐藏账户?电脑如何设置隐藏账户?
  11. Javascipt超详细版思维导图+基础语法导航
  12. svn服务器安装与配置
  13. CKEditor 5 v19.1.1 发布,新增导出为 PDF 功能
  14. mysql1045错误解读_谈谈MYSQL ERROR 1045 错误的解决办法!
  15. Linux——vi命令详解
  16. war文件放到服务器乱码,maven 打war包tomcat服务器乱码问题
  17. dau、mau、pcu、dnu、wau、acu、uv分别是什么意思?
  18. 嵌入式--串口、RS232、RS485通信
  19. 修改360浏览器模式为极速模式
  20. python-第一章 计算机基础知识

热门文章

  1. 机械臂(一)---机器臂的启动
  2. 20220513-rk3568编译linux4.19的buildroot4-(rootrs)
  3. 英雄联盟服务器维护中启动游戏失败,英雄联盟无法进入游戏,教您英雄联盟进不了游戏怎么办...
  4. 老娘舅餐饮冲刺A股:拟募资8.3亿 杨国民杨峻珲父子为实控人
  5. 中国半导体存储行业未来50年发展路线图
  6. 如何ping通带端口的网址
  7. c语言设计的读书报告怎么写,读书报告的写法
  8. Ionic3学习笔记(十三)HttpClient 实现 HTTP 请求以及踩过的一些坑
  9. 黑群晖Aria2下载BT,磁力PT自用详细设置
  10. docker修练之容器内的脚本