Android系统Surface机制的SurfaceFlinger服务的线程模型分析剖析.docx
《Android系统Surface机制的SurfaceFlinger服务的线程模型分析剖析.docx》由会员分享,可在线阅读,更多相关《Android系统Surface机制的SurfaceFlinger服务的线程模型分析剖析.docx(26页珍藏版)》请在冰点文库上搜索。
Android系统Surface机制的SurfaceFlinger服务的线程模型分析剖析
Android系统Surface机制的SurfaceFlinger服务的线程模型分析
在前面两篇文章中,我们分析了SurfaceFlinger服务的启动过程以及SurfaceFlinger服务初始化硬件帧缓冲区的过程。
从这两个过程可以知道,SurfaceFlinger服务在启动的过程中,一共涉及到了三种类型的线程,它们分别是Binder线程、UI渲染线程和控制台事件监控线程。
在本文中,我们就将详细分SurfaceFlinger服务的线程模型,即上述三种类型的线程是如何运行和交互的。
从一文可以知道,SurfaceFlinger服务是在System进程的主线程中启动的。
System进程的主线程在启动系统的关键服务之前,会先启动一个Binder线程池。
这样运行在System进程中的系统关系服务就可以与其它进程执行Binder进程间通信了。
SurfaceFlinger服务虽然是由System进程的主线程来负责启动的,但是最终它会运行在一个独立的线程中。
我们将这个独立的线程称为UI渲染线程,因为它负责渲染系统的UI。
从一文可以知道,SurfaceFlinger服务的UI渲染线程的启动的时候,会对系统的硬件帧缓冲区进行初始化。
在初始化的过程,又会创建另外一个线程来监控硬件帧缓冲区的睡眠/唤醒状态切换事件。
为了方便描述,我们这个线程称称为控制台事件监控线程。
上述的三种类型的线程的启动顺序,可以通过图1来描述,如下所示:
从图1就可以清楚地看到,System进程的主线程负责启动Binder线程池,以及UI渲染线程,而UI渲染线程又负责启动控制台事件监控线程。
在这三种类型的线程中,UI渲染线程是主角,Binder线程和控制台事件监控线程是配角。
Binder线程池是为了让其它进程,例如Android应用程序进程,可以与SurfaceFlinger服务进行Binder进程间通信的,有一部分通信所执行的操作便是让UI渲染线程更新系统的UI。
控制台事件监控线程是为了监控硬件帧缓冲区的睡眠/唤醒状态切换事件的。
一旦硬件帧缓冲区要进入睡眠或者唤醒状态,控制台事件监控线程都需要通知UI渲染线程,以便UI渲染线程可以执行关闭或者启动显示屏的操作。
接下来,为了弄清楚SurfaceFlinger服务的线程模型,我们就首先简要分析UI渲染线程的运行模型,接着再分析Binder线程与UI渲染线程的交互过程,最后分析控制台事件监控线程与UI渲染线程的交互过程。
1.UI渲染线程的运行模型
在前面一文中提到,SurfaceFlinger服务的UI渲染线程有一个消息队列。
当消息队列为空时,SurfaceFlinger服务的UI渲染线程就会进入睡眠等待状态。
一旦SurfaceFlinger服务的Binder线程接收到其它进程发送过来的渲染UI的请求时,它就会往SurfaceFlinger服务的UI渲染线程的消息队列中发送一个消息,以便可以将SurfaceFlinger服务的UI渲染线程唤醒起来执行渲染的操作。
同样,一旦SurfaceFlinger服务的控制台事件监控线程发现硬件帧缓冲区即将要进入睡眠或者唤醒状态时,它就会往SurfaceFlinger服务的UI渲染线程的消息队列中发送一个消息,以便SurfaceFlinger服务的UI渲染线程可以执行冻结或者解冻显示屏的操作。
从前面一文又可以知道,SurfaceFlinger服务的UI渲染线程是以SurfaceFlinger类的成员函数threadLoop为线程执行体的,即SurfaceFlinger服务的UI渲染线程会不断地循环执行SurfaceFlinger类的成员函数threadLoop。
接下来,我们就通过SurfaceFlinger类的成员函数threadLoop的实现来分析SurfaceFlinger服务的UI渲染线程的运行模型,如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
boolSurfaceFlinger:
:
threadLoop()
{
waitForEvent();
//checkfortransactions
if(UNLIKELY(mConsoleSignals)){
handleConsoleEvents();
}
if(LIKELY(mTransactionCount==0)){
//ifwe'reinaglobaltransaction,don'tdoanything.
constuint32_tmask=eTransactionNeeded|eTraversalNeeded;
uint32_ttransactionFlags=getTransactionFlags(mask);
if(LIKELY(transactionFlags)){
handleTransaction(transactionFlags);
}
}
//postsurfaces(ifneeded)
handlePageFlip();
constDisplayHardware&hw(graphicPlane(0).displayHardware());
if(LIKELY(hw.canDraw()&&!
isFrozen())){
#ifdefUSE_COMPOSITION_BYPASS
if(handleBypassLayer()){
unlockClients();
returntrue;
}
#endif
//repainttheframebuffer(ifneeded)
constintindex=hw.getCurrentBufferIndex();
GraphicLog&logger(GraphicLog:
:
getInstance());
logger.log(GraphicLog:
:
SF_REPAINT,index);
handleRepaint();
//informtheh/wthatwe'redonecompositing
logger.log(GraphicLog:
:
SF_COMPOSITION_COMPLETE,index);
positionComplete();
logger.log(GraphicLog:
:
SF_SWAP_BUFFERS,index);
postFramebuffer();
logger.log(GraphicLog:
:
SF_REPAINT_DONE,index);
}else{
//pretendwedidthepost
positionComplete();
usleep(16667);//60fpsperiod
}
returntrue;
}
这个函数定义在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中。
从前面一文可以知道,SurfaceFlinger类的成员函数threadLoop的工作过程如下所示:
1.调用SurfaceFlinger类的成员函数waitForEvent中检查SurfaceFlinger服务的UI渲染线程的消息队列是否为空。
如果不为空,那么就会马上返回来执行其它的操作,否则的话,SurfaceFlinger服务的UI渲染线程就会进入睡眠等状态,直到被SurfaceFlinger服务的Binder线程或者控制台事件监控线程唤醒为止。
2.当SurfaceFlinger服务的UI渲染线程被控制台事件监控线程唤醒时,SurfaceFlinger类的成员变量mConsoleSignals的值就会不等于0。
在这种情况下,SurfaceFlinger类的成员函数threadLoop就会调用另外一个成员函数handleConsoleEvents来处理控制台事件。
后面在分析SurfaceFlinger服务的UI渲染线程和控制台事件监控线程的交互过程时,我们再分析这个成员函数的实现。
3.SurfaceFlinger类的成员变量mTransactionCount用来描述SurfaceFlinger服务是否正在执行事务。
如果SurfaceFlinger服务正在执行事务,那么SurfaceFlinger类的成员变量mTransactionCount的值就会大于0。
怎么理解SurfaceFlinger服务所执行的事务是什么呢?
这些事务是用来处理系统的显示属性的。
这些属性划分为两种类型。
一种类型是与整个显示屏属性相关的,例如屏幕旋转方向发生了变化,另一外类型是与某一个应用程序的Surface相关的,例如某一个Surface的大小或者Z轴位置发生了变化。
一般来说,每当系统的显示属性发生了变化的时候,SurfaceFlinger服务的UI渲染线程都需要马上刷新系统UI,以便可以反映真实情况。
但是,为了减少屏幕的闪烁,有时候可以将多个属性变化组合成一个事务来刷新系统UI。
例如,我们可以在修改了一个Surface的大小和Z轴位置之后,才要求SurfaceFlinger服务的UI渲染线程去刷新系统UI,这样就可以减少一个刷新系统UI的操作。
因此,只有当SurfaceFlinger类的成员变量mTransactionCount的值的等于0的时候,,SurfaceFlinger类的成员函数threadLoop才会判断系统的显示属性是否发生了变化。
如果发生了变化,那么就会调用另外一个成员函数handleTransaction来进一步处理。
在接下来的一篇文章中分析SurfaceFlinger服务的UI渲染过程时,我们就详细分析这个成员函数的实现。
4.SurfaceFlinger服务的UI渲染线程接下来调用SurfaceFlinger类的成员函数handlePageFlip来通知各个应用程序的Surface将接下来要渲染的图形缓冲区设置为当前激活的图形缓冲区,以便接下来可以渲染到硬件帧缓冲区中去。
我们同样会在接下来的一篇文章中分析SurfaceFlinger服务的UI渲染过程时,详细分析这个成员函数的实现。
5.如果SurfaceFlinger服务的UI渲染线程目前只有一个Surface需要渲染,并且SurfaceFlinger类在编译时,指定了USE_COMPOSITION_BYPASS宏,那么SurfaceFlinger类的成员函数threadLoop就会直接调用另外一个成员函数handleBypassLayer来将这个Surface直接渲染到硬件帧缓冲区中去。
这是一个优化操作,避免执行接下来的Surface合成操作。
6.如果SurfaceFlinger服务的UI渲染线程目前有多个Surface需要渲染,或者SurfaceFlinger类在编译时没指定USE_COMPOSITION_BYPASS宏,那么SurfaceFlinger类的成员函数threadLoop接下来就会调用另外一个成员函数handleRepaint来将各个Surface的图形缓冲区合成起来,以便接下来可以渲染到硬件帧缓冲区中去。
Surface的合成操作比较复杂,因为它涉及到可见性计算等。
我们同样会在接下来的一篇文章中分析SurfaceFlinger服务的UI渲染过程时,详细分析这个成员函数的实现。
7.要渲染的各个Surface的图形缓冲区被合成之后,SurfaceFlinger类的成员函数threadLoop接下来前面获得的用来描述系统主显示屏的DisplayHardware对象hw的成员函数compositionComplete来通知HAL层Gralloc模块中的fb设备,以便这个fb设备可以在Surface合成操作完成时执行一些逻辑。
这一步是可选的,取决于HAL层Gralloc模块中的fb设备是否需要接收这个Surface合成完成通知。
8.上述步骤都执行完成之后,SurfaceFlinger类的成员函数threadLoop最后就可以调用SurfaceFlinger类的成员函数postFramebuffer来将合成后得到的图形缓冲区渲染到硬件帧缓冲区去了,这样就可以将系统的最新UI渲染出来,或者说刷新了系统的UI。
在本小节中,我们只关注第1步的处理过程,即SurfaceFlinger类的成员函数waitForEvent的实现,以便可以了解SurfaceFlinger服务的UI渲染线程是如何围绕它的消息队列来运行的。
SurfaceFlinger类的成员函数waitForEvent的实现如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
voidSurfaceFlinger:
:
waitForEvent()
{
while(true){
nsecs_ttimeout=-1;
constnsecs_tfreezeDisplayTimeout=ms2ns(5000);
if(UNLIKELY(isFrozen())){
//wait5seconds
constnsecs_tnow=systemTime();
if(mFreezeDisplayTime==0){
mFreezeDisplayTime=now;
}
nsecs_twaitTime=freezeDisplayTimeout-(now-mFreezeDisplayTime);
timeout=waitTime>0?
waitTime:
0;
}
spmsg=mEventQueue.waitMessage(timeout);
//seeifwetimedout
if(isFrozen()){
constnsecs_tnow=systemTime();
nsecs_tfrozenTime=(now-mFreezeDisplayTime);
if(frozenTime>=freezeDisplayTimeout){
//wetimedoutandarestillfrozen
LOGW("timeoutexpiredmFreezeDisplay=%d,mFreezeCount=%d",
mFreezeDisplay,mFreezeCount);
mFreezeDisplayTime=0;
mFreezeCount=0;
mFreezeDisplay=false;
}
}
if(msg!
=0){
switch(msg->what){
caseMessageQueue:
:
INVALIDATE:
//invalidatemessage,justreturntothemainloop
return;
}
}
}
}
这个函数定义在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中。
在分析这个函数的实现之前,我们首先了解一下SurfaceFlinger类的三个成员变量mFreezeDisplay、mFreezeDisplayTime和mFreezeCount的含义。
外部进程,例如,应用程序进程,可以请求SurfaceFlinger服务将显示屏冻结,这时候SurfaceFlinger类的成员变量mFreezeDisplay的值就会等于true。
当显示屏被冻结时,SurfaceFlinger服务同时也会记录被冻结的起始时间,记录在SurfaceFlinger类的成员变量mFreezeDisplayTime中。
另一方面,SurfaceFlinger服务在修改某一个Surface的显示属性时,例如,修改它的大小时,如果发现显示屏此时正处于被冻结的状态,这时候就会将SurfaceFlinger类的成员变量mFreezeCount的值增加1,表示这个Surface也需要冻结显示屏。
从SurfaceFlinger类的成员函数threadLoop的实现可以知道,SurfaceFlinger服务都会调用SurfaceFlinger类的另外一个成员函数isFrozen来判断显示屏是否处于冻结状态。
如果是的话,那么SurfaceFlinger服务是不可以执行渲染UI的操作的。
SurfaceFlinger类的成员函数isFrozen的实现如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
classSurfaceFlinger:
publicBinderService,
publicBnSurfaceComposer,
protectedThread
{
......
private:
......
inlineboolisFrozen()const{
return(mFreezeDisplay||mFreezeCount>0)&&mBootFinished;
}
......
};
这个函数定义在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.h中。
SurfaceFlinger类的另外一个成员变量mBootFinished用来表示系统是否已经启动完成的。
从前面一文可以知道,当系统启动完成时,第三个开机画面,即开动动画,就会被停止,同时SurfaceFlinger类的成员变量mBootFinished的值会被设置为true。
从SurfaceFlinger类的成员函数isFrozen的实现也可以看出,只有当系统启动完成之后,显示屏才会有冻结的概念。
由于在系统启动的过程中,显示屏都是依次被三个开机画面独占的,而在独占的期间,不会出现同时去修改显示属性的问题,因此就不需要去冻结显示屏。
由于将显示屏冻结的目的一般是为了修改显示屏的显示属性,例如修改某一个Surface的大小或者修改显示并的旋转方向等,因此,通过SurfaceFlinger类的两个成员变量mFreezeDisplay和mFreezeCount,SurfaceFlinger服务就可以将多个显示属性变化合并在一起去渲染UI,避免单独为每一个显示属性变化执行一次UI渲染操作。
单独为每一个显示属性变化执行一次UI渲染操作会出现什么情况呢?
假如有两个显示属性是同时发生变化的,那么执行两次UI渲染操作就会可能导致冲突,从而造成一些画面上的误差。
理解了SurfaceFlinger类的三个成员变量mFreezeDisplay、mFreezeDisplayTime和mFreezeCount的含义之后,接下来我们就可以分析SurfaceFlinger类的成员函数waitForEvent的实现了。
当消息队列为空时,SurfaceFlinger服务的UI渲染线程每一次进行睡眠等待状态的默认时间被设置为5000毫秒,保存在变量freezeDisplayTimeout中。
但是如果显示屏当前正处于冻结状态,那么这个等待的时间就会从默认值减去已经被冻结的时间。
这样做的目的是避免显示屏长时间被冻结而导致UI不能被渲染,即相当于是将显示屏的最长冻结时间设置为5000毫秒。
最终得到的等待时间就保存在变量timeout中,接下来SurfaceFlinger类的成员函数waitForEvent就会调用成员变量mEventQueue所描述的一个消息队列的成员函数waitMessage来检查是否有新的消息需要处理。
如果没有,那么SurfaceFlinger服务的UI渲染线程就会进入到睡眠等待状态中去,直到消息队列有新的消息需要处理或者等待超时为止。
SurfaceFlinger服务的UI渲染线程从SurfaceFlinger类的成员变量mEventQueue所描述的一个消息队列的成员函数waitMessage返回来时,如果有新的消息需要处理,那么变量msg就指向这个需要处理的新消息,即变量msg的值不等于0。
目前SurfaceFlinger服务的UI渲染线程只处理一种类型为MessageQueue:
:
INVALIDATE的消息,因此,变量msg所指向的消息的类型为MessageQueue:
:
INVALIDATE时,SurfaceFlinger服务的UI渲染线程就会从SurfaceFlinger类的成员函数waitForEvent中返回到调用它的成员函数threadLoop中去,以便可以处理控制台事件或者渲染UI的操作。
注意,当SurfaceFlinger服务的UI渲染线程从SurfaceFlinger类的成员变量mEventQueue所描述的一个消息队列的成员函数waitMessage返回来时,如果这时候显示屏仍然处于冻结状态,那么SurfaceFlinger类的成员函数waitForEvent就需要检查显示屏的冻结时间是否已经大于等于5000毫秒。
如果大于等于的话,那么就会自动对显示屏执行解冻操作,即分别将SurfaceFlinger类的成员变量mFreezeDisplayTime、mFreezeCount和mFreezeDisplay的值重置为0、0和false。
SurfaceFlinger类的成员变量mEventQueue所描述的一个消息队列的类型为MessageQueue,实现在文件frameworks/base/services/surfaceflinger/MessageQueue.cpp,它与的实现思路是类似的,不过会更简单一些。
简单来说,这个消息队列就是由一个消息列表以及一个条件变量组成。
当消息列表为空时,调用MessageQueue类的成员函数waitMessage的线程就会在MessageQueue类内部的条件变量上进入睡眠等状态。
而当其它线程向这个消息队列添加一个新消息的时候,就会通过MessageQueue类内部的条件变量来将前面正在等待的线程唤醒起来,以它可以将前面加入到它的消息队列中的新消息取出来处理。
至此,我们就分析完成SurfaceFlinger服务的UI渲染线程的运行模型了,在下一篇文章中我们还会继续详细分析这个线程是如何执行UI渲染操作的,接下来我们接着分析Binder线程与UI渲染线程的交互过程。
2.Binder线程与UI渲染线程的交互过程
前面提到,System进程在启动SurfaceFlinger服务之前,首先会启动一个Binder线程池。
Binder线程池的启动过程可以参考一文。
System进程中的Binder线程池启动起来之后,其它进程,例如Android应用程序进程,就可以请求SurfaceFlinger服务来渲染系统的UI了。
以为例,当它需要刷新自己的UI时,就会通过它所运行在的进程的SurfaceClient单例的