安卓linux開機畫面,Android系統的開機畫面顯示過程分析(1)

好幾個月都沒有更新過博客了,從今天開始,老羅將嘗試對Android系統的UI實現作一個系統的分析,也算是落實之前所作出的承諾。提到Android系統的UI,我們最先接觸到的便是系統在啟動過程中所出現的畫面了。Android系統在啟動的過程中,最多可以出現三個畫面,每一個畫面都用來描述一個不同的啟動階段。本文將詳細分析這三個開機畫面的顯示過程,以便可以開啟我們對Android系統UI實現的分析之路。

第一個開機畫面是在內核啟動的過程中出現的,它是一個靜態的畫面。第二個開機畫面是在init進程啟動的過程中出現的,它也是一個靜態的畫面。第三個開機畫面是在系統服務啟動的過程中出現的,它是一個動態的畫面。無論是哪一個畫面,它們都是在一個稱為幀緩沖區(frame buffer,簡稱fb)的硬件設備上進行渲染的。接下來,我們就分別分析這三個畫面是如何在fb上顯示的。

1. 第一個開機畫面的顯示過程

Android系統的第一個開機畫面其實是Linux內核的啟動畫面。在默認情況下,這個畫面是不會出現的,除非我們在編譯內核的時候,啟用以下兩個編譯選項:

CONFIG_FRAMEBUFFER_CONSOLE

CONFIG_LOGO

第一個編譯選項表示內核支持幀緩沖區控制臺,它對應的配置菜單項為:Device Drivers ---> Graphics support ---> Console display driver support ---> Framebuffer Console support。第二個編譯選項表示內核在啟動的過程中,需要顯示LOGO,它對應的配置菜單項為:Device Drivers ---> Graphics support ---> Bootup logo。配置Android內核編譯選項可以參考在Ubuntu上下載、編譯和安裝Android最新內核源代碼(Linux Kernel)一文。

幀緩沖區硬件設備在內核中有一個對應的驅動程序模塊fbmem,它實現在文件kernel/goldfish/drivers/video/fbmem.c中,它的初始化函數如下所示:

/**

*??????fbmem_init?-?init?frame?buffer?subsystem

*

*??????Initialize?the?frame?buffer?subsystem.

*

*??????NOTE:?This?function?is?_only_?to?be?called?by?drivers/char/mem.c.

*

*/

static?int?__init

fbmem_init(void)

{

proc_create("fb",?0,?NULL,?&fb_proc_fops);

if?(register_chrdev(FB_MAJOR,"fb",&fb_fops))

printk("unable?to?get?major?%d?for?fb?devs\n",?FB_MAJOR);

fb_class?=?class_create(THIS_MODULE,?"graphics");

if?(IS_ERR(fb_class))?{

printk(KERN_WARNING?"Unable?to?create?fb?class;?errno?=?%ld\n",?PTR_ERR(fb_class));

fb_class?=?NULL;

}

return?0;

}

這個函數首先調用函數proc_create在/proc目錄下創建了一個fb文件,接著又調用函數register_chrdev來注冊了一個名稱為fb的字符設備,最后調用函數class_create在/sys/class目錄下創建了一個graphics目錄,用來描述內核的圖形系統。

模塊fbmem除了會執行上述初始化工作之外,還會導出一個函數register_framebuffer:

EXPORT_SYMBOL(register_framebuffer);

這個函數在內核的啟動過程會被調用,以便用來執行注冊幀緩沖區硬件設備的操作,它的實現如下所示:

/**

*??????register_framebuffer?-?registers?a?frame?buffer?device

*??????@fb_info:?frame?buffer?info?structure

*

*??????Registers?a?frame?buffer?device?@fb_info.

*

*??????Returns?negative?errno?on?error,?or?zero?for?success.

*

*/

int

register_framebuffer(struct?fb_info?*fb_info)

{

int?i;

struct?fb_event?event;

......

if?(num_registered_fb?==?FB_MAX)

return?-ENXIO;

......

num_registered_fb++;

for?(i?=?0?;?i?

if?(!registered_fb[i])

break;

fb_info->node?=?i;

mutex_init(&fb_info->lock);

fb_info->dev?=?device_create(fb_class,?fb_info->device,

MKDEV(FB_MAJOR,?i),?NULL,?"fb%d",?i);

if?(IS_ERR(fb_info->dev))?{

/*?Not?fatal?*/

printk(KERN_WARNING?"Unable?to?create?device?for?framebuffer?%d;?errno?=?%ld\n",?i,?PTR_ERR(fb_info->dev));

fb_info->dev?=?NULL;

}?else

fb_init_device(fb_info);

......

registered_fb[i]?=?fb_info;

event.info?=?fb_info;

fb_notifier_call_chain(FB_EVENT_FB_REGISTERED,?&event);

return?0;

}

由于系統中可能會存在多個幀緩沖區硬件設備,因此,fbmem模塊使用一個數組registered_fb保存所有已經注冊了的幀緩沖區硬件設備,其中,每一個幀緩沖區硬件都是使用一個結構體fb_info來描述的。

我們知道,在Linux內核中,每一個硬件設備都有一個主設備號和一個從設備號,它們用來唯一地標識一個硬件設備。對于幀緩沖區硬件設備來說,它們的主設備號定義為FB_MAJOR(29),而從設備號則與注冊的順序有關,它們的值依次等于0,1,2等。

每一個被注冊的幀緩沖區硬件設備在/dev/graphics目錄下都有一個對應的設備文件fb,其中,表示一個從設備號。例如,第一個被注冊的幀緩沖區硬件設備在/dev/graphics目錄下都有一個對應的設備文件fb0。用戶空間的應用程序通過這個設備文件就可以操作幀緩沖區硬件設備了,即將要顯示的畫面渲染到幀緩沖區硬件設備上去。

這個函數最后會通過調用函數fb_notifier_call_chain來通知幀緩沖區控制臺,有一個新的幀緩沖區設備被注冊到內核中來了。

幀緩沖區控制臺在內核中對應的驅動程序模塊為fbcon,它實現在文件kernel/goldfish/drivers/video/console/fbcon.c中,它的初始化函數如下所示:

static?struct?notifier_block?fbcon_event_notifier?=?{

.notifier_call??=?fbcon_event_notify,

};

......

static?int?__init?fb_console_init(void)

{

int?i;

acquire_console_sem();

fb_register_client(&fbcon_event_notifier);

fbcon_device?=?device_create(fb_class,?NULL,?MKDEV(0,?0),?NULL,

"fbcon");

if?(IS_ERR(fbcon_device))?{

printk(KERN_WARNING?"Unable?to?create?device?"

"for?fbcon;?errno?=?%ld\n",

PTR_ERR(fbcon_device));

fbcon_device?=?NULL;

}?else

fbcon_init_device();

for?(i?=?0;?i?

con2fb_map[i]?=?-1;

release_console_sem();

fbcon_start();

return?0;

}

這個函數除了會調用函數device_create來創建一個類別為graphics的設備fbcon之外,還會調用函數fb_register_client來監聽幀緩沖區硬件設備的注冊事件,這是由函數fbcon_event_notify來實現的,如下所示:

static?int?fbcon_event_notify(struct?notifier_block?*self,

unsigned?long?action,?void?*data)

{

struct?fb_event?*event?=?data;

struct?fb_info?*info?=?event->info;

......

int?ret?=?0;

......

switch(action)?{

......

case?FB_EVENT_FB_REGISTERED:

ret?=?fbcon_fb_registered(info);

break;

......

}

done:

return?ret;

}

幀緩沖區硬件設備的注冊事件最終是由函數fbcon_fb_registered來處理的,它的實現如下所示:

static?int?fbcon_fb_registered(struct?fb_info?*info)

{

int?ret?=?0,?i,?idx?=?info->node;

fbcon_select_primary(info);

if?(info_idx?==?-1)?{

for?(i?=?first_fb_vc;?i?<=?last_fb_vc;?i++)?{

if?(con2fb_map_boot[i]?==?idx)?{

info_idx?=?idx;

break;

}

}

if?(info_idx?!=?-1)

ret?=?fbcon_takeover(1);

}?else?{

for?(i?=?first_fb_vc;?i?<=?last_fb_vc;?i++)?{

if?(con2fb_map_boot[i]?==?idx)

set_con2fb_map(i,?idx,?0);

}

}

return?ret;

}

函數fbcon_select_primary用來檢查當前注冊的幀緩沖區硬件設備是否是一個主幀緩沖區硬件設備。如果是的話,那么就將它的信息記錄下來。這個函數只有當指定了CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY編譯選項時才有效,否則的話,它是一個空函數。

在Linux內核中,每一個控制臺和每一個幀緩沖區硬件設備都有一個從0開始的編號,它們的初始對應關系保存在全局數組con2fb_map_boot中。控制臺和幀緩沖區硬件設備的初始對應關系是可以通過設置內核啟動參數來初始化的。在模塊fbcon中,還有另外一個全局數組con2fb_map,也是用來映射控制臺和幀緩沖區硬件設備的對應關系,不過它映射的是控制臺和幀緩沖區硬件設備的實際對應關系。

全局變量first_fb_vc和last_fb_vc是全局數組con2fb_map_boot和con2fb_map的索引值,用來指定系統當前可用的控制臺編號范圍,它們也是可以通過設置內核啟動參數來初始化的。全局變量first_fb_vc的默認值等于0,而全局變量last_fb_vc的默認值等于MAX_NR_CONSOLES - 1。

全局變量info_idx表示系統當前所使用的幀緩沖區硬件的編號。如果它的值等于-1,那么就說明系統當前還沒有設置好當前所使用的幀緩沖區硬件設備。在這種情況下,函數fbcon_fb_registered就會在全局數組con2fb_map_boot中檢查是否存在一個控制臺編號與當前所注冊的幀緩沖區硬件設備的編號idx對應。如果存在的話,那么就會將當前所注冊的幀緩沖區硬件設備編號idx保存在全局變量info_idx中。接下來還會調用函數fbcon_takeover來初始化系統所使用的控制臺。在調用函數fbcon_takeover的時候,傳進去的參數為1,表示要顯示第一個開機畫面。

如果全局變量info_idx的值不等于-1,那么函數fbcon_fb_registered同樣會在全局數組con2fb_map_boot中檢查是否存在一個控制臺編號與當前所注冊的幀緩沖區硬件設備的編號idx對應。如果存在的話,那么就會調用函數set_con2fb_map來調整當前所注冊的幀緩沖區硬件設備與控制臺的映射關系,即調整數組con2fb_map_boot和con2fb_map的值。

為了簡單起見,我們假設系統只有一個幀緩沖區硬件設備,這樣當它被注冊的時候,全局變量info_idx的值就會等于-1。當函數fbcon_fb_registered在全局數組con2fb_map_boot中發現有一個控制臺的編號與這個幀緩沖區硬件設備的編號idx對應時,接下來就會調用函數fbcon_takeover來設置系統所使用的控制臺。

函數fbcon_takeover的實現如下所示:

static?int?fbcon_takeover(int?show_logo)

{

int?err,?i;

if?(!num_registered_fb)

return?-ENODEV;

if?(!show_logo)

logo_shown?=?FBCON_LOGO_DONTSHOW;

for?(i?=?first_fb_vc;?i?<=?last_fb_vc;?i++)

con2fb_map[i]?=?info_idx;

err?=?take_over_console(&fb_con,?first_fb_vc,?last_fb_vc,

fbcon_is_default);

if?(err)?{

for?(i?=?first_fb_vc;?i?<=?last_fb_vc;?i++)?{

con2fb_map[i]?=?-1;

}

info_idx?=?-1;

}

return?err;

}

全局變量logo_shown的初始值為FBCON_LOGO_CANSHOW,表示可以顯示第一個開機畫面。但是當參數show_logo的值等于0的時候,全局變量logo_shown的值會被重新設置為FBCON_LOGO_DONTSHOW,表示不可以顯示第一個開機畫面。

中間的for循環將當前可用的控制臺的編號都映射到當前正在注冊的幀緩沖區硬件設備的編號info_idx中去,表示當前可用的控制臺與緩沖區硬件設備的實際映射關系。

函數take_over_console用來初始化系統當前所使用的控制臺。如果它的返回值不等于0,那么就表示初始化失敗。在這種情況下,最后的for循環就會將全局數組con2fb_map的各個元素的值設置為-1,表示系統當前可用的控制臺還沒有映射到實際的幀緩沖區硬件設備中去。這時候全局變量info_idx的值也會被重新設置為-1。

調用函數take_over_console來初始化系統當前所使用的控制臺,實際上就是向系統注冊一系列回調函數,以便系統可以通過這些回調函數來操作當前所使用的控制臺。這些回調函數使用結構體consw來描述。這里所注冊的結構體consw是由全局變量fb_con來指定的,它的定義如下所示:

/*

*??The?console?`switch'?structure?for?the?frame?buffer?based?console

*/

static?const?struct?consw?fb_con?=?{

.owner??????????????????=?THIS_MODULE,

.con_startup????????????=?fbcon_startup,

.con_init???????????????=?fbcon_init,

.con_deinit?????????????=?fbcon_deinit,

.con_clear??????????????=?fbcon_clear,

.con_putc???????????????=?fbcon_putc,

.con_putcs??????????????=?fbcon_putcs,

.con_cursor?????????????=?fbcon_cursor,

.con_scroll?????????????=?fbcon_scroll,

.con_bmove??????????????=?fbcon_bmove,

.con_switch?????????????=?fbcon_switch,

.con_blank??????????????=?fbcon_blank,

.con_font_set???????????=?fbcon_set_font,

.con_font_get???????????=?fbcon_get_font,

.con_font_default???????=?fbcon_set_def_font,

.con_font_copy??????????=?fbcon_copy_font,

.con_set_palette????????=?fbcon_set_palette,

.con_scrolldelta????????=?fbcon_scrolldelta,

.con_set_origin?????????=?fbcon_set_origin,

.con_invert_region??????=?fbcon_invert_region,

.con_screen_pos?????????=?fbcon_screen_pos,

.con_getxy??????????????=?fbcon_getxy,

.con_resize?????????????=?fbcon_resize,

};

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/373146.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/373146.shtml
英文地址,請注明出處:http://en.pswp.cn/news/373146.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如果你的NavigationDrawer里面的Item沒有響應,Drawer不能左滑關閉

如果你的NavigationDrawer里面的Item沒有響應&#xff0c;Drawer不能左滑關閉&#xff0c;應該是因為你沒有把主要內容放在DrawerLayout標簽下的第一位。 The main content view (the FrameLayout above) must be the first child in the DrawerLayout because the XML order i…

JAXB和未映射的屬性

JAXB&#xff08;JSR-222&#xff09;是例外配置&#xff0c;這意味著存在默認映射應用于域對象。 這意味著有時您需要顯式排除字段/屬性。 在本文中&#xff0c;我將討論如何使用XmlTransient或XmlAccessorType&#xff08;XmlAccessType.NONE&#xff09;以及何時使用每個選項…

sublime text3 使用SVN插件

Simon在項目中經常使用SVN&#xff0c;每次都要切換提交&#xff0c;很麻煩&#xff0c;有了這個SVN插件就很方便了&#xff0c;使用快捷方式提交&#xff0c;更新。 安裝: Ctrl Shift P 調用出Sublime Text的包管理工具&#xff0c;輸入TortoiseSVN&#xff0c;回車進行安裝…

c語言空格有什么作用,空格在c語言中怎么表示 C語言中的空格字符怎么表示

c語言中表示空格的是什么代碼&#xff1f;分析如下&#xff1a; 不是所有字符都需要轉義的&#xff0c;空格直接就敲空格&#xff0c;或者使用ASCII碼值賦值為32。 空格沒有轉義字符。合法轉義字符如下&#xff1a;\a 響鈴(BEL) 、\b 退格(BS)、\f 換頁(FF)、\n 換行(LF)、\r 回…

二維數組實現八皇后問題

之前關八皇后的問題全部使用的是一維數組進行實現(http://www.cnblogs.com/SeaSky0606/p/4604955.html)&#xff0c;現改一種數據存儲方式&#xff0c;按照8x8的二維棋盤存儲皇后。基本邏輯不變&#xff0c;可參見如下代碼&#xff1a; #include<cstdio> #include<alg…

Java的深度:通過協方差暴露的API泄漏

Java有時可能非常棘手&#xff0c;特別是在API設計中。 讓我們看一個非常有趣的展示柜。 jOOQ強烈地將API與實現分開。 所有API都在org.jooq包中&#xff0c;并且是公共的。 大多數實現都在org.jooq.impl包和package-private中。 只有工廠和一些專用的基礎實現是公開的。 這允許…

StringMVC 中如何做數據校驗

步驟一&#xff1a;引入四個jar包 步驟二&#xff1a;注冊類型轉換器 <context:component-scan base-package"cn.happy.controller"></context:component-scan><!-- 配置驗證器 --><bean id"myvalidator" class"org.springframe…

ibm+x3650+m4+linux+raid驅動,IBM X3650M4陣列卡驅動下載

ibm X3650M4raid陣列卡驅動適合安裝windowsserver2008,windowsserver2008R2,系統問題&#xff0c;服務器問題&#xff0c;可以聯系我們也可以到5分享論壇發帖求助。IBM System x3650 M4服務器是一款應用最為廣泛的2U機架服務器&#xff0c;支持Xeon E5-2600機架服務器的所有產品…

為什么在Java 6上Math.round(0.499999999999999917)舍入為1

總覽 錯誤表示錯誤和算術舍入錯誤有兩種類型&#xff0c;它們在浮點計算中很常見。 在此簡單示例中&#xff0c;這兩個錯誤組合在一起&#xff0c;在Java 6中Math.round&#xff08;0.4999999999999999999917&#xff09;舍入為1。 表示錯誤 浮點數是以2為底的格式&#xff0c…

單利模式

class Singleton{ public:static Singleton* GetInstance(){if (m_pInstance nullptr){m_pInstance new Singleton;}return m_pInstance;} private:Singleton(){}//需要將構造和析構定義成私有的防止外界構造和析構~Singleton(){}static Singleton* m_pInstance;//static所有…

C語言switch中break的作用,C語言中switch...case語句中break的重要性

在C語言中switch...case語句是經常用到的&#xff0c;下面我介紹一下在使用該語句時候需要注意的一個細節問題。話不多說&#xff0c;直接舉例子&#xff1a;例子1&#xff1a;switch(fruit){case 1:printf("apple"); break;case 2:printf("banana"); brea…

BZOJ 1898: [Zjoi2005]Swamp 沼澤鱷魚 [矩陣乘法]

1898: [Zjoi2005]Swamp 沼澤鱷魚 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 1082 Solved: 602[Submit][Status][Discuss]Description 潘塔納爾沼澤地號稱世界上最大的一塊濕地&#xff0c;它地位于巴西中部馬托格羅索州的南部地區。每當雨季來臨&#xff0c;這里碧波蕩漾…

從Spring開始,Java EE 6必須具備哪些附加功能?

我是一名高級Java開發人員&#xff0c;必須研究應用程序架構師選擇的技術。 我最多只能表達對特定技術的看法&#xff0c;不能做出/影響技術選擇的決定。 因此&#xff0c;在我的正式項目中&#xff0c;我別無選擇從Spring遷移到JavaEE6或從JavaEE6遷移到Spring。 我堅信&#…

UML類圖與類的關系詳解

在畫類圖的時候&#xff0c;理清類和類之間的關系是重點。類的關系有泛化(Generalization)、實現&#xff08;Realization&#xff09;、依賴(Dependency)和關聯(Association)。其中關聯又分為一般關聯關系和聚合關系(Aggregation)&#xff0c;合成關系(Composition)。下面我們…

教程:Hibernate,JPA和Spring MVC –第2部分

本教程將向您展示如何使用基本的Hibernate / JPA應用程序&#xff0c;如何將其轉換為Spring MVC Web項目&#xff0c;以便能夠在Web瀏覽器中查看數據庫&#xff0c;以及最后使用Spring的Transactional注釋來減少樣板代碼。 本教程假定您熟悉Java和Maven&#xff0c;并且已經完成…

算法轉換c語言程序,(轉)C語言實現卡爾曼濾波算法程序

非常感謝原作者&#xff0c;我在這個的基礎上轉換成純整形運算。STM32F103 12位ADC先放大1000倍再運算&#xff0c;理論上可以保留小數點后三位的結果。效果非常不錯&#xff0c;運算速度也快&#xff0c;72M時鐘 1-2uS左右(根據MDK周期數)。]uint32_t KalmanFilter(int32_t Re…

Java 8的烹調方式–拼圖項目

什么是Project Jigsaw&#xff1a;Project Jigsaw是使Java編譯器模塊知道的項目。 多年以來&#xff0c;Java API一直是整體的&#xff0c;即從代碼的任何部分都可以平等地看到整個API。 還沒有任何方法可以聲明代碼對任何其他用戶庫的依賴關系。 拼圖項目試圖以非常有效的方式…

python之路-SQLAlchemy

SQLAchemy SQLAlchemy是Python編程語言下的一款ORM框架&#xff0c;該框架建立在數據庫API之上&#xff0c;使用關系對象映射進行數據庫操作&#xff0c;簡言之便是&#xff1a;將對象轉換成SQL&#xff0c;然后使用數據API執行SQL并獲取執行結果。 安裝&#xff1a; pip3 inst…

POJ 1751 Highways

題意&#xff1a;n個城市&#xff0c;然后把n個城市的坐標都給你&#xff0c;然后給你m條已經修好的道路&#xff0c;然后給出m個已經修好道路的城市a&#xff0c;b&#xff0c; However, they want to guarantee that every town is highway-reachable from every other town.…

C語言編程中void什么意思,程序設計中遇到的void到底是什么意思

部分編程的初學者都會問"void是什么意思","為什么很多函數前都要加個void".實際上,void最簡單的解釋就是把0轉換成空類型的意思。下面用各個開發語言來詳解void1.C語言中的void表示空類型&#xff0c;它跟int&#xff0c;float是同地位的&#xff0c;一般用…