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

我们知道在Client App中获取的IBinder实际上是BinderProxy类型的对象。那么在上一文中Client App调用sayHello方法过程的的#2.3.2中,我们卡住了,现在可以继续了:
1 2 3 4
| virtual status_t transact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) = 0;
|
一. IBinder.transact
- Service.onServiceConnected
- IDemoInterface.Stub.Proxy.sayHello
- BinderProxy.transact(Stub.TRANSACTION_sayHello, ……)
1.1 BinderProxy.transact
1 2 3 4 5 6 7 8
| public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { try { return transactNative(code, data, reply, flags); } }
|
1.2 android_util_Binder#android_os_BinderProxy_transact
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj, jint code, jobject dataObj, jobject replyObj, jint flags) { IBinder* target = getBPNativeData(env, obj)->mObject.get();
status_t err = target->transact(code, *data, reply, flags);
return JNI_FALSE; }
|
1.3 BpBinder#transact
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| status_t BpBinder::transact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { if (mAlive) {
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| 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;
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr); if ((flags & TF_ONE_WAY) == 0) { if (reply) { err = waitForResponse(reply); } else { 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 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; 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(); } mOut.writeInt32(cmd); mOut.write(&tr, sizeof(tr));
return NO_ERROR; }
|
这里折后就是将数据保存在mOut中,这里是怎么保证多线程并发的时的处理呢?稍后我们研究IPC的初始化就知道了。
1.5 IPCThreadState#waitForResponse
从名字也可以猜出来,这里应该就是与binder.c驱动通信。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) { uint32_t cmd; int32_t err;
while (1) { if ((err=talkWithDriver()) < NO_ERROR) break; 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; case BR_REPLY: { binder_transaction_data tr; 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->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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| status_t IPCThreadState::talkWithDriver(bool doReceive) { if (mProcess->mDriverFD < 0) { return -EBADF; }
binder_write_read bwr;
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
bwr.write_size = outAvail; bwr.write_buffer = (uintptr_t)mOut.data();
if (doReceive && needRead) { bwr.read_size = mIn.dataCapacity(); bwr.read_buffer = (uintptr_t)mIn.data(); } else { bwr.read_size = 0; bwr.read_buffer = 0; }
do { if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) err = NO_ERROR; else err = -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
- 头文件: #include <sys/ioctl.h>, 用于指定ioctl()请求的宏和定义位于文件<sys/ioctl.h>中。
- 参数: int ioctl(int fd, unsigned long request, …);
- fd 必须是打开文件描述符。
- cmd 是依赖于设备的请求代码, 即交互协议,设备驱动将根据 cmd 执行对应操作
- argp(…) 是指向内存的非类型指针, 它传统上是char*argp, ioctl()请求在其中编码了参数是in参数还是out参数,参数argp的大小以字节为单位。
- 返回值: ioctl() 函数执行成功时返回 0,失败则返回 -1 并设置全局变量 errorno 值
- EBADF fd不是有效的文件描述符。
- EFAULT 默认argp引用不可访问的内存区域。
- EINVAL 请求或argp无效。
- ENOTTY fd与字符专用设备不关联。
- ENOTTY 指定的请求不适用于文件描述符fd引用的对象类型。
2.2 ioctl用户与驱动之间的协议
参考:https://blog.csdn.net/zifehng/article/details/59576539
总结
通过这个sayHello的过程,我们对binder通信有了基本概念。知道BBinder, BpBinder, IBinder, IInterface等等类的作用。
接下来我们先看看Binder驱动的加载过程,之后在继续分析ioctl接下来的流程:比如Client发送请求后,binder驱动是怎么找到对应Server的。
参考资料
- Android Binder详解 https://mr-cao.gitbooks.io/android/content/android-binder.html
- msm-4.14 Code https://github.com/android-linux-stable/msm-4.14/blob/9c4b6ed1b229cfc35e5c3e5815e297b7f519cf93/drivers/android/binder.c
- linux 内核 - ioctl 函数详解 https://blog.csdn.net/qq_19923217/article/details/82698787
- ioctl(2) — Linux manual page https://man7.org/linux/man-pages/man2/ioctl.2.html
- ioctl()分析——从用户空间到设备驱动 https://blog.csdn.net/zifehng/article/details/59576539