未加星标

BootAnimation分析(一)

字体大小 | |
[系统(linux) 所属分类 系统(linux) | 发布者 店小二04 | 时间 2016 | 作者 红领巾 ] 0人收藏点击收藏

BootAnimation分析(一)

android开机有很多画面显示,从boot到kernel,再到最后的bootAnimation,每一部分显示都用到了不同的方法。一直都感觉图像显示相关的内容比较有意思,加上最近有做跟BootAnimation相关的task,就分析一下BootAnimation的流程用于记录,非常短小精悍的一段程序,但是涉及到的知识点却很多:opengl&egl接口使用,surfaceflinger,property service,binder以及其自身的业务逻辑。

bootanimation作为一个bin档,是由init进程创建的,main函数很简单:

int main(int argc, char** argv) { #if defined(HAVE_PTHREADS) setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY); #endif char value[PROPERTY_VALUE_MAX]; property_get("debug.sf.nobootanimation", value, "0"); int noBootAnimation = atoi(value); ALOGI_IF(noBootAnimation, "boot animation disabled"); if (!noBootAnimation) { sp<ProcessState> proc(ProcessState::self()); ProcessState::self()->startThreadPool(); // create the boot animation object sp<BootAnimation> boot = new BootAnimation(); IPCThreadState::self()->joinThreadPool(); } return 0; }

其中有两个操作:1.ProcessState::self()->startThreadPool()

2.IPCThreadState::self()->joinThreadPool()

在android的很多native的程序中都会出现他俩,就谈下个人对这两个函数的理解吧:这两个类是定义在Framework的native的binder的lib中的,也就说这两个类是涉及到binder通信的工具类。这里都是通过self静态方法获取对象,也就是都是单例模式,ProcessState是整个进程只存在一个对象;IPCThreadState则是线程相关的,是一个线程局部静态变量。

void ProcessState::startThreadPool() { AutoMutex _l(mLock); if (!mThreadPoolStarted) { mThreadPoolStarted = true; spawnPooledThread(true);//产生<池子线程> } } void ProcessState::spawnPooledThread(bool isMain) { if (mThreadPoolStarted) { String8 name = makeBinderThreadName(); ALOGV("Spawning new pooled thread, name=%s\n", name.string()); sp<Thread> t = new PoolThread(isMain); t->run(name.string()); } } class PoolThread : public Thread { public: PoolThread(bool isMain) : mIsMain(isMain) { } protected: virtual bool threadLoop() { IPCThreadState::self()->joinThreadPool(mIsMain); return false; } const bool mIsMain; };

注意前面传入的参数isMain都是true,在threadLoop中调用了线程局部变量的joinThreadPool且传入参数是true,然后return false,表明这个线程只会执行一次就结束掉。但是如果joinThreadPool退不出来的话,这个线程也是结束不了的。

其实到现在也还不知道startThreadPool到底做了啥!

void IPCThreadState::joinThreadPool(bool isMain)//这个函数是带默认参数为true的 { LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid()); mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER); // This thread may have been spawned by a thread that was in the background // scheduling group, so first we will make sure it is in the foreground // one to avoid performing an initial transaction in the background. set_sched_policy(mMyThreadId, SP_FOREGROUND); status_t result; do { processPendingDerefs(); // now get the next command to be processed, waiting if necessary result = getAndExecuteCommand();//主要处理函数 if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) { ALOGE("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting", mProcess->mDriverFD, result); abort(); } // Let this thread exit the thread pool if it is no longer // needed and it is not the main process thread. if(result == TIMED_OUT && !isMain) { break; } } while (result != -ECONNREFUSED && result != -EBADF);//只要处理的结果不属于这两类就继续去从driver等待事件 //这种先往mOut里写东西,然后调用talkwithdriver就是在做IPC通信 mOut.writeInt32(BC_EXIT_LOOPER); talkWithDriver(false); }

然后转向核心函数:getAndExecuteCommand()

status_t IPCThreadState::getAndExecuteCommand() { status_t result; int32_t cmd; /** step1、从driver读出cmd */ result = talkWithDriver();//这个函数其实出镜率挺高的,dump进程状态的时候,经常有这个函数出现在backtrace中。默认参数是true if (result >= NO_ERROR) { size_t IN = mIn.dataAvail(); if (IN < sizeof(int32_t)) return result; cmd = mIn.readInt32(); IF_LOG_COMMANDS() { alog << "Processing top-level Command: " << getReturnString(cmd) << endl; } pthread_mutex_lock(&mProcess->mThreadCountLock); mProcess->mExecutingThreadsCount++;//增加用于在处理binder的线程的数量 pthread_mutex_unlock(&mProcess->mThreadCountLock); /** step2、执行command,这里感觉就是针对service的进程的使用 */ result = executeCommand(cmd); pthread_mutex_lock(&mProcess->mThreadCountLock); mProcess->mExecutingThreadsCount--; pthread_cond_broadcast(&mProcess->mThreadCountDecrement); pthread_mutex_unlock(&mProcess->mThreadCountLock); // After executing the command, ensure that the thread is returned to the // foreground cgroup before rejoining the pool. The driver takes care of // restoring the priority, but doesn't do anything with cgroups so we // need to take care of that here in userspace. Note that we do make // sure to go in the foreground after executing a transaction, but // there are other callbacks into user code that could have changed // our group so we want to make absolutely sure it is put back. set_sched_policy(mMyThreadId, SP_FOREGROUND); } return result; }

接下来查看talkwithdriver是怎么和driver交互的:

这个binder_write_read是bionic库中定义的一个结构,用于记录binder的读写的状态。mIn和mOut代表的是进这个线程的数据和出这个线程的数据,即从binder收到的数据和需要往外写的数据,这两个parcel也应该是线程的局部变量。

struct binder_write_read { 61 binder_size_t write_size; 62 binder_size_t write_consumed; 63/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 64 binder_uintptr_t write_buffer; 65 binder_size_t read_size; 66 binder_size_t read_consumed; 67 binder_uintptr_t read_buffer; 68/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 69}; //step1、这个函数实际上是能够同时读取和写入的,也就是同时首发数据,只要doreceive设为true就会强制性收数据 status_t IPCThreadState::talkWithDriver(bool doReceive) { if (mProcess->mDriverFD <= 0) { return -EBADF; } binder_write_read bwr;//这个数据会通过ioctl交给driver使用 // Is the read buffer empty? const bool needRead = mIn.dataPosition() >= mIn.dataSize();//表明mIn这个parcel已经被读完了 // We don't want to write anything if we are still reading // from data left in the input buffer and the caller // has requested to read the next data. 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(HAVE_ANDROID_OS) //通过IOCTL跟binder的driver交互,ioctl之后会修改write_consumed和read_consumed两个变量 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); //根据从driver返回的数据来更新mIn和mOut的dataposition, 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; }

step2:执行cmd,executeCommand函数处理的case很多,因为binder driver传上来的cmd有很多种,这里就选择其中一种也是最主要的也就是最常用的BR_TRANSACTION

case BR_TRANSACTION: { binder_transaction_data tr; result = mIn.read(&tr, sizeof(tr));//将传进来的parcel读进tr数据中 ALOG_ASSERT(result == NO_ERROR, "Not enough command data for brTRANSACTION"); if (result != NO_ERROR) break; Parcel buffer;//然后将tr转入到parcel中 buffer.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), CodeSecfer, this); const pid_t origPid = mCallingPid; const uid_t origUid = mCallingUid; const int32_t origStrictModePolicy = mStrictModePolicy; const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags; //这里可以看到通过binder驱动是能够得到调用该数据的pid数据的,也难怪Binder的API中有获取pid这类的API mCallingPid = tr.sender_pid; mCallingUid = tr.sender_euid; mLastTransactionBinderFlags = tr.flags; 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. set_sched_policy(mMyThreadId, SP_BACKGROUND); } } //ALOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid); Parcel reply;//这个是需要回传给client端的数据 status_t error; IF_LOG_TRANSACTIONS() { TextOutput::Bundle _b(alog); alog << "BR_TRANSACTION thr " << (void*)pthread_self() << " / obj " << tr.target.ptr << " / code " << TypeCode(tr.code) << ": " << indent << buffer << dedent << endl << "Data addr = " << reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer) << ", offsets addr=" << reinterpret_cast<const size_t*>(tr.data.ptr.offsets) << endl; } /***********************这部分的BBinder来源还不明*****************************/ if (tr.target.ptr) { sp<BBinder> b((BBinder*)tr.cookie); //调用BBinder的transact去处理这个 error = b->transact(tr.code, buffer, &reply, tr.flags); } else { error = the_context_object->transact(tr.code, buffer, &reply, tr.flags); } /*************************************************************************/ //ALOGI("<<<< TRANSACT from pid %d restore pid %d uid %d\n", // mCallingPid, origPid, origUid); if ((tr.flags & TF_ONE_WAY) == 0) { LOG_ONEWAY("Sending reply to %d!", mCallingPid); if (error < NO_ERROR) reply.setError(error); //返回reply回去 sendReply(reply, 0); } else { LOG_ONEWAY("NOT sending reply to %d!", mCallingPid); } mCallingPid = origPid; mCallingUid = origUid; mStrictModePolicy = origStrictModePolicy; mLastTransactionBinderFlags = origTransactionBinderFlags; IF_LOG_TRANSACTIONS() { TextOutput::Bundle _b(alog); alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj " << tr.target.ptr << ": " << indent << reply << dedent << endl; } } break;

到此就知道ProcessState::self()->startThreadPool()的实作就是创建开启一个池线程,然后这个池线程去talkwithdriver,然后executecmd。

接下来的main函数又调用了一次IPCThreadState::self()->joinThreadPool(),将这个主线程也进入到了talkwithdriver的状态。

看了上面的main函数中跟binder线程相关的操作,现在有三个疑问:

1、在IPC的C/S架构中,针对client端的请求会同时存在多个,那么S端是怎么开出来多个thread去处理的呢?

答:是通过binder driver往service发送BR_SPAWN_LOOPER cmd,然后线程去调用mProcess->spawnPooledThread(false)来开辟新的线程的。

case BR_SPAWN_LOOPER: mProcess->spawnPooledThread(false); break;

2、在收到transaction的命令之后,获得的BBinder是从哪里来?

答:这个BBinder肯定是某个BnService,但是问题就出在,是在什么地方设置这个鬼的(未解待续,如果有了解的希望能指点一下)

3、前面看到这两个操作是为了接收binder driver数据,有可能是作为service的用途,但是tmd bootanimation没有要作为service的意思,他只是作为SF的client,为毛还要做这两步呢?去掉不行么?

答:如果作为一个客户端的话,传输数据本质是调用IPCThreadState的transact函数,其中的套路:先往mOut中写数据--->talkwithdriver来发送数据,这个过程中必然要求binder文件已经被打开之类,但是在这个流程中肯定会打开binder文件,不论你之前是否调用了ProcessState::self()->startThreadPool()或者IPCThreadState::self()->joinThreadPool()。所以我的意见是删掉也还是能跑起来(这个等有编译环境之后再验证删掉功能是否正常)。

到此就为一个native的应用在main函数中开启binder线程的功能做了一定分析,按这个讨论就能更好地去看其他native的程序了,如servicemanager、surfaceflinger等。但可能存在不完善或者理解不到位的地方,希望得到批评指正,感谢。

本文系统(linux)相关术语:linux系统 鸟哥的linux私房菜 linux命令大全 linux操作系统

主题: 数据TIGISUTCTIAUFUAUTUC其实
分页:12
转载请注明
本文标题:BootAnimation分析(一)
本站链接:http://www.codesec.net/view/483016.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 系统(linux) | 评论(0) | 阅读(29)