Binder框架 -- 用户空间和驱动的交互
来源:互联网 发布:华能贵诚信托 知乎 编辑:程序博客网 时间:2024/05/29 19:32
Binder框架 – 用户空间和驱动的交互
Binder框架 – android AIDL 的使用
Binder框架 – 用户空间和驱动的交互
Binder框架 – Binder 驱动
Binder 框架 – binder 用户空间框架
MediaPlayerServic 启动的过程中,代码可以简化为:
- sp sm = new BpServiceManager(BpBinder(0));
- sm->addService(String16(“media.player”), new MediaPlayerService());
针对上面的两步,看下用户态程序和内核驱动是怎么交互的。
Parcel
在用户程序和内核的交互中,Android提供了Parcel 类帮助我们对需要传递的数据进行封装,其实就是数据的序列化,按照类型和先后顺序写入到内存中。Parcel 内部的存储区域主要有两个,mData和mObjects, mData 存储基本数据类型,mObjects存储Binder 数据类型。Parcel 提供了针对各种数据写入和读取的操作函数。这两块区域都是使用 malloc 分配出来。
uint8_t* mData;size_t mDataSize;size_t mDataCapacity;mutable size_t mDataPos;binder_size_t* mObjects;size_t mObjectsSize;size_t mObjectsCapacity;mutable size_t mNextObjectHint;
flat_binder_object
在Parcel 的序列化中,Binder 对象使用flat_binder_object 结构体保存。同时提供了flatten_binder 和unflatten_binder 函数用于序列化和反序列化。
struct flat_binder_object { __u32 type; __u32 flags; union { binder_uintptr_t binder; __u32 handle; }; binder_uintptr_t cookie;};
status_t flatten_binder(const sp<ProcessState>& /*proc*/, const wp<IBinder>& binder, Parcel* out){ flat_binder_object obj; obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; if (binder != NULL) { sp<IBinder> real = binder.promote(); if (real != NULL) { IBinder *local = real->localBinder(); if (!local) { BpBinder *proxy = real->remoteBinder(); if (proxy == NULL) { ALOGE("null proxy"); } const int32_t handle = proxy ? proxy->handle() : 0; obj.type = BINDER_TYPE_WEAK_HANDLE; obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */ obj.handle = handle; obj.cookie = 0; } else { obj.type = BINDER_TYPE_WEAK_BINDER; obj.binder = reinterpret_cast<uintptr_t>(binder.get_refs()); obj.cookie = reinterpret_cast<uintptr_t>(binder.unsafe_get()); } return finish_flatten_binder(real, obj, out); } // XXX How to deal? In order to flatten the given binder, // we need to probe it for information, which requires a primary // reference... but we don't have one. // // The OpenBinder implementation uses a dynamic_cast<> here, // but we can't do that with the different reference counting // implementation we are using. ALOGE("Unable to unflatten Binder weak reference!"); obj.type = BINDER_TYPE_BINDER; obj.binder = 0; obj.cookie = 0; return finish_flatten_binder(NULL, obj, out); } else { obj.type = BINDER_TYPE_BINDER; obj.binder = 0; obj.cookie = 0; return finish_flatten_binder(NULL, obj, out); }}status_t unflatten_binder(const sp<ProcessState>& proc, const Parcel& in, sp<IBinder>* out){ const flat_binder_object* flat = in.readObject(false); if (flat) { switch (flat->type) { case BINDER_TYPE_BINDER: *out = reinterpret_cast<IBinder*>(flat->cookie); return finish_unflatten_binder(NULL, *flat, in); case BINDER_TYPE_HANDLE: *out = proc->getStrongProxyForHandle(flat->handle); return finish_unflatten_binder( static_cast<BpBinder*>(out->get()), *flat, in); } } return BAD_TYPE;}status_t unflatten_binder(const sp<ProcessState>& proc, const Parcel& in, wp<IBinder>* out){ const flat_binder_object* flat = in.readObject(false); if (flat) { switch (flat->type) { case BINDER_TYPE_BINDER: *out = reinterpret_cast<IBinder*>(flat->cookie); return finish_unflatten_binder(NULL, *flat, in); case BINDER_TYPE_WEAK_BINDER: if (flat->binder != 0) { out->set_object_and_refs( reinterpret_cast<IBinder*>(flat->cookie), reinterpret_cast<RefBase::weakref_type*>(flat->binder)); } else { *out = NULL; } return finish_unflatten_binder(NULL, *flat, in); case BINDER_TYPE_HANDLE: case BINDER_TYPE_WEAK_HANDLE: *out = proc->getWeakProxyForHandle(flat->handle); return finish_unflatten_binder( static_cast<BpBinder*>(out->unsafe_get()), *flat, in); } } return BAD_TYPE;}
new BpServiceManager(BpBinder(0))
BpServiceManager 的构造函数以BpBinder 为参数,在BpBinder 的onFirstRef 中完成了驱动强引用计数加1,构造函数中完成了弱引用计数的+1,这个都比较简单。
BpBinder::BpBinder(int32_t handle) : mHandle(handle) , mAlive(1) , mObitsSent(0) , mObituaries(NULL){ ALOGV("Creating BpBinder %p handle %d\n", this, mHandle); extendObjectLifetime(OBJECT_LIFETIME_WEAK); IPCThreadState::self()->incWeakHandle(handle);}void BpBinder::onFirstRef(){ ALOGV("onFirstRef BpBinder %p handle %d\n", this, mHandle); IPCThreadState* ipc = IPCThreadState::self(); if (ipc) ipc->incStrongHandle(mHandle);}void IPCThreadState::incStrongHandle(int32_t handle){ LOG_REMOTEREFS("IPCThreadState::incStrongHandle(%d)\n", handle); mOut.writeInt32(BC_ACQUIRE); mOut.writeInt32(handle);}
addService
virtual status_t addService(const String16& name, const sp<IBinder>& service, bool allowIsolated){ Parcel data, reply; data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); data.writeString16(name); data.writeStrongBinder(service); data.writeInt32(allowIsolated ? 1 : 0); status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); return err == NO_ERROR ? reply.readExceptionCode() : err;}
localBinder 和remoteBinder
android 提供了三个API用于方位本进程的binder, 根据服务端和代理端的不同,这三个API可以做不同的区分,实现了多态。 这是三个虚函数函数定义在IBinder中,BBinder 和BpBinder 根据需要,BBinder 实现了localBinder, BpBinder 实现了remoteBinder,函数的名字与其所代表的含义也恰如其分,BBinder 在服务端,所以是localBinder, BpBinde 在代理端, 对服务端来说,是remote。
writeStrongBinder
Parcel 提供的 writeStrongBinder 是对binder类型的数据进行序列化操作,这一点从内部调用的函数flatten_binder名字上也可以看出来。序列化的时候首先判断是代理端还是服务端,用的是localBinder 函数进行判断。为空,表明是代理端,用remoteBinder 获取BpBinder.不为空则为服务端,这里不为空。序列化以后的Binder 存储在 flat_binder_object结构体中。注意flat_binder_object 的赋值。BBinder 序列化:
- type = BINDER_TYPE_BINDER
- binder = reinterpret_cast(local->getWeakRefs()); // 弱引用对象
- cookie = reinterpret_cast(local); //this 对象,BBinder本身
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; if (binder != NULL) { IBinder *local = binder->localBinder(); if (!local) { ...... } else { obj.type = BINDER_TYPE_BINDER; obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs()); obj.cookie = reinterpret_cast<uintptr_t>(local); } } else { ...... } return finish_flatten_binder(binder, obj, out);}inline static status_t finish_flatten_binder( const sp<IBinder>& /*binder*/, const flat_binder_object& flat, Parcel* out){ return out->writeObject(flat, false);}status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData){ const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity; const bool enoughObjects = mObjectsSize < mObjectsCapacity; if (enoughData && enoughObjects) {restart_write: // 通过类型转换,对Parcle 的对象存储区域赋值保存, 好隐晦 *reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val; // remember if it's a file descriptor if (val.type == BINDER_TYPE_FD) { if (!mAllowFds) { return FDS_NOT_ALLOWED; } mHasFds = mFdsKnown = true; } // Need to write meta-data? // nullMetaDAta == false val.binder != 0, 参考上面的序列化赋值过程 if (nullMetaData || val.binder != 0) { mObjects[mObjectsSize] = mDataPos; // 强弱引用计数加减 acquire_object(ProcessState::self(), val, this, &mOpenAshmemSize); mObjectsSize++; } return finishWrite(sizeof(flat_binder_object)); } // 存储区域的内存分配过程 if (!enoughData) { const status_t err = growData(sizeof(val)); if (err != NO_ERROR) return err; } if (!enoughObjects) { size_t newSize = ((mObjectsSize+2)*3)/2; if (newSize < mObjectsSize) return NO_MEMORY; // overflow binder_size_t* objects = (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t)); if (objects == NULL) return NO_MEMORY; mObjects = objects; mObjectsCapacity = newSize; } goto restart_write;}
transact 过程
Parcel 数据的序列化完成以后,就需要把Parcel 数据传递给服务端,transact 出马了。具体的条用流程根据前面的分析是:BpBinder#transact => IPCThreadState#transact. BpBinder 的调用传递了三个参数,第一个是cmd, 第二个是传入的Parcel, 第三个保存返回值。
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
BpBinder::transact
BpBinder::transact 中参数增加了一个mHandler, 在BpServiceManager 的构造中,mHandler 被赋值为0。在这里代表要传递的服务。后边会讨论这个hander 的来源和代表的意义。
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); if (status == DEAD_OBJECT) mAlive = 0; return status; } return DEAD_OBJECT;}
IPCThreadState::transact
- writeTransactionData
- waitForResponse
- talkWithDriver
status_t IPCThreadState::transact(int32_t handle, uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags){ status_t err = data.errorCheck(); flags |= TF_ACCEPT_FDS; if (err == NO_ERROR) { err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL); } if (err != NO_ERROR) { if (reply) reply->setError(err); return (mLastError = err); } if ((flags & TF_ONE_WAY) == 0) { if (reply) { err = waitForResponse(reply); } else { Parcel fakeReply; err = waitForResponse(&fakeReply); } } else { err = waitForResponse(NULL, NULL); } return err;}
writeTransactionData
在IPCThreadState::transact 中最重要的函数调用是writeTransactionData,在这里 传入的Parcel 数据 以及handle code 被封装为能和kernel 识别的数据类型:struct binder_transaction_data。数据类型被标记为 BC_TRANSACTION。 然后也被写入IPCThreadState 的Parcel mOut的内存区域中。这时候已经有三个Parcel ,两个参数data 和 reply, 还有一个IPCThreadState 的内部成员mOut, mOut 保存了binder_transaction_data.
struct binder_transaction_data { union { __u32 handle; binder_uintptr_t ptr; } target; // 传递给那个Binder binder_uintptr_t cookie; __u32 code; // 服务内部的命令字 __u32 flags; pid_t sender_pid; uid_t sender_euid; binder_size_t data_size; // 数据区域的大小 binder_size_t offsets_size; // 对象区域的大小, 名字好怪 是不是objs_size 更合适 union { struct { binder_uintptr_t buffer; // 数据区域地址 binder_uintptr_t offsets; // 对象区域的地址 变量命名值得商议 } ptr; __u8 buf[8]; } data; // 描述的传递数据};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; // 不适用ptr 指针, 使用handle 查找binder. 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); // 对象区域的大小,名字好怪 是不是objs_size 更合适 tr.data.ptr.offsets = data.ipcObjects(); // 对象区域的地址 } else if (statusBuffer) { tr.flags |= TF_STATUS_CODE; *statusBuffer = err; tr.data_size = sizeof(status_t); tr.data.ptr.buffer = reinterpret_cast<uintptr_t>(statusBuffer); tr.offsets_size = 0; tr.data.ptr.offsets = 0; } else { return (mLastError = err); } mOut.writeInt32(cmd); mOut.write(&tr, sizeof(tr)); return NO_ERROR;}
waitForResponse
waitForResponse 中调用talkWithDriver
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; ...... }}
talkWithDriver
talkWithDriver 要和驱动通信了,在这里所有的数据又被一个新的结构体标记了,struct binder_write_read.为什么是标记而不是取代,binder_write_read 仅仅描述了IPCThreadState 的两个Parcel 大小,已经内部内存的地址。ioctl 调用BINDER_WRITE_READ 命令进入内核, 传递的参数是 struct binder_write_read。mIn 用来存放从内核的返回数据。
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(); // This is what we'll read. if (doReceive && needRead) { bwr.read_size = mIn.dataCapacity(); bwr.read_buffer = (uintptr_t)mIn.data(); } else { bwr.read_size = 0; bwr.read_buffer = 0; } // Return immediately if there is nothing to do. if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR; bwr.write_consumed = 0; bwr.read_consumed = 0; status_t err; do {#if defined(__ANDROID__) if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) err = NO_ERROR; else err = -errno;#else err = INVALID_OPERATION;#endif if (mProcess->mDriverFD <= 0) { err = -EBADF; } } while (err == -EINTR); if (err >= NO_ERROR) { if (bwr.write_consumed > 0) { if (bwr.write_consumed < mOut.dataSize()) mOut.remove(0, bwr.write_consumed); else mOut.setDataSize(0); } if (bwr.read_consumed > 0) { mIn.setDataSize(bwr.read_consumed); mIn.setDataPosition(0); } return NO_ERROR; } return err;}
数据的转移和封装
在transact 过程中,需要传递的数据由最初的基本数据类型和Binder 类型被层层封装。最后有struct binder_write_read 结构体描述,struct binder_write_read 作为ioctl 的参数传递到内核中。下面的图展示了数据的封装过程。
binder_ioctl
binder_ioctl_write_read
进入内核以后,binder_ioctl 根据cmd 选择binder_ioctl_write_read,binder_ioctl_write_read 调用copy_from_user 从用户空间copy binder_write_read 结构体,第一次copy。 和用户空间封装数据的顺序相反,然后根据写数据的大小和读数据空间的大小进行读写操作。
static int binder_ioctl_write_read(struct file *filp, unsigned int cmd, unsigned long arg, struct binder_thread *thread){ int ret = 0; struct binder_proc *proc = filp->private_data; unsigned int size = _IOC_SIZE(cmd); void __user *ubuf = (void __user *)arg; struct binder_write_read bwr; if (copy_from_user(&bwr, ubuf, sizeof(bwr))) { ret = -EFAULT; goto out; } if (bwr.write_size > 0) { ret = binder_thread_write(proc, thread, bwr.write_buffer, bwr.write_size, &bwr.write_consumed); trace_binder_write_done(ret); if (ret < 0) { bwr.read_consumed = 0; if (copy_to_user(ubuf, &bwr, sizeof(bwr))) ret = -EFAULT; goto out; } } if (bwr.read_size > 0) { ret = binder_thread_read(proc, thread, bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK); trace_binder_read_done(ret); if (!list_empty(&proc->todo)) wake_up_interruptible(&proc->wait); if (ret < 0) { if (copy_to_user(ubuf, &bwr, sizeof(bwr))) ret = -EFAULT; goto out; } } ......}
binder_thread_write
get_user 第二次copy, 读出binder_transaction_data 数据的命令字,这里为BC_TRANSACTION,仅仅一个int 大小。第三次copy_from_user copy 读出binder_transaction_data 结构体
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) { if (get_user(cmd, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); trace_binder_command(cmd); if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) { binder_stats.bc[_IOC_NR(cmd)]++; proc->stats.bc[_IOC_NR(cmd)]++; thread->stats.bc[_IOC_NR(cmd)]++; } switch (cmd) { ...... case BC_TRANSACTION: case BC_REPLY: { struct binder_transaction_data tr; if (copy_from_user(&tr, ptr, sizeof(tr))) return -EFAULT; ptr += sizeof(tr); binder_transaction(proc, thread, &tr, cmd == BC_REPLY); break; } ...... default: return -EINVAL; } *consumed = ptr - buffer; } return 0;}
binder_transaction
- binder_transaction 中首先根据handler 查找目标的binder的target_node, handler == 0 .
- 构造binder_transaction 结构体,这个结构体仅仅驱动内部使用,用来在不同的进程中传递数据。调用 binder_alloc_buf,为数据复制准备空间。binder_alloc_buf 除了分配空间,还需要把内核空间地址计算出 target_node 所代表的进程用户空间地址。
- copy_from_user, 复制 数据域数据。第四次copy
- copy_from_user, 复制 对象域数据。第五次copy
- while 循环,将对象域的所有binder 数据解析出来, 还是用 表示
- 前边 writeStrongBinder 的时候binder type == BINDER_TYPE_BINDER。
- binder_get_node 获取 自身的binder_node 节点,这个时候还没有,所以new一个。
- binder_get_ref_for_node, 找到对target_node 节点的引用 binder_ref * ref
- 对解析出来的结果处理,我们传递进来的IBinder 为MediaPlayerService, 实质上是一个BnMediaPlayerService, 那 BnMediaPlayerService 的hanler, 在这里被赋值 fp->handle = ref->desc ref 为binder_get_ref_for_node 对target_node 的引用。这个值得大小及意义后边分析。在这里,找到了Binder 对象的handle 的出生之处。
- 将binder_transaction 加入到target_node 所代表的进程的target_list 队列中,实际上是todo 队列。
- 唤醒target_node 进程。到这里完成了进程的切换。
static void binder_transaction(struct binder_proc *proc, struct binder_thread *thread, struct binder_transaction_data *tr, int reply){ struct binder_transaction *t; struct binder_work *tcomplete; binder_size_t *offp, *off_end; binder_size_t off_min; struct binder_proc *target_proc; struct binder_thread *target_thread = NULL; struct binder_node *target_node = NULL; struct list_head *target_list; wait_queue_head_t *target_wait; struct binder_transaction *in_reply_to = NULL; struct binder_transaction_log_entry *e; uint32_t return_error; if (reply) { } else { if (tr->target.handle) { // target.handle= 0, 走else 分支 } else { target_node = binder_context_mgr_node; if (target_node == NULL) { return_error = BR_DEAD_REPLY; goto err_no_context_mgr_node; } } target_proc = target_node->proc; if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) { struct binder_transaction *tmp; tmp = thread->transaction_stack; if (tmp->to_thread != thread) { proc->pid, thread->pid, tmp->debug_id, tmp->to_proc ? tmp->to_proc->pid : 0, tmp->to_thread ? tmp->to_thread->pid : 0); return_error = BR_FAILED_REPLY; goto err_bad_call_stack; } while (tmp) { if (tmp->from && tmp->from->proc == target_proc) target_thread = tmp->from; tmp = tmp->from_parent; } } } if (target_thread) { e->to_thread = target_thread->pid; target_list = &target_thread->todo; target_wait = &target_thread->wait; } else { // target_thread 为空 target_list = &target_proc->todo; target_wait = &target_proc->wait; } /* TODO: reuse incoming transaction for reply */ // t 为binder_transaction, 构造 binder_transaction结构体 t = kzalloc(sizeof(*t), GFP_KERNEL); if (t == NULL) { return_error = BR_FAILED_REPLY; goto err_alloc_t_failed; } binder_stats_created(BINDER_STAT_TRANSACTION); tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL); if (tcomplete == NULL) { return_error = BR_FAILED_REPLY; goto err_alloc_tcomplete_failed; } binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE); t->debug_id = ++binder_last_id; // 计数,全局变量 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); trace_binder_transaction(reply, t, target_node); t->buffer = binder_alloc_buf(target_proc, tr->data_size, tr->offsets_size, !reply && (t->flags & TF_ONE_WAY)); if (t->buffer == NULL) { return_error = BR_FAILED_REPLY; goto err_binder_alloc_buf_failed; } t->buffer->allow_user_free = 0; t->buffer->debug_id = t->debug_id; t->buffer->transaction = t; t->buffer->target_node = target_node; trace_binder_transaction_alloc_buf(t->buffer); if (target_node) binder_inc_node(target_node, 1, 0, NULL); offp = (binder_size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *))); if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t) tr->data.ptr.buffer, tr->data_size)) { binder_user_error("%d:%d got transaction with invalid data ptr\n", proc->pid, thread->pid); return_error = BR_FAILED_REPLY; goto err_copy_data_failed; } if (copy_from_user(offp, (const void __user *)(uintptr_t) tr->data.ptr.offsets, tr->offsets_size)) { binder_user_error("%d:%d got transaction with invalid offsets ptr\n", proc->pid, thread->pid); return_error = BR_FAILED_REPLY; goto err_copy_data_failed; } off_end = (void *)offp + tr->offsets_size; off_min = 0; for (; offp < off_end; offp++) { struct flat_binder_object *fp; fp = (struct flat_binder_object *)(t->buffer->data + *offp); off_min = *offp + sizeof(struct flat_binder_object); switch (fp->type) { case BINDER_TYPE_BINDER: case BINDER_TYPE_WEAK_BINDER: { struct binder_ref *ref; struct binder_node *node = binder_get_node(proc, fp->binder); if (node == NULL) { node = binder_new_node(proc, fp->binder, fp->cookie); if (node == NULL) { return_error = BR_FAILED_REPLY; goto err_binder_new_node_failed; } node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK; node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); } ref = binder_get_ref_for_node(target_proc, node); if (ref == NULL) { return_error = BR_FAILED_REPLY; goto err_binder_get_ref_for_node_failed; } if (fp->type == BINDER_TYPE_BINDER) fp->type = BINDER_TYPE_HANDLE; else fp->type = BINDER_TYPE_WEAK_HANDLE; fp->binder = 0; fp->handle = ref->desc; fp->cookie = 0; binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE, &thread->todo); trace_binder_transaction_node_to_ref(t, node, ref); binder_debug(BINDER_DEBUG_TRANSACTION, " node %d u%016llx -> ref %d desc %d\n", node->debug_id, (u64)node->ptr, ref->debug_id, ref->desc); } break; default: return_error = BR_FAILED_REPLY; goto err_bad_object_type; } } if (reply) { BUG_ON(t->buffer->async_transaction != 0); binder_pop_transaction(target_thread, in_reply_to); } else if (!(t->flags & TF_ONE_WAY)) { BUG_ON(t->buffer->async_transaction != 0); t->need_reply = 1; t->from_parent = thread->transaction_stack; thread->transaction_stack = t; } else { BUG_ON(target_node == NULL); BUG_ON(t->buffer->async_transaction != 1); if (target_node->has_async_transaction) { target_list = &target_node->async_todo; target_wait = NULL; } else target_node->has_async_transaction = 1; } t->work.type = BINDER_WORK_TRANSACTION; list_add_tail(&t->work.entry, target_list); tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; list_add_tail(&tcomplete->entry, &thread->todo); if (target_wait) wake_up_interruptible(target_wait); return; ......}
binder_thread_read
binder_thread_read 返回,binder_thread_read 开始了,由于无数据,被wait_event_freezable_exclusive 阻塞。
handler 的身世
在binder 通信中handler 代表了一个Binder 对象,那么这个hander 的真是意义到底是什么呢。上边我们看到了handler出处,看下这个handler 到底从哪里来,代表什么含义。fp 为flat_binder_object 对象, handler 被赋值为binder_refs 对象的desc. ServiceManger服务一定是0,其他的服务在这个基础上+1, 看下这个desc 的计算过程,本进程中的代理对象的 binder_ref 最大值 计数+1, 也就是说本进程的服务计数+1。所以不同的服务,可能有相同的handler。这个handler 为什么不交给ServiceManager 管理呢,所有的服务统一引用计数
ref = binder_get_ref_for_node(target_proc, node);fp->binder = 0;fp->handle = ref->desc;fp->cookie = 0;static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc, struct binder_node *node){ struct rb_node *n; struct rb_node **p = &proc->refs_by_node.rb_node; struct rb_node *parent = NULL; struct binder_ref *ref, *new_ref; ...... new_ref->desc = (node == binder_context_mgr_node) ? 0 : 1; for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) { ref = rb_entry(n, struct binder_ref, rb_node_desc); if (ref->desc > new_ref->desc) break; new_ref->desc = ref->desc + 1; } ...... return new_ref;}
ServiceManger 进程的binder_thread_read
ServiceManagere 对应的驱动的队列被唤醒,binder_thread_read 开始工作。
- binder_thread_read 首先从TODO 队列中取出需要完成的事物,注意是两个,write 的时候写添加了两个。首先取出 BINDER_WORK_TRANSACTION, binder_transaction。
- 然后将binder_transaction 结构体转化为 binder_transaction_data 结构体,这样用户态程序可以访问,cmd 转换。
- 将buffer 内核的地址转化为用户空间地址。依次调用put_user copy_to_user 将相关数据传递到用户空间。
- 取出 BINDER_WORK_TRANSACTION_COMPLETE binder_transaction。将数据传递到用户空间
static int binder_thread_read(struct binder_proc *proc, struct binder_thread *thread, binder_uintptr_t binder_buffer, size_t size, binder_size_t *consumed, int non_block){ void __user *buffer = (void __user *)(uintptr_t)binder_buffer; void __user *ptr = buffer + *consumed; void __user *end = buffer + size; int ret = 0; int wait_for_proc_work; if (*consumed == 0) { if (put_user(BR_NOOP, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); }retry: wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo); thread->looper |= BINDER_LOOPER_STATE_WAITING; if (wait_for_proc_work) proc->ready_threads++; binder_unlock(__func__); ...... binder_lock(__func__); if (wait_for_proc_work) proc->ready_threads--; thread->looper &= ~BINDER_LOOPER_STATE_WAITING; if (ret) return ret; while (1) { uint32_t cmd; struct binder_transaction_data tr; struct binder_work *w; struct binder_transaction *t = NULL; 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 { /* no data added */ if (ptr - buffer == 4 && !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) goto retry; break; } if (end - ptr < sizeof(tr) + 4) break; switch (w->type) { case BINDER_WORK_TRANSACTION: { t = container_of(w, struct binder_transaction, work); } break; case BINDER_WORK_TRANSACTION_COMPLETE: { cmd = BR_TRANSACTION_COMPLETE; if (put_user(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; if (!t) continue; BUG_ON(t->buffer == NULL); if (t->buffer->target_node) { struct binder_node *target_node = t->buffer->target_node; 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 { tr.target.ptr = 0; tr.cookie = 0; cmd = BR_REPLY; } tr.code = t->code; tr.flags = t->flags; tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid); if (t->from) { struct task_struct *sender = t->from->proc->tsk; tr.sender_pid = task_tgid_nr_ns(sender, task_active_pid_ns(current)); } else { tr.sender_pid = 0; } tr.data_size = t->buffer->data_size; tr.offsets_size = t->buffer->offsets_size; tr.data.ptr.buffer = (binder_uintptr_t)( (uintptr_t)t->buffer->data + proc->user_buffer_offset); tr.data.ptr.offsets = tr.data.ptr.buffer + ALIGN(t->buffer->data_size, sizeof(void *)); if (put_user(cmd, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); if (copy_to_user(ptr, &tr, sizeof(tr))) return -EFAULT; ptr += sizeof(tr); trace_binder_transaction_received(t); binder_stat_br(proc, thread, cmd); 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); binder_stats_deleted(BINDER_STAT_TRANSACTION); } break; } return 0;}
ServiceManager 的ioctl 阻塞返回。
mmap 的设计和binder 效率的讨论
有这么一种说法是 mmap 映射了用户空间和内核空间的地址,减少了一次从内核空间到用户地址空间的copy, 所以提高了效率。从上面的分析看
1. 从用户态到内核, 发生了4次拷贝
2. 从内核到用户态, 发生了一共 2 +2 =4 次copy, 仅仅减少了1次。
所以 binder 驱动的效率提升不在这里。为什么要使用mmap, 从binder 的设计上来看,读写操作没有谁用标准的linux 接口read和write, 那么从内核传递数据到用户态如何完成呢,ioctl 的第三个参数只能是从用户到内核传递地址。mmap 的设计是为了解决从内核传递数据到用户这一功能。
那为什么Binder 效率比传统的IPC要高效呢。
内核资源竞争 在传统的IPC中,read 和 write 操作都需要对内核中的 读写的buffer 进行资源竞争,解决并发方位的问题,一方面加锁操作消耗了一定的资源,另一方面,各个操作需要排队等待。
Binder 为了解决内内核中 buffer 的竞争,采用了为每个线程单独分配空间,避免了内核态的等待。这样效率就提高了,但是内核态不需要等待,用户空间的线程不一定能够及时的操作,所以binder 在用户态又引用了多线程支持。从内核态和用户态解决了资源访问等待的问题。这是binder 效率高的真正原因。
- Binder框架 -- 用户空间和驱动的交互
- Binder 框架 -- binder 用户空间框架
- BQ27501驱动和用户空间交互数据
- ioctl----内核和用户空间的交互
- binder驱动和内核交互笔记
- Binder框架 -- Binder 驱动
- linux内核空间和用户空间的区别及交互
- binder驱动-交互时的传输实现
- 用户空间的驱动
- Linux驱动:用户空间,内核空间内存交互测试
- V4L2用户空间和kernel层driver的交互过程
- V4L2用户空间和kernel层driver的交互过程
- V4L2用户空间和kernel层driver的交互过程
- V4L2用户空间和kernel层driver的交互过程
- V4L2用户空间和kernel层driver的交互过程
- V4L2用户空间和kernel层driver的交互过程
- V4L2用户空间和kernel层driver的交互过程
- V4L2用户空间和kernel层driver的交互过程
- MongoDB基本概念
- Binder框架 -- Binder 驱动
- test
- 【Day59】MySQL数据库性能优化之一(缓存参数优化)
- windows杀线程,专业解决8080,8009端口占用20年
- Binder框架 -- 用户空间和驱动的交互
- PullToRefresh 修改下拉动画 以及 轮播实现点击停止 和PullToRefreListView 的复杂布局
- Openssl 1.1.0C VS2013编译
- begin
- 深入理解 Spring 事务原理
- Solr 5.5.3配置ansj5.0.3
- C++创建多级目录及判断目录是否存在的方法
- 坦诚相待,学会感恩
- mui自定义图标