IPCThreadState 详解
IPC是Android的精华,找了个时间,详细分析下IPCThreadState 的组织架构及其功能
初始化:用这个方法,我们可以在每个线程里取得IPCThreadState
IPCThreadState* IPCThreadState::self()
{
// 全局标志位,代表是否TSD已经建立
if (gHaveTLS) {
restart:
const pthread_key_t k = gTLS;
// 获取线程私有数据,即IPCThreadState本身,没有则创建。 保证每个线程都有相对应的IPCThreadState
IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
if (st) return st;
return new IPCThreadState;
}
if (gShutdown) return NULL;
pthread_mutex_lock(&gTLSMutex);
if (!gHaveTLS) {
// 如果线程私有数据没建立,则创建一个。
if (pthread_key_create(&gTLS, threadDestructor) != 0) {
pthread_mutex_unlock(&gTLSMutex);
return NULL;
}
gHaveTLS = true;
}
pthread_mutex_unlock(&gTLSMutex);
// 创建完,恢复到启始逻辑
goto restart;
}
看了它的构造函数,明显,这就是为每个线程邦定了个结构体。
然后,就是IPCThreadState的构造函数,非常简单简洁
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()),
mMyThreadId(androidGetTid()),
mStrictModePolicy(0),
mLastTransactionBinderFlags(0)
{
pthread_setspecific(gTLS, this);
clearCaller();
mIn.setDataCapacity(256);
mOut.setDataCapacity(256);
}
可以看到,mIn和mOut 直接设了个容量,
接下来总结下各个功能
1. freeBuffer: 用于释放空间。 2个用处: 1) buffer.ipcSetDataReference 作为释放函数传入 2) BR_REPLY: if no reply 作为释放函数传入时,分别在传入和传出有体现BR_TRANSACTION and BR_REPLY
if (parcel != NULL) parcel->closeFileDescriptors(); 检查是否为空, noreply(2) 情况此值为空。 传入(1)此值为this, 当前parsel. 遍寻parsel结构体关闭所有 BINDER_TYPE_FD类型file .
IPCThreadState* state = self(); 得到当前线程的IPCThreadState, 其实也就是当前结构体
state->mOut.writeInt32(BC_FREE_BUFFER); state->mOut.writeInt32((int32_t)data); 通过read write 写命令到驱动层,释放结构体。
2. threadDestructor 用于毁掉这个线程,应该包括各种释放
self->flushCommands(); ==〉 // 检查是否打开了底层驱动,如果没,直接返回 if (mProcess->mDriverFD <= 0) return; // 与底层交流,试图关闭驱动
talkWithDriver(false); 细节以后分析,不需要接受命令,但会把当前须传出的命令传下去
#if defined(HAVE_ANDROID_OS) ioctl(self->mProcess->mDriverFD, BINDER_THREAD_EXIT, 0);
#endif 直接操纵底层驱动,从驱动层删除了这个线程
3. executeCommand 执行各种命令 命令基本来自驱动层
case BR_ERROR: result = mIn.readInt32();
break; 从下层读一个32bit, 好象是下面发了个错误信号,上层去读为什么出问题
case BR_OK: break; 蛋疼。。
case BR_ACQUIRE: refs = (RefBase::weakref_type*)mIn.readInt32(); obj = (BBinder*)mIn.readInt32(); 从底层拿这两个结构体上来。 obj->incStrong(mProcess.get());
真实工作 mOut.writeInt32(BC_ACQUIRE_DONE); mOut.writeInt32((int32_t)refs);
mOut.writeInt32((int32_t)obj); ref啥也没干,又放回去了 什么时候会调这个函数呢。。。似乎是一次binder函数结束时搞定 case BR_RELEASE: mPendingStrongDerefs.push(obj); mPendingWeakDerefs.push(refs) 直接加到这个队列里,应该有个什么地方释放巴
BR_INCREFS: refs->incWeak(mProcess.get()); 可以看到refs终于干活了。记得传说,obj 讲的是服务,也就是本地进程,ref是客户端进程, 待严正
case BR_DECREFS: mPendingWeakDerefs.push(refs) 另外一个队列
case BR_ATTEMPT_ACQUIRE const bool success = refs->attemptIncStrong(mProcess.get()); 这玩干什么的阿。。没找到谁调用的阿。goolge下说没实现。。 这个是用来变弱为强的阿,设计者在想什么啊
case BR_TRANSACTION: 正题~~
binder_transaction_data tr; result = mIn.read(&tr, sizeof(tr)); 从下面读上来的。这是要操作的数据马?
Parcel buffer; buffer.ipcSetDataReference(
reinterpret_cast调用者进程号线程号被记下来的,竟然是实名制。(tr.data.ptr.buffer), tr.data_size, reinterpret_cast(tr.data.ptr.offsets), tr.offsets_size/sizeof(size_t), freeBuffer, this); 构造了个parcel, 然后传进来的数据被按进去了
const pid_t origPid = mCallingPid;
const uid_t origUid = mCallingUid;
mCallingPid = tr.sender_pid;
mCallingUid = tr.sender_euid;
int curPrio = getpriority(PRIO_PROCESS, mMyThreadId); if (gDisableBackgroundScheduling) {
if (curPrio > ANDROID_PRIORITY_NORMAL) {
// We have inherited a reduced priority from the caller, but do not
// want to run in that state in this process. The driver set our
// priority already (though not our scheduling class), so bounce
// it back to the default before invoking the transaction.
setpriority(PRIO_PROCESS, mMyThreadId, ANDROID_PRIORITY_NORMAL);
}
} else {
if (curPrio >= ANDROID_PRIORITY_BACKGROUND) {
// We want to use the inherited priority from the caller.
// Ensure this thread is in the background scheduling class,
// since the driver won't modify scheduling classes for us.
// The scheduling group is reset to default by the caller
// once this method returns after the transaction is complete.
androidSetThreadSchedulingGroup(mMyThreadId,
ANDROID_TGROUP_BG_NONINTERACT);
}
} 如果启动了线程调控机制, androidSetThreadSchedulingGroup,否则单独设一个线程的优先级。 这里会涉及Linux 的调度概念,比如CFS(Completely Fair Scheduler) Parcel reply; if (tr.target.ptr) {
sp
const status_t error = b->transact(tr.code, buffer, &reply, tr.flags);
if (error < NO_ERROR) reply.setError(error);
如果这个BBinder包含了一个可用的object, 即服务存在,则调用服务的transact
} else {
const status_t error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
if (error < NO_ERROR) reply.setError(error);
}
sendReply(reply, 0);
} else {
LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);
} 这个好理解,transact 的返回值被封装在reply再传回driver.
case BR_DEAD_BINDER BpBinder *proxy = (BpBinder*)mIn.readInt32(); proxy->sendObituary();
mOut.writeInt32(BC_DEAD_BINDER_DONE);
mOut.writeInt32((int32_t)proxy); 出了个新概念,BpBinder~~ 传说他是跟BBinder对应的,不过这里他代表着一个Binder, 可惜这里表示的是个死binder 然后这个家伙发出讣告,通知所有的注册的deathnotifier~~~ 基本上是清理工作
case BR_CLEAR_DEATH_NOTIFICATION_DONE: 当前task取消订阅binder死亡通知成功之后的得到回复命令-- google said 还没看到相关细节 case BR_SPAWN_LOOPER: mProcess->spawnPooledThread(false); 这个函数是用来注册用的阿,那参数如果是true 就是主函数,否则就是加入循环。 具体用法待分析
还有几个小case,没意义,不分析了
4. 往上看是个将就的表达,这 the_context_object全局变量表示的是总service,可笑的是竟然找不到谁调了这个setTheContextObject 如果不是这个方法被弃用了,就是什么特殊手段在某些地方定义的。。。 等以后碰到了再说吧
sp
void setTheContextObject(sp
{
the_context_object = obj;
}
5. writeTransactionData 这个函数很重要啊,贯通与驱动的数据通信
如果没有问题,数据打包封装 if (err == NO_ERROR) {
tr.data_size = data.ipcDataSize();
tr.data.ptr.buffer = data.ipcData();
tr.offsets_size = data.ipcObjectsCount()*sizeof(size_t);
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 = statusBuffer;
tr.offsets_size = 0;
tr.data.ptr.offsets = NULL;
} else {
return (mLastError = err);
}
最后是写下去 mOut.writeInt32(cmd);
mOut.write(&tr, sizeof(tr));
6. talkWithDriver 这个估计是这个文件里最重要的地方了
binder_write_read bwr; 负责通信的载体
const bool needRead = mIn.dataPosition() >= mIn.dataSize(); dataposition 是数据位置,size是终止位置,当没有数据时,他们应该相等
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
outavail 由2个因素决定,doreceive 是传进来的,不接受的时候,可以有数据输出 needread, 是上个函数算出来的,读buffer为空,没有数据在读进来
当地英文解释说,写操作力图避免与读操作同时发生
bwr.write_size = outAvail;
bwr.write_buffer = (long unsigned int)mOut.data(); 写入的一次付值,如果write_size为0, 则不会有写的实际操作发生
if (doReceive && needRead) {
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (long unsigned int)mIn.data();
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
} 如果需要读,且读buffer确实为空,则设置准备读
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR; 如果读写皆为0, 则返回
bwr.write_consumed = 0; ` bwr.read_consumed = 0;
status_t err;
do {
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
} while (err == -EINTR); 直接操作ioctl 与驱动层通信。
if (err >= NO_ERROR) {if (bwr.write_consumed > 0) {
if (bwr.write_consumed < (ssize_t)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);
} 如果读写出错,这里做了下错误处理。没什么内容。
说道这里,就是说所有mOut写下去的数据都需要talkwithdriver 真正的写下去, mIn也是调完talk才可以用的
7.waitForResponse 用于传数据下去后,等待底层回复
while (1) 程序起手死循环,等确定条件发生时break
if ((err=talkWithDriver()) < NO_ERROR) break; err = mIn.errorCheck();
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue; 直接与底层交互,有错误推出,没数据传入继续等待
cmd = mIn.readInt32(); 成功读入数据
switch (cmd) { 根据不同返回的命令进行相关工作 case BR_TRANSACTION_COMPLETE: if (!reply && !acquireResult) goto finish; 这个逻辑应该有上下文,当传入的两个参数皆为空才返回。应该有些条件约束。不过他表达的是transaction 完成
case BR_DEAD_REPLY: err = DEAD_OBJECT;
goto finish; 返回错误码,应该是服务线程已经不存在了
case BR_FAILED_REPLY:
err = FAILED_TRANSACTION;
返回错误码,传输失败
case BR_ACQUIRE_RESULT: { LOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT"); const int32_t result = mIn.readInt32(); if (!acquireResult) continue; *acquireResult = result ? NO_ERROR : INVALID_OPERATION; } 返回结果,暂时搞不懂下层在什么情况下返回这个,难道是某种状态查询?
case BR_REPLY:
{
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) {
reply->ipcSetDataReference(
reinterpret_cast(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast(tr.data.ptr.offsets),
tr.offsets_size/sizeof(size_t),
freeBuffer, this);
需要搞明白这是要干什么,不满足的情况都释放掉
} else {
err = *static_cast(tr.data.ptr.buffer);
freeBuffer(NULL,
reinterpret_cast(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast(tr.data.ptr.offsets),
tr.offsets_size/sizeof(size_t), this);
}
} else {
freeBuffer(NULL,
reinterpret_cast(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast(tr.data.ptr.offsets),
tr.offsets_size/sizeof(size_t), this);
continue;
}
}
goto finish;
这个要反过来再讲一讲
8. sendReply 这个函数用在BR_TRANSACTION, 当服务线程工作时,错误值,打包返回driver, 最后到client thread
err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer);
if (err < NO_ERROR) return err;
return waitForResponse(NULL, NULL);
9. clearDeathNotification/requestDeathNotification 相当于某种程度的注册,应该是binder死亡后的相关处理
10. transact
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL); 着要是调到这个函数。前面已经分析过 他增加了下对wait respond 的各种等待 11. joinThreadPool 这是这个线程的主逻辑 网上有很多文章分析这个
mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER); 通过BC_ENTER_LOOPER 或者 BC_REGISTER_LOOPER确定是服务线程还是各个services.
androidSetThreadSchedulingGroup(mMyThreadId, ANDROID_TGROUP_DEFAULT); 设定策略层,线程调度策略
如果没有数据传入,清空那obj和ref对列 // When we've cleared the incoming command queue, process any pending derefs
if (mIn.dataPosition() >= mIn.dataSize()) {
size_t numPending = mPendingWeakDerefs.size();
if (numPending > 0) {
for (size_t i = 0; i < numPending; i++) {
RefBase::weakref_type* refs = mPendingWeakDerefs[i];
refs->decWeak(mProcess.get());
}
mPendingWeakDerefs.clear();
}
numPending = mPendingStrongDerefs.size();
if (numPending > 0) {
for (size_t i = 0; i < numPending; i++) {
BBinder* obj = mPendingStrongDerefs[i];
obj->decStrong(mProcess.get());
}
mPendingStrongDerefs.clear();
}
}
与驱动交互
result = talkWithDriver();
如果有数据传入,执行相关命令
if (result >= NO_ERROR) {
size_t IN = mIn.dataAvail();
if (IN < sizeof(int32_t)) continue;
cmd = mIn.readInt32();
IF_LOG_COMMANDS() {
alog << "Processing top-level Command: "
<< getReturnString(cmd) << endl;
}
result = executeCommand(cmd);
}
基本就是这个逻辑反复循环。。
到这,这里的基本内容就算理清了
更多相关文章
- delphi xe5 android 开发数据访问手机端(二)
- android中 异步消息处理机制及Handler
- Android(安卓)连接Mysql数据库步骤(新手步骤)
- android 导出带数据库文件的APK
- Android之使用Http协议实现文件上传功能
- 说说Android中的Settings.Secure
- 包管理系统分析 包管理脚本 /system/bin/pm 解析:
- Android(安卓)面试题总结(一)
- android远程服务