【Android源码 栏目提醒】:以下是网学会员为您推荐的Android源码 -Android系统的开机画面显示过程分析 - 其它资料,希望本篇文章对您学习有所帮助。
Android 系统的开机画面显示过程分析 分类:
Android2012-07-09 00:56 1252人阅读 评论39 收藏 举报 好几个月都没有更新过博客了,从今天开始,老罗将尝试对
Android 系统的 UI 实现作一个系统的分析,也算是落实之前所作出的承诺。
提到
Android 系统的 UI,我们最先接触到的便是系统在启动过程中所出现的画面了。
Android 系统在启动的过程中,最多可以出现三个画面,每一个画面都用来描述一个不同的启动阶段。
本文将详细分析这三个开机画面的显示过程,以便可以开启我们对
Android 系统 UI 实现的分析之路。
第一个开机画面是在内核启动的过程中出现的,它是一个静态的画面。
第二个开机画面是在 init 进程启动的过程中出现的,它也是一个静态的画面。
第三个开机画面是在系统服务启动的过程中出现的,它是一个动态的画面。
无论是哪一个画面,它们都是在一个称为帧缓冲区(frame buffer,简称 fb)的硬件设备上进行渲染的。
接下来,我们就分别分析这三个画面是如何在 fb 上显示的。
1. 第一个开机画面的显示过程
Android 系统的第一个开机画面其实是 Linux 内核的启动画面。
在默认情况下,这个画面是不会出现的,除非我们在编译内核的时候,启用以下两个编译选项: CONFIG_FRAMEBUFFER_CONSOLE CONFIG_LOGO 第一个编译选项表示内核支持帧缓冲区控制台,它对应的配置菜单项为:Device DeviceDrivers ---gt Graphics support ---gt Console display driver support ---gt Framebuffer support。
第二个编译选项表示内核在启动的过程中,需要显示 LOGO,它对应Console support的配置菜单项为:Device Drivers ---gt Graphics support ---gt Bootup logo 配置
Android logo。
内核编译选项可以参考在 Ubuntu 上下载、编译和安装
Android 最新内核源代码(LinuxKernel)一文。
帧缓冲区硬件设备在内核中有一个对应的驱动程序模块 fbmem,它实现在文件kernel/goldfish/drivers/video/fbmem.c 中,它的初始化函数如下所示: 1 / 2 fbmem_init - init frame buffer subsystem 3 4 Initialize the frame buffer subsystem. 5 6 NOTE: This function is _only_ to be called by drivers/char/mem.c. 7 8 / 9 10 static int __init 11 void void fbmem_initvoid 12 13 proc_createquotfbquot 0 NULL ampfb_proc_fops 14 15 if register_chrdevFB_MAJORquotfbquotampfb_fops 16 printkquotunable to get major d for fb devsnquot FB_MAJOR 17 18 fb_class class_createTHIS_MODULE quotgraphicsquot 19 if IS_ERRfb_class 20 printkKERN_WARNING quotUnable to create fb class errno ldnquot PTR_ERRfb_class 21 fb_class NULL 22 23 return 0 24 这个函数首先调用函数 proc_create 在/proc 目录下创建了一个 fb 文件,接着又调用函数 register_chrdev 来注册了一个名称为 fb 的字符设备,最后调用函数 class_create 在/sys/class 目录下创建了一个 graphics 目录,用来描述内核的图形系统。
模块 fbmem 除了会执行上述初始化工作之外,还会导出一个函数register_framebuffer: 25 EXPORT_SYMBOLregister_framebuffer 这个函数在内核的启动过程会被调用,以便用来执行注册帧缓冲区硬件设备的操作,它的实现如下所示: 26 / 27 register_framebuffer - registers a frame buffer device28 fb_info: frame buffer info structure29 30 Registers a frame buffer device fb_info.31 32 Returns negative errno on error or zero for success.33 34 /3536 int37 struct register_framebufferstruct fb_info fb_info38 39 int i40 struct fb_event event41 ......4243 if num_registered_fb FB_MAX44 return -ENXIO4546 ......4748 num_registered_fb49 for i 0 i lt FB_MAX i50 if registered_fbi51 break break52 fb_info-gtnode i53 mutex_initampfb_info-gtlock54 fb_info-gtdev device_createfb_class fb_info-gtdevice55 MKDEVFB_MAJOR i NULL quotfbdquot i56 if IS_ERRfb_info-gtdev 57 / Not fatal /58 printkKERN_WARNING quotUnable to create device for framebuffer derrno ldnquot i PTR_ERRfb_info-gtdev59 fb_info-gtdev NULL60 else61 fb_init_devicefb_info6263 ......6465 registered_fbi fb_info6667 event.info fb_info68 fb_notifier_call_chainFB_EVENT_FB_REGISTERED ampevent69 return 070 由于系统中可能会存在多个帧缓冲区硬件设备,因此,fbmem 模块使用一个数组registered_fb 保存所有已经注册了的帧缓冲区硬件设备,其中,每一个帧缓冲区硬件都是使用一个结构体 fb_info 来描述的。
我们知道,在 Linux 内核中,每一个硬件设备都有一个主设备号和一个从设备号,它们用来唯一地标识一个硬件设备。
对于帧缓冲区硬件设备来说,它们的主设备号定义为FB_MAJOR(29),而从设备号则与注册的顺序有关,它们的值依次等于0,1,2等。
每一个被注册的帧缓冲区硬件设备在/dev 目录和/sys/class/graphics 目录下都有一个对应的设备文件 fbltminorgt,其中,ltminorgt表示一个从设备号。
例如,第一个被注册的帧缓冲区硬件设备在/dev 目录和/sys/class/graphics 目录下都有一个对应的设备文件 fb0。
用户空间的应用程序通过这个设备文件就可以操作帧缓冲区硬件设备了,即将要显示的画面渲染到帧缓冲区硬件设备上去。
这个函数最后会通过调用函数 fb_notifier_call_chain 来通知帧缓冲区控制台,有一个新的帧缓冲区设备被注册到内核中来了。
帧缓冲区控制台在内核中对应的驱动程序模块为 fbcon,它实现在文件kernel/goldfish/drivers/video/console/fbconn.c 中,它的初始化函数如下所示: 71 static struct notifier_block fbcon_event_notifier 72 .notifier_call fbcon_event_notify 73 74 75 ...... 76 77 void void static int __init fb_console_initvoid 78 79 int i 80 81 acquire_console_sem 82 fb_register_clientampfbcon_event_notifier 83 fbcon_device device_createfb_class NULL MKDEV0 0 NULL 84 quotfbconquot 85 86 if IS_ERRfbcon_device 87 printkKERN_WARNING quotUnable to create device quot 88 quotfor fbcon errno ldnquot 89 PTR_ERRfbcon_device 90 fbcon_device NULL 91 else 92 fbcon_init_device 93 94 for i 0 i lt MAX_NR_CONSOLES i 95 con2fb_mapi -1 96 97 release_console_sem 98 fbcon_start 99 return 0 100 这个函数除了会调用函数 device_create 来在/sys/class/graphics 目录下创建一个fbconn 文件之外,还会调用函数 fb_register_client 来监听帧缓冲区硬件设备的注册事件,这是由函数 fbcon_event_notify 来实现的,如下所示: struct 101 static int fbcon_event_notifystruct notifier_block self 102 unsigned long action void data 103 104 struct fb_event event data 105 struct fb_info info event-gtinfo 106 ...... 107 int ret 0 108 109 ...... 110 111 switchaction switch 112 ...... 113 case FB_EVENT_FB_REGISTERED: 114 ret fbcon_fb_registeredinfo 115 break break 116 ...... 117 118 119 120 done: 121 return ret 122 帧缓冲区硬件设备的注册事件最终是由函数 fbcon_fb_registered 来处理的,它的实现如下所示: struct 123 static int fbcon_fb_registeredstruct fb_info info 124 125 int ret 0 i idx info-gtnode 126 127 fbcon_select_primaryinfo 128 129 if info_idx -1 130 for i first_fb_vc i lt last_fb_vc i 131 if con2fb_map_booti idx 132 info_idx idx 133 break break 134 135 136 137 if info_idx -1 138 ret fbcon_takeover1 139 else 140 for i first_fb_vc i lt last_fb_vc i 141 if con2fb_map_booti idx 142 set_con2fb_mapi idx 0 143 144 145 146 return ret 147 函数 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 的实现如下所示: int 148 static int fbcon_takeoverint show_logo 149 150 int err i 151 152 if num_registered_fb 153 return -ENODEV 154 155 if show_logo 156 logo_shown FBCON_LOGO_DONTSHOW 157 158 for i first_fb_vc i lt last_fb_vc i 159 con2fb_mapi info_idx 160 161 err take_over_consoleampfb_con first_fb_vc last_fb_vc 162 fbcon_is_default 163 164 if err 165 for i first_fb_vc i lt last_fb_vc i 166 con2fb_mapi -1 167 168 info_idx -1 169 170 171 return err 172 全局变量 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 来指定的,它的定义如下所示: 173 / 174 The console switch structure for the frame buffer based console 175 / 176 177 static const struct consw fb_con 178 .owner THIS_MODULE 179 .con_startup fbcon_startup 180 .con_init fbcon_init 181 .con_deinit fbcon_deinit 182 .con_clear fbcon_clear 183 .con_putc fbcon_putc 184 .con_putcs fbcon_putcs 185 .con_cursor fbcon_cursor 186 .con_scroll fbcon_scroll 187 .con_bmove fbcon_bmove 188 .con_switch fbcon_switch 189 .con_blank fbcon_blank 190 .con_font_set fbcon_set_font 191 .con_font_get fbcon_get_font 192 .con_font_default fbcon_set_def_font 193 .con_font_copy fbcon_copy_font 194 .con_set_palette fbcon_set_palette 195 .con_scrolldelta fbcon_scrolldelta 196 .con_set_origin fbcon_set_origin 197 .con_invert_region fbcon_invert_region 198 .con_screen_pos fbcon_screen_pos 199 .con_getxy fbcon_getxy 200 .con_resize fbcon_resize 201 接下来我们主要关注函数 fbcon_init 和 fbcon_switch 的实现,系统就是通过它来初始化和切换控制台的。
在初始化的过程中,会决定是否需要准备第一个开机画面的内容,而在切换控制台的过程中,会决定是否需要显示第一个开机画面的内容。
函数 fbcon_init 的实现如下所示: struct 202 static void fbcon_initstruct vc_data vc int init 203 204 struct fb_info info registered_fbcon2fb_mapvc-gtvc_num 205 struct fbcon_ops ops 206 struct vc_data default_mode vc-gtvc_display_fg 207 struct vc_data svc default_mode 208 struct display t p ampfb_displayvc-gtvc_num 209 int logo 1 new_rows new_cols rows cols charcnt 256 210 int cap 211 212 if info_idx -1 info NULL 213 return return 214 215 ...... 216 217 if vc svc logo_shown FBCON_LOGO_DONTSHOW 218 info-gtfix.type FB_TYPE_TEXT 219 logo 0 220 221 ...... 222 223 if logo 224 fbcon_prepare_logovc info cols rows new_cols new_rows 225 226 ...... 227 当前正在初始化的控制台使用参数 vc 来描述,而它的成员变量 vc_num 用来描述当前正在初始化的控制台的编号。
通过这个编号之后,就可以在全局数组 con2fb_map 中找到对应的帧缓冲区硬件设备编号。
有了帧缓冲区硬件设备编号之后,就可以在另外一个全局数组中 registered_fb 中找到一个 fb_info 结构体 info,用来描述与当前正在初始化的控制台所对应的帧缓冲区硬件设备。
参数 vc 的成员变量 vc_display_fg 用来描述系统当前可见的控制台,它是一个类型为 vc_data的指针。
从这里就可以看出,最终得到的 vc_data 结构体 svc 就是用来描述系统当前可见的控制台的。
变量 logo 开始的时候被设置为1,表示需要显示第一个开机画面,但是在以下三种情况下,它的值会被设置为0,表示不需要显示开机画面: A. 参数 vc 和.
上一篇:
基于android手机的即时视频通讯系统
下一篇:
不规则零件优化排样的神经网络混合优化算法