经过 Binder 上一篇文章的分析, 咱们知道了 Binder 驱动在咱们应用开发过程当中的使用方式, 了解到了 BBinder 和 BpBinder 两个很是重要的 Native 对象, 本次咱们就着重分析一下 Binder 在运行时库层知识
由于运行时库是使用 C/C++ 编写的, 对于 Android 开发者来讲, 可能有些晦涩难懂, 笔者也下了很大的功夫, 对此感兴趣的同窗能够耐心往下读, 若是存在让你们兴奋的点, 那么笔者就十分知足了node
Android 系统将各类 Binder 驱动程序操做封装成一个 Binder 库, 进程就可使用 Binder 库, 方便地调用 HAL 驱动提供的服务, 屏蔽了底层的细节, 更有利于开发者进行使用linux
在 Binder 库中, Service 组件与 Client 组件分别使用模板类 BnInterface 和 BpInterface 来描述android
template<typename INTERFACE>
class BnInterface : public INTERFACE, public BBinder
{
public:
virtual sp<IInterface> queryLocalInterface(const String16& _descriptor);
virtual cosnt String16& getInterfaceDescriptor() const;
protected:
virtual IBinder* onAsBinder();
}
复制代码
接下来分析一下 BBinder 的实现bash
class BBinder : public IBinder
{
public:
......
virtual status_t transact(
unit32_t code,
const Parcel& data,
Parcel* reply,
unit32_t flag = 0
);
proctected:
......
virtual status_t onTransact(
unit32_t code,
const Parcel& data,
Parcel* reply,
unit32_t flag = 0
)
}
复制代码
BBinder 类有两个重要的成员函数 transact 和 onTransactcookie
class IBinder : public RefBase {
......
}
复制代码
可见 IBinder 类又继承了 RefBase, 也就是说 Binder 本地对象是经过引用计数技术来维护生命周期的并发
template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase
{
public:
BpInterface(const sp<IBinder>& remote);
protected:
virtual IBinder* onAsBinder();
}
复制代码
模板类 BpInterface 继承了 BpRefBase, 后者为 Binder 代理对象提供了抽象的进程间通讯接口app
class BpRefBase : public virtual RefBase
{
protected:
BpRefBase(const sp<IBinder>& o);
......
inline IBinder* remote() {
return mRemote;
}
inline IBinder* remote() const {
return mRemote;
}
private:
.......
IBinder* const mRemote;
}
复制代码
能够看到 BpRefBase 中有一个成员变量 mRemote, 它的实现类为 BpBinder, 看看这个实现类是作了哪些操做框架
class BpBinder : public IBinder
{
public:
BpBinder(int32_t handle);
inline int32_t handle() const {
return mHandle;
}
......
virtual status_t transact(
unit32_t code,
const Parcel& data,
Parcel* reply,
unit32_t flags = 0
);
......
private:
const int32_t mHandle;
}
复制代码
能够看到 BpBinder 中有一个 mHandle 句柄, 它表示 Binder 引用对象的句柄值, 能够经过 handle 来获取异步
class IPCThreadState
{
public:
static IPCThread* self();
......
status_t transact(
int32_t handle,
unit32_t code,
const Parcel& data,
Parcel* reply,
unit32_t flags
);
......
private:
status_t talkWithDriver(bool doRecive = true);
......
const sp<ProcessState> mProcess;
......
}
复制代码
能够看到 IPCThreadState 内部存在一个成员变量 mProcessasync
接下来看看 ProcessState 的实现
class ProcessState: public virtual RefBase
{
public:
static sp<ProcessState> self();
......
private:
int mDriverID;
void* mVMStart;
}
复制代码
好的至此, 咱们队 Binder 库中几个很是重要的 C++ 对象有了必定的认识, 接下来看看这几个类的相互依赖关系
运行时库中的 ServiceManager 与 Java 中的 ServiceManager 是对应的
因为篇幅缘由, 这里就不介绍 Java 中的 ServiceManager 了, 感兴趣的同窗能够看看 Zygote 与系统服务进程的启动, 没准能够找到你想要的答案
ServiceManager 该程序的入口函数 main 实如今 service_manager.c 中
// frameworks/base/cmds/servicemanager/service_manager.c
int main(int argc, char **argv) {
struct binder_state *bs;
void *svcmgr = BINDER_SERVICE_MANAGER;
// 打开设备文件
bs = binder_open(128*1024);
// 将本身注册为 Binder 驱动的上下文管理者
if (binder_become_context_manager(bs)) {
return -1;
}
svcmgr_handle = svcmgr;
// 循环等待和处理 Client 进程的通讯请求
binder_loop(bs, svcmgr_handler);
return 0;
}
复制代码
可见 service_manager 的主函数中主要作了三件事情
// frameworks/base/cmds/servicemanager/binder.c
struct binder_state *binder_open(size_t mapsize)
{
struct binder_state *bs;
struct binder_version vers;
// 在堆内存中建立了 binder_state 的实例
bs = malloc(sizeof(*bs));
if (!bs) {
errno = ENOMEM;
return NULL;
}
// 调用 open 函数打开 Binder 设备文件
bs->fd = open("/dev/binder", O_RDWR | O_CLOEXEC);
if (bs->fd < 0) {
goto fail_open;
}
if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
(vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
goto fail_open;
}
// 将给进程分配的内核缓冲区大小记录到 binder_state 结构体对象中
bs->mapsize = mapsize;
// 调用函数 mmap 将设备文件 /dev/binder 映射到地址空间, 而且将其地址空间的首地址记录到 binder_state 结构体对象中
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;
}
// 返回这个 binder_state 这个结构体对象
return bs;
fail_map:
close(bs->fd);
fail_open:
free(bs);
return NULL;
}
复制代码
可见 ServiceManager 的打开设备文件的操做很是简单
// frameworks/base/cmds/servicemanager/binder.c
int binder_become_context_manager(struct binder_state *bs)
{
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
复制代码
能够看到注册上下文管理者的函数中, 调用了 ioctl 这个函数(即 Binder IO controller, 用于用户空间与 Binder 驱动交互)
接下来简单的看一下, Binder 内核驱动中对这个 IO 控制命令作了哪些处理
// Binder 通讯上下文管理者的在 Binder 内核驱动中的 Binder 实体对象
static struct binder_node *binder_context_mgr_node;
// 描述了注册了 Binder 通讯上下文管理者的有效用户 ID
static struct binder_context_mgr_uid = -1;
static long binder_ioctrl(struct file *filp, unsigned int cmd, unsigned long arg) {
// 获取当前进程的 binder 线程, 没有则建立一个
thread = binder_get_thread(proc);
switch(cmd) {
......
case BINDER_SET_CONTEXT_MGR:
// 说明 Binder 上下文管理者已经注册过了
if (binder_context_mgr_node != NULL) {
goto error;
}
// 说明 Binder 上下文管理者已经注册过了
if (binder_context_mgr_uid != -1) {
goto error;
} else {
// 通过一系列验证以后, 给当前进程建立其对应的 binder 实体对象保存在全局的 binder_context_mgr_node 变量中
binder_context_mgr_node = binder_new_node(proc, NULL, NULL);
}
......
break;
}
}
复制代码
Binder 内核驱动中针对 BINDER_SET_CONTEXT_MGR 这个控制码, 主要作了如下操做
// frameworks/base/cmds/servicemanager/binder.c
void binder_loop(struct binder_state *bs, binder_handler func)
{
int res;
struct binder_write_read bwr;
uint32_t readbuf[32];
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
// BC_ENTER_LOOPER: 控制位的含义是, 将当前线程注册成为 Binder 线程
// 以便 Binder 驱动程序能够将进程间的通讯请求分发给它处理
readbuf[0] = BC_ENTER_LOOPER;
// 该函数经过 IO 控制命令将 readbuf 发送给 Binder 驱动程序, 通知其处理 readbuf 中的控制位
binder_write(bs, readbuf, sizeof(uint32_t));
// for 循环从 binder 驱动中获取须要处理的进程间通讯请求
for (;;) {
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
// 经过 BINDER_WRITE_READ 控制位, 从 Binder 驱动中获取当前是否有新的进程间请求须要处理
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
if (res < 0) {
ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
break;
}
// 处理进程间的请求
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
if (res == 0) {
ALOGE("binder_loop: unexpected reply?!\n");
break;
}
if (res < 0) {
ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
break;
}
}
}
复制代码
binder_loop 主要作了如下几件事情
接下来看看 binder_write 方法的实现
// frameworks/base/cmds/servicemanager/binder.c
int binder_write(struct binder_state *bs, void *data, size_t len)
{
struct binder_write_read bwr;
int res;
bwr.write_size = len;
bwr.write_consumed = 0;
// 将数据存储在 write_buffer 中, 即 BC_ENTER_LOOPER 这个控制码
bwr.write_buffer = (uintptr_t) data;
bwr.read_size = 0;
bwr.read_consumed = 0;
bwr.read_buffer = 0;
// 调用 ioctl 与 binder 驱动通讯, 请求码为 BINDER_WRITE_READ
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
if (res < 0) {
fprintf(stderr,"binder_write: ioctl failed (%s)\n",
strerror(errno));
}
return res;
}
复制代码
可见真正用于和 Binder 内核驱动交互的请求码为 BINDER_WRITE_READ, 接下来看看 binder 驱动作了哪些处理
static long binder_ioctrl(struct file *filp, unsigned int cmd, unsigned long arg) {
// 获取当前进程的 binder 线程, 没有则建立一个
thread = binder_get_thread(proc);
switch(cmd) {
......
case BINDER_WRITE_READ:
......
if (bwr.write_size > 0) {
// 可见这里将 BC_ENTER_LOOPER 请求码转发给了 binder_thread_write 函数
ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.read_consumed);
}
......
break;
}
}
int binder_thread_write(......) {
while(...) {
switch(cmd) {
case BC_ENTER_LOOPER:
// 这里将这个线程注册成为了 looper 线程, 至此 Binder 进行间的通讯请求便会交由这个线程处理
thread->looper |= BINDER_LOOPER_STATE_ENTERED;
break;
}
}
}
复制代码
至此, ServiceManager 的主线程即可以接收到 Binder 驱动发送的通讯请求了
了解了 ServiceManager 如何启动了以后, 咱们就进入重头戏, 看看一次 Binder 驱动通讯的流程是如何进行的
defaultServiceManager()->addService(String16("SampleService"), new SampleService);
复制代码
在应用框架层中, 获取 ServiceManager 代理对象的方式为 defaultServiceManager()
接下来看看 addService 这个方法在 BpServiceManager 代理类中的实现
// frameworks/base/libs/binder/IServiceManager.cpp
virtual status_t addService(const String16& name, const sp<IBinder>& service,
bool allowIsolated)
{
Parcel data, reply;
// 1. 将通讯参数封装到 data 中
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
data.writeStrongBinder(service);
data.writeInt32(allowIsolated ? 1 : 0);
// 2. 调用 BpBinder 的 transact
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
// 3. 读取请求结束后 Server 经过 Binder 驱动返回回来的数据
return err == NO_ERROR ? reply.readExceptionCode() : err;
}
复制代码
代理实现方法主要作了如下几步操做
// frameworks/native/libs/binder/BpBinder.cpp
status_t BpBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
// mAlive 用于判断 Binder 代理对象所引用的 Binder 本地对象是否存活
if (mAlive) {
// 调用了 IPCThreadState 的 transact 方法
// mHandle 为这个代理对象的句柄值
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}
// frameworks/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) {
// 1. 将 data 封装到一个 binder_transaction_data 结构体对象中
// handle 为当前 Binder 代理对象的句柄值
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
}
// TF_ONE_WAY 若为 0 则说明是同步的进程间请求
if ((flags & TF_ONE_WAY) == 0) {
if (reply) {
// 2. 经过 waitForResponse 向 Binder 驱动发送上面封装的 binder_transaction_data 结构体对象
// 操做码为 BC_TRANSACTION
err = waitForResponse(reply);
} else {
......
}
} else {
......
}
return err;
}
复制代码
可见 Client 调用远程方法时, 其代理对象的 transact 方法主要作了如下的操做
先看看如何将 data 等数据封装成为 binder_transaction_data
// frameworks/native/libs/binder/IPCThreadState.cpp
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 对象
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) {
// 将 data 中的数据拷贝到 tr 中
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();
} else if (statusBuffer) {
......
} else {
......
}
// mOut 描述一个命令缓冲协议区
mOut.writeInt32(cmd);// 将 cmd 这个命令写入, 表示这个命令以后须要发送给 Binder 驱动
mOut.write(&tr, sizeof(tr));// 将 tr 这个结构体写入, 用于后续与 Binder 驱动交互
return NO_ERROR;
}
复制代码
writeTransactionData 这个函数作的事情与咱们上述一致
接下来看看 waitForResponse 如何经过 tr 和 BC_TRANSACTION 命令与 Binder 驱动程序进行交互
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
uint32_t cmd;
int32_t err;
while (1) {
// 可见这个里调用了 talkWithDriver() 与 Binder 驱动交互
if ((err=talkWithDriver()) < NO_ERROR) break;
if (err < NO_ERROR) break;
// 缓冲区 mIn 这个与 mOut 相对应, 它用于保存从 Binder 驱动程序接收到的返回协议
if (mIn.dataAvail() == 0) continue;
cmd = (uint32_t)mIn.readInt32();
......
}
}
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
......
// 1. 定义 binder_write_read 结构体, 指定输入缓冲区和输出缓冲区
binder_write_read bwr;
// 指定从当前进程输出到 Binder 驱动的缓冲区
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();// 将要输出的数据保存到 bwr 的 write_buffer 变量中
// doReceive 用来描述调用者是否能够收到 Binder 的返回协议码
if (doReceive && needRead) {
// 设置从 Binder 驱动输入到当前进程缓冲区的相关参数
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (uintptr_t)mIn.data();
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
}
// 若是输出缓冲区和输入缓冲区大小都为 0, 说明不须要与 Binder 驱动交互
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
......
// 2. 使用 IO 控制命令 BINDER_WRITE_READ 来与 Binder 驱动进行交互, 说明要进行读写操做
// bwr 即为读写操做的内容
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
......
} while (err == -EINTR);
if (err >= NO_ERROR) {
// 将 Binder 驱动已处理的命令协议从 mOut 中移除
if (bwr.write_consumed > 0) {
if (bwr.write_consumed < mOut.dataSize())
mOut.remove(0, bwr.write_consumed);
else
mOut.setDataSize(0);
}
// 将 Binder 驱动返回的命令协议保存到 mIn 中
if (bwr.read_consumed > 0) {
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
return NO_ERROR;
}
return err;
}
复制代码
talkWithDriver 这个函数很是重要, 它是 Binder 停留在应用程序框架层的最后一个函数, 主要作了以下操做
// kernel/goldfish/drivers/staging/android/binder.c
static int binder_thread_write(struct binder_proc *proc,
struct binder_thread *thread,binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed)
{
uint32_t cmd;
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
while (ptr < end && thread->return_error == BR_OK) {
// 获取从用户空间传递过来的指令码保存在 cmd 中, 由上面可知, cmd 为 BC_TRANSACTION
if (get_user_preempt_disabled(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
......
switch(cmd) {
......
// 处理 BC_TRANSACTION/BC_REPLY
case BC_TRANSACTION:
case BC_REPLY: {
// 从用户空间拷贝数据到 transaction_data 中
struct binder_transaction_data tr;
if (copy_from_user_preempt_disabled(&tr, ptr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
// 进行指令码的处理操做
binder_transaction(proc, thread, &tr,
cmd == BC_REPLY, 0);
break;
}
......
}
}
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply,
binder_size_t extra_buffers_size)
{
......
if (reply) {// 处理 BC_REPLY 指令, 到后面分析
....
} else {
// 处理 BC_TRANSACTION 指令
if (tr->target.handle) {
// 1. 获取 Client 调用的 binder 引用对象
struct binder_ref *ref;
// 从 client 进程中, 经过句柄值, 获取其在 linux 内核驱动的引用对象
ref = binder_get_ref(proc, tr->target.handle, true);
// 2. 经过引用对象找到其对应的实体对象
target_node = ref->node;
} else {
target_node = context->binder_context_mgr_node;
}
......
// 3. 经过 binder 实体对象, 找对对应的 Server 进程
target_proc = target_node->proc;
......
// 4. 尝试在 Server 进程找到最合适的空闲线程去处理此次 Client 端的请求
if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
struct binder_transaction *tmp;
tmp = thread->transaction_stack;
......
while (tmp) {
if (tmp->from && tmp->from->proc == target_proc)
target_thread = tmp->from;
tmp = tmp->from_parent;
}
}
}
// 5. 将目标线程的 todo 队列和 wait 队列保存到成员变量中
if (target_thread) {
// 更新成员变量指向目标线程中的相关属性
target_list = &target_thread->todo;
target_wait = &target_thread->wait;
} else {
// 更新成员变量指向目标进程中的相关属性
target_list = &target_proc->todo;
target_wait = &target_proc->wait;
};
/* TODO: reuse incoming transaction for reply */
// 6.1 binder_transaction 对象 t 会被封装成为 BINDER_WORK_TRANSACTION 工做项,
// 后续会添加到 Server 目标线程的 todo 中, 以便其可以接受到 Binder 驱动发送的 BR_TRANSACTION 协议
t = kzalloc_preempt_disabled(sizeof(*t));
// 6.2 binder_transaction 对象 tcomplete 会被封装成 BINDER_WORK_TRANSACTION_COMPLETE 工做项
// 后续会发送到 Client 发起线程的 todo 队列中, 以便其可以接收到 Binder 驱动发送的 BR_TRANSACTION_COMPLETE 协议
tcomplete = kzalloc_preempt_disabled(sizeof(*tcomplete));
// 初始化 t
if (!reply && !(tr->flags & TF_ONE_WAY))
t->from = thread;
else
t->from = NULL;
t->sender_euid = task_euid(proc->tsk);
t->to_proc = target_proc;
t->to_thread = target_thread;
t->code = tr->code;
t->flags = tr->flags;
t->priority = task_nice(current);
// 从 tr 中复制数据到目标进程的内核缓冲区
t->buffer = binder_alloc_buf(target_proc, tr->data_size, tr->offsets_size, extra_buffers_size, !reply && (t->flags & TF_ONE_WAY));
t->buffer->allow_user_free = 0;
t->buffer->debug_id = t->debug_id;
t->buffer->transaction = t;
t->buffer->target_node = target_node;
......
}
复制代码
好了, 总结一下, 这个 binder 内核中处理用户空间指令码交换的方法主要是 binder_transaction, 关于 BC_TRANSACTION 它主要作了以下操做
接下来就要处理方法的调用了, 咱们在应用层调用了 data.writeStrongBinder(binder); 将 binder 注册到目标进程中, 看看 binder_transaction 是怎样处理的
for (; offp < off_end; offp++) {
struct flat_binder_object *fp;
fp = (struct flat_binder_object *) (t->buffer->data + *offp)
switch (fp->type) {
// 咱们开始的时候调用的是 put 方法
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
struct binder_ref *ref;
// 8. 尝试从 Client 进程中获取参数中 binder 对象在 linux 内核驱动中的实体对象
struct binder_node* node = binder_get_node(proc, fp->binder);
if (node == NULL) {
// 若源进程没有其实体对象, 则调用 binder_new_node 建立一个
node = binder_new_node(proc, fp->binder, fp->cookie);
}
// 9. 获取 Sever 进程须要使用的 binder 引用对象(没有则建立一个)
ref = binder_get_ref_for_node(target_proc, node);
} break;
}
复制代码
可见这些的操做很是的重要
好的, 线程参数数据也已经获取完了, 咱们接着往下看 binder_transaction
if (reply) {
......
} else if (!(t->flags & TF_ONE_WAY)) {
// 同步操做
t->need_reply = 1;
t->from_parent = thread->transaction_stack;
thread->transaction_stack = t;
} else {
// 若为异步操做, 则将任务添加到目标 binder 进程的异步队列中
if (target_node->has_async_transaction) {
target_list = &target_node->async_todo;
target_wait = NULL;
} else
target_node->has_async_transaction = 1;
}
// 将 t 的工做项设置为 BINDER_WORK_TRANSACTION
t->work.type = BINDER_WORK_TRANSACTION;
// 添加到目标进程的工做队列的尾部
list_add_tail(&t->work.entry, target_list);
// 将 tcomplete 的工做项设置为 BINDER_WORK_TRANSACTION_COMPLETE
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
// 添加到源进程的工做项的尾部
list_add_tail(&tcomplete->entry, &thread->todo);
if (target_wait) {
// 唤醒目标线线程去执行 BINDER_WORK_TRANSACTION 任务
wake_up_interruptible(target_wait);
}
return;
复制代码
找到了目标进程以及须要执行的线程后, 源线程和目标进程就回去并发的处理本身的工做项了
static int binder_thread_read(.......) {
......
while(1) {
switch(w->type) {
......
case BINDER_WORK_TRANSACTION_COMPLETE: {
// 将 BR_TRANSACTION_COMPLETE 返回用户空间
cmd = BR_TRANSACTION_COMPLETE;
if (put_user_preempt_disabled(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
binder_stat_br(proc, thread, cmd);
list_del(&w->entry);
kfree(w);
binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
} break;
......
}
}
}
复制代码
可见 binder_thread_read 对 BINDER_WORK_TRANSACTION_COMPLETE 处理也很是简单
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
uint32_t cmd;
int32_t err;
while (1) {
if ((err=talkWithDriver()) < NO_ERROR) break;
err = mIn.errorCheck();
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue;
// 从输入缓冲区中读取, 是否有 Binder 驱动写入的数据
cmd = (uint32_t)mIn.readInt32();
// 主要查看 BR_TRANSACTION_COMPLETE 指令码
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
if (!reply && !acquireResult) goto finish;
break;
......
}
finish:
if (err != NO_ERROR) {
if (acquireResult) *acquireResult = err;
if (reply) reply->setError(err);
mLastError = err;
}
return err;
}
复制代码
能够看到 BR_TRANSACTION_COMPLETE 指令码很简单
因此, 接下来的重头戏即是咱们须要查看目标线程对 Binder 驱动发出的 BINDER_WORK_TRANSACTION 指令的处理
由前面可知, BINDER_WORK_TRANSACTION 会将工做项添加到目标进程的 todo 队列中, 那么目标进程就会被唤醒, 进而执行器 binder_thread_read 处理 todo 队列中的工做项
// kernel/goldfish/drivers/staging/android/binder.c
static int binder_thread_write(struct binder_proc *proc,
struct binder_thread *thread,binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed)
{
......
// 循环从其读取器工做项数据
while (1) {
uint32_t cmd;
struct binder_transaction_data tr;
struct binder_work *w;
struct binder_transaction *t = NULL;
// 1. 从其线程/进程的 todo 队列中获取工做项, 而且将数据存入 binder_work 结构体对象中
if (!list_empty(&thread->todo)) {
w = list_first_entry(&thread->todo, struct binder_work,
entry);
} else if (!list_empty(&proc->todo) && wait_for_proc_work) {
w = list_first_entry(&proc->todo, struct binder_work,
entry);
} else {
......
break;
}
// 2. 处理工做项中对应的指令码
switch (w->type) {
// 咱们主要关注对 BINDER_WORK_TRANSACTION 的处理
case BINDER_WORK_TRANSACTION: {
// 2.1 将 binder_work 转为一个 binder_transaction 结构体对象
t = container_of(w, struct binder_transaction, work);
} break;
......
// 3. 将 binder_transaction 中的数据从 binder_transaction_data 中, 以便后续能够传输到用户空间
if (t->buffer->target_node) {// target_node 不为 NULL, 则指定协议码为 BR_TRANSACTION
struct binder_node *target_node = t->buffer->target_node;
// 将目标线程 binder 本地对象的信息复制到 tr 中, 以便目标线程的 thread 接收到 binder 驱动发送的 BR_TRANSACTION 以后, 能够将返回协议交给指定的 binder 本地对象处理
tr.target.ptr = target_node->ptr;
tr.cookie = target_node->cookie;
t->saved_priority = task_nice(current);
// 保证目标线程的优先级 < 源线程的优先级
if (t->priority < target_node->min_priority &&
!(t->flags & TF_ONE_WAY))
binder_set_nice(t->priority);
else if (!(t->flags & TF_ONE_WAY) ||
t->saved_priority > target_node->min_priority)
binder_set_nice(target_node->min_priority);
cmd = BR_TRANSACTION;
} else {
......
}
.......
// 4. 将 tr 数据拷贝到目标进程的用户空间中
if (put_user_preempt_disabled(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
// 将对应的协议码拷贝到用户空间中
if (copy_to_user_preempt_disabled(ptr, &tr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
.......
// 5. 这个工做项已经被处理了, 从链表中删除
list_del(&t->work.entry);
t->buffer->allow_user_free = 1;
// 判断是否为同步请求
if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
t->to_parent = thread->transaction_stack;
t->to_thread = thread;
// 压入目标线程的任务栈中
thread->transaction_stack = t;
} else {
t->buffer->transaction = NULL;
kfree(t);
}
break;
}
return 0;
}
复制代码
可见目标线程被唤醒以后他在 binder 驱动中作了以下的事情
好的, 接下来就进入了目标进程的用户空间了
ServiceManager 被 Binder 驱动唤醒后, 会调用 binder_parse 方法来处理从 Binder 驱动程序中接收到的返回协议
// frameworks/base/cmds/servicemanager/binder.c
int binder_parse(struct binder_state *bs, struct binder_io *bio,
uintptr_t ptr, size_t size, binder_handler func)
{
int r = 1;
uintptr_t end = ptr + (uintptr_t) size;
while (ptr < end) {
// 1. 从用户空间的缓冲区中读取 Binder 驱动传递过来的协议码
uint32_t cmd = *(uint32_t *) ptr;
ptr += sizeof(uint32_t);
switch(cmd) {
......
// 这里主要关注 BR_TRANSACTION 协议
case BR_TRANSACTION: {
// 2. 从缓冲区中获取通讯数据的结构体
struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
......
if (func) {
unsigned rdata[256/4];
struct binder_io msg; // 解析从 Binder 驱动程序读取回来的进程间通讯数据
struct binder_io reply; // 将通讯结果写入 reply 中以便于传给 Binder 驱动, 进而返回源进程
int res;
// 3. 初始化 reply 和 rdata
bio_init(&reply, rdata, sizeof(rdata), 4);
// 4. 解析 txn 中的数据到 msg 中
bio_init_from_txn(&msg, txn);
// 5. 调用 func 函数指针, 处理协议, 将结果写入 reply
res = func(bs, txn, &msg, &reply);
......
// 将通讯结果返回给 binder 驱动
binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
}
ptr += sizeof(*txn);
break;
}
}
}
}
复制代码
binder_parse 中所作的事情很是清晰
在 ServiceManager 中, func 这个函数指针, 指代 svcmgr_handler 这个函数
// frameworks/base/cmds/servicemanager/service_manager.c
int svcmgr_handler(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply)
{
......
// 验证接口名称的描述
strict_policy = bio_get_uint32(msg);
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
// 执行对应的方法
switch(txn->code) {
case SVC_MGR_ADD_SERVICE:
// 获取一个要注册服务的名称 (如"ActivityManagerService")
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
// 从 msg 中取出要注册的服务 binder 引用对象的句柄值
handle = bio_get_ref(msg);
allow_isolated = bio_get_uint32(msg) ? 1 : 0;
// 执行添加服务的操做
if (do_add_service(bs, s, len, handle, txn->sender_euid,
allow_isolated, txn->sender_pid))
return -1;
break;
default:
ALOGE("unknown code %d\n", txn->code);
return -1;
}
// 调用这个函数, 将成功代码 0 写入到 binder 结构体 reply 中
bio_put_uint32(reply, 0);
return 0;
}
复制代码
能够看到一个很是重要的函数 do_add_service 这个函数真正执行了服务的添加过程
// frameworks/base/cmds/servicemanager/service_manager.c
int do_add_service(struct binder_state *bs,
const uint16_t *s, size_t len,
uint32_t handle, uid_t uid, int allow_isolated,
pid_t spid)
{
struct svcinfo *si;
if (!handle || (len == 0) || (len > 127))
return -1;
// 判断 uid 所指代的源进程, 是否有资格进行注册操做
if (!svc_can_register(s, len, spid, uid)) {
return -1;
}
// 判断 si 服务是否已经注册了
si = find_svc(s, len);
if (si) {
......
} else {
// 建立一个 svcinfo 而且链入 svclist 中
si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
si->handle = handle;
si->len = len;
memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
si->name[len] = '\0';
si->death.func = (void*) svcinfo_death;
si->death.ptr = si;
si->allow_isolated = allow_isolated;
si->next = svclist;
svclist = si;
}
binder_acquire(bs, handle);
// 绑定死亡通知
binder_link_to_death(bs, handle, &si->death);
return 0;
}
复制代码
至此就成功的将一个 Service 组件注册到 ServiceManager 中了
// frameworks/base/cmds/servicemanager/service_manager.c
void binder_send_reply(struct binder_state *bs,
struct binder_io *reply,
binder_uintptr_t buffer_to_free,
int status)
{
struct {
uint32_t cmd_free;
binder_uintptr_t buffer;
uint32_t cmd_reply;
struct binder_transaction_data txn;
} __attribute__((packed)) data;
// cmd_free 的协议码为 BC_FREE_BUFFER
data.cmd_free = BC_FREE_BUFFER;
data.buffer = buffer_to_free;
// cmd_reply 的协议为 BC_REPLY
data.cmd_reply = BC_REPLY;
data.txn.target.ptr = 0;
data.txn.cookie = 0;
data.txn.code = 0;
// 将一些数据写入 data 的 txn 中
if (status) {
data.txn.flags = TF_STATUS_CODE;
data.txn.data_size = sizeof(int);
data.txn.offsets_size = 0;
data.txn.data.ptr.buffer = (uintptr_t)&status;
data.txn.data.ptr.offsets = 0;
} else {
data.txn.flags = 0;
data.txn.data_size = reply->data - reply->data0;
data.txn.offsets_size = ((char*) reply->offs) - ((char*) reply->offs0);
data.txn.data.ptr.buffer = (uintptr_t)reply->data0;
data.txn.data.ptr.offsets = (uintptr_t)reply->offs0;
}
// 其内部调用 ioctl() 函数和 IO 控制命令 BINDER_WRITE_READ 将 BC_FREE_BUFFER/BC_REPLY 发送给 binder 驱动程序
binder_write(bs, &data, sizeof(data));
}
复制代码
能够看到 binder_send_reply 中作的操做也比较清晰
接下来看看 Binder 驱动如何处理这些协议码的
// kernel/goldfish/drivers/staging/android/binder.c
static int binder_thread_write(struct binder_proc *proc,
struct binder_thread *thread,binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed)
{
......
while (ptr < end && thread->return_error == BR_OK) {
// 获取从用户空间传递过来的指令码保存在 cmd 中, 由上面可知, cmd 为 BC_TRANSACTION
if (get_user_preempt_disabled(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
......
switch(cmd) {
......
// 处理 BC_FREE_BUFFER 协议码
case BC_FREE_BUFFER: {
binder_uintptr_t data_ptr;
struct binder_buffer *buffer;
// 从用户空间中获得要释放的内核缓冲区的地址, 存放到 data_ptr 中
if (get_user_preempt_disabled(data_ptr, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
// 获取缓冲区
buffer = binder_buffer_lookup(proc, data_ptr);
if (buffer == NULL) {
break;
}
// 判断是否容许释放
if (!buffer->allow_user_free) {
break;
}
// 说明内核缓冲区分配给 transaction 使用的
if (buffer->transaction) {
buffer->transaction->buffer = NULL;
buffer->transaction = NULL;
}
if (buffer->async_transaction && buffer->target_node) {
BUG_ON(!buffer->target_node->has_async_transaction);
if (list_empty(&buffer->target_node->async_todo))
buffer->target_node->has_async_transaction = 0;
else
list_move_tail(buffer->target_node->async_todo.next, &thread->todo);
}
// 减小相关的引用计数
binder_transaction_buffer_release(proc, buffer, NULL);\
// 释放内核缓冲区 buffer
binder_free_buf(proc, buffer);
break;
}
......
}
复制代码
能够看到 binder_thread_write 中对于 BC_FREE_BUFFER 协议码的处理, 主要是释放通讯过程给目标进程分配的内核缓冲区, 减小相关的引用计数
接下来看看 binder_thread_write 对 BC_REPLY 的处理
......
// 处理 BC_TRANSACTION/BC_REPLY
case BC_TRANSACTION:
case BC_REPLY: {
// 从用户空间拷贝数据到 binder_transaction_data 中
struct binder_transaction_data tr;
if (copy_from_user_preempt_disabled(&tr, ptr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
// 进行指令码的处理操做
binder_transaction(proc, thread, &tr,
cmd == BC_REPLY, 0);
break;
}
......
}
}
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply,
binder_size_t extra_buffers_size)
{
......
if (reply) {// 处理 BC_REPLY 指令
// 找寻目标线程(即 Client 端的线程)
in_reply_to = thread->transaction_stack;
if (in_reply_to == NULL) {
binder_user_error("%d:%d got reply transaction with no transaction stack\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
goto err_empty_call_stack;
}
// 恢复目标线程的优先级
binder_set_nice(in_reply_to->saved_priority);
if (in_reply_to->to_thread != thread) {
......
return_error = BR_FAILED_REPLY;
in_reply_to = NULL;
goto err_bad_call_stack;
}
// 将要处理的事务, 添加到线程栈的顶端
thread->transaction_stack = in_reply_to->to_parent;
target_thread = in_reply_to->from;
if (target_thread == NULL) {
return_error = BR_DEAD_REPLY;
goto err_dead_binder;
}
if (target_thread->transaction_stack != in_reply_to) {
return_error = BR_FAILED_REPLY;
in_reply_to = NULL;
target_thread = NULL;
goto err_dead_binder;
}
target_proc = target_thread->proc;
} else {// 处理 BC_TRANSACTION 指令, 前面已经分析过了
......
}
// ....... 与分析 BC_TRANSACTION 后续一致
}
复制代码
能够看到 Binder 驱动对于 BC_REPLY 比较简单, 除了 BC_REPLY 中的操做与 BC_TRANSACTION 有所不一样, 后续的操做是一致的, 毕竟调用的是同一个方法 , 最终会封装成两个工做项 BINDER_WORK_TRANSACTION 和 BINDER_WORK_TRANSACTION_COMPLETE 分别发送给目标进程和源进程 (这里的目标进程为Client 端, 源进程为 Server 端了, 由于本次发起 Binder 驱动通讯的为 Server 端)
源进程接收到 BINDER_WORK_TRANSACTION_COMPLETE 以后, 就完全的结束此次的 Binder 通讯了, 这里再也不赘述
接下来看看目标进程如何处理 BINDER_WORK_TRANSACTION 工做项
// kernel/goldfish/drivers/staging/android/binder.c
static int binder_thread_write(struct binder_proc *proc,
struct binder_thread *thread,binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed)
{
......
// 循环从其读取器工做项数据
while (1) {
uint32_t cmd;
struct binder_transaction_data tr;
struct binder_work *w;
struct binder_transaction *t = NULL;
......
// 处理工做项中对应的指令码
switch (w->type) {
// 咱们主要关注对 BINDER_WORK_TRANSACTION 的处理
case BINDER_WORK_TRANSACTION: {
t = container_of(w, struct binder_transaction, work);
} break;
......
// 将 binder_transaction 中的数据从 binder_transaction_data 中, 以便后续能够传输到用户空间
if (t->buffer->target_node) {
......// 上面已经分析过了
} else {
// target_node 为 NULL, 则指定协议码 BR_REPLY
tr.target.ptr = 0;
tr.cookie = 0;
cmd = BR_REPLY;
}
.......
// 将协议码和数据拷贝到用户空间
if (put_user_preempt_disabled(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
if (copy_to_user_preempt_disabled(ptr, &tr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
.......
// 这个工做项已经被处理了, 从链表中删除
list_del(&t->work.entry);
t->buffer->allow_user_free = 1;
// 判断是否为同步请求
if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
// ...... 在上面已经分析过了
} else {
// 直接释放内核缓冲区的内存
t->buffer->transaction = NULL;
kfree(t);
}
break;
}
return 0;
}
复制代码
能够看到 BINDER_WORK_TRANSACTION 工做项的 t->buffer->target_node 为 NULL 时, 会将协议码置为 BR_REPLY, 而后将数据写入用户空间, 接下来咱们看看用户空间对 BR_REPLY 的处理
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
uint32_t cmd;
int32_t err;
while (1) {
if ((err=talkWithDriver()) < NO_ERROR) break;
err = mIn.errorCheck();
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue;
// 从输入缓冲区中读取, 是否有 Binder 驱动写入的数据
cmd = (uint32_t)mIn.readInt32();
// 主要查看 BR_TRANSACTION_COMPLETE 指令码
switch (cmd) {
case BR_REPLY:
{
// 从用户缓冲区获取 Binder 驱动写入的数据
binder_transaction_data tr;
err = mIn.read(&tr, sizeof(tr));
if (err != NO_ERROR) goto finish;
if (reply) {
// 表示该线程发送的进程间通讯请求已经被处理了
if ((tr.flags & TF_STATUS_CODE) == 0) {
// 这个方法将 Binder 驱动传递过来的数据写入 Parcel 的 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 {
......
}
} else {
......
}
}
// 跳出循环, 即 waitForResponse 等待进程通讯的结果的操做已经结束了
goto finish;
......
}
finish:
......
return err;
}
复制代码
能够看到 Client 端用户空间对 BR_REPLY 的操做也很是清晰
至此, 一次 Binder 进程间的通讯就分析完了, 这里再次梳理一下, 其主要包括以下几个步骤
至此, 咱们 Binder 驱动的讲解就结束了, 这部分的内容较之音视频的难度, 感受也不遑多让, 笔者花费了很大的精力去剖析, 的确很是的晦涩难懂, 这也是笔者为什么迟迟难如下笔的缘由
如有人可以坚持读到这里, 那这可真是至关使人欣慰的事情啊, 若可以帮你理清 Binder 驱动相关知识, 那我将会感到很是的荣幸
这里祝你们新年快乐了, 但愿新的一年里, 你们都能收获本身想要的, 越努力, 越幸运 !