Android系统的开机画面显示过程分析
- 格式:pdf
- 大小:598.06 KB
- 文档页数:61
Android开机画面制作(Bootloader阶段)Android开机画面由四部分组成,第一部分在bootloader启动时显示;第二部分在Linux系统启动时(就是那小企鹅);第三部分是在Android平台启动初始化时,出现“A N D R I O D”文字字样画面;第四部分是在平台图形系统启动后,出现含闪烁的ANDROID字样的动画。
在bootloader阶段,aboot_init函数中,调用了display_init完成显示的初始化动作后,接着调用display_image_on_screen,在该函数中涉及开机画面的显示。
原理比较简单,开机画面已被转换为可以直接用于显示的RGB数据,存放在一个常量数组中,需要显示时,定义显示的区域,直接将这些数据拷贝到Buffer中。
这里说一下如何将一般的图片(jpg、png、bmp等)转换得到bootloader开机画面所需的显示数据,其中有些细节需要注意的。
一、得到png文件大多数格式的图片,都可以通过photoshop转换成png格式。
注意的是,bootloader需要的png必须是256色且不带alpha(透明度)通道的。
首先,打开需要转换的文件,比如android.jpg,然后选择“文件”菜单下的“存储为Web和设备所用格式”。
注意设置页面右上角红色框内的选项,必须选择PNG-8格式,颜色为256,不勾选透明度。
设置完毕,保存为png文件,如android.png。
二、转换成raw文件这里要用到一个开源工具ImageMagick,可以到网上下载,有windows版本和Linux版本,其官方网站是。
但是,无论是windows还是Linux版本,格式转换的操作都只支持命令行操作方式。
使用下面的命令,得到可进一步利用的raw文件:convert *.png –depth 8 rgb:*raw比如上面的android.png,执行:convert android.png –depth 8 rgb:android.raw三、使用rgb2565Android自带了一个可将raw转换成直接用于显示的RGB数据的工具,在out/host/linux-x86/bin/目录下。
一、Android开机启动流程简介1、OS-level:由bootloader载入linux kernel后kernel开始初始化, 并载入built-in 的驱动程序。
Kernel完成开机后,载入init process,切换至user-space。
Init进程是第一个在user-space启动的进程。
2、Android-level:由init process读取init.rc,Native 服务启动,并启动重要的外部程序,例如:servicemanager、Zygote以及System Server等。
由 init process 根据硬件类型读取init.xxx.rc。
由init.xxx.rc加载init.xxx.sh。
由 init.xxx.sh 加载特定的硬件驱动。
如hi_tuner.ko、hi_demux.ko等。
3、Zygote-Mode:Zygote 启动完SystemServer 后,进入Zygote Mode,在Socket 等候命令。
随后,使用者将看到一个桌面环境(Home Screen)。
桌面环境由一个名为[Launcher]的应用程序负责提供。
本文档重点研究Android-level中的启动流程。
启动流程如下图所示:二、init process流程分析init进程简介init进程是第一个在user-space启动的进程。
由内核启动参数[init]传递给内核,如果该项没有设置,内核会按/etc/init,/bin/init,/sbin/init,/bin/sh的顺序进行尝试,如果都有的都没找到,内核会抛出 kernel panic:的错误。
init进程包含在跟文件系统中,跟文件系统被直接编译到了内核。
对init 进程的修改,需要重新编译烧写内核。
编译命令:#cd kernel#make uImagCONFIG_INITRAMFS_SOURCE=../out/target/product/Hi3716C/root/ -j 8#cp kernel/arch/arm/boot/uImage out/target/product/Hi3716C/kernel -afv 源代码路径:froyo\system\core\init编译的文件:builtins.cinit.cdevices.cproperty_service.cutil.cparser.clogo.c编译命令:make init生成的文件:/out/target/product/Hi3716C/root/initinit进程启动流程1.安装SIGCHLD信号。
⾼通Andriod开机流程与镜像说明Android镜像说明Android设备刷机时都需要ROM包,ROM包下⾯有很多的.img和其他的相关镜像⽂件,其中这⾥⾯包含了Android很多的分区,Android镜像⽂件是通过源码编译⽣成的,下⾯是ROM包各个镜像的作⽤:镜像⽂件说明boot.img boot分区,包括内核⽂件和虚拟内存盘Ramdisk,负责设备开机,可在recovery模式进⾏擦除,重新安装带有boot分区的新系统system.img system分区,包含Android系统的⽤户界⾯以及设置上的⼀些预装系统应⽤recovery.img recovery分区,替代启动分区,执⾏恢复和维护系统的⼀些操作userdata.img data分区,⽤于保存⽤户的数据,例如联系⼈,短信,设置偏好和应⽤程序存放的地⽅cache.img cache分区,⽤于放置系统频繁访问的数据和应⽤程序组件的分区persist.img persist分区包含了设备的传感器和信号部分的驱动程序,例如wifi,蓝⽛连接都有关系splash.img开机画⾯⽂件NON-HLOS.bin modem image负责处理通讯协议相关的基带镜像prog_emmc_firehose_8953_ddr.mbn QFIL软件烧录系统的时候,需要⽤到此⽂件,应该是关于EMMC、DDR的配置参数相关的sbl1.mbn硬件的初始化,并且保存加载其他模块信息的顺序tz.mbn trustzone是ARM TrustZone® 技术是系统范围的安全⽅法,基于安全需求和引导模式配置XPU,NAND MPU,它和其他模块代码运⾏在相互隔离的区域,主要实现底层很多安全性特性emmc_appsboot.mbn bootloader⽂件,进⼊fastboot模式相关⽂件rpm.mbn电源管理器,是⾼通MSM平台另外加的⼀块芯⽚,虽然与AP芯⽚打包在⼀起,但其是⼀个独⽴的ARM Core。
基于UT4412BV03开发板订制Android系统开机动画我们在使用友坚恒天UT4412BV03开发板时,系统启动时可以看到三种不同的图片。
但是我们看到的开机画面不一定是我们喜欢的。
或者我们要使开机画面为自己公司的开机logo,这时我们可以按照下面的方式修改。
对于android系统启动过程中显示的画面,我们可以根据自己的需要而设计,但我们在设计开机logo时可能也会考虑到怎样让系统以最快的速度启动,在开发UT4412BV03开发板的过程中,我们将系统启动画面显示分为了3个阶段,不包括UBOOT部分的logo,为了让系统以最快的速度启动,所以未在在uBOOT中置入开机logo,只是从kernel开始显示开机logo。
UT4412BV03开发板系统启动时有三个显示阶段,下面将分析怎样制作这三个显示画面显示的图片。
第一阶段为:kernel中的开机logo(kernel中实现)第二阶段为:显示Android字样(android中实现)第三阶段为:显示动态滚动的图片(android中实现)一.Kernel中开机动画的制作1.首先,安装光盘中的logo目录下的gimp-2.8.14-setup软件2.用gimp软件打开logo目录下的logo图片,右键单击鼠标,选择图像---模式----索引如下图将最大颜色数改为“224”点击“转换”然后单击“Export As”将图片命名为“logo_linux_clut224.ppm”点击“转换”选择“ASCII”,选择“导出”将导出的logo_linux_clut224.ppm文件复制到内核源码的kernel/driver/video/logo目录下,然后重新执行“make –j2”编译出zImage镜像下载到开发板上。
重启系统将看到kernel中制作好的开机动画。
二.将系统启动的android字样修改为urbertter修改需要显示的文字:将Android源码Android_source\system\core\init路径下的init.c中的如下函数中的android 字样修改为urbetter三.android动态画面的制作Android开机动画修改方法,android 2.0及之后,使用bootanimation程序显示开机画面,如需修改开机画面,不用修改代码,只需按格式要求做bootanimation.zip包,放在系统的/system/media目录中,开机画面主要是由一个zip格式的压缩包bootanimation.zip组成,压缩包里面包含数张png格式的图片,还有一个desc.txt的文本文档,开机时按desc.txt 里面的指令,屏幕上会按文件名称顺序连续的播放一张张图片,形成连贯的动画。
Android手机开机问题分析流程本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以下内容:一、如何抓取开机问题Log一、如何抓取开机问题Log当我们遇到开机问题时候,不同阶段,我们需要不同的Log分析,这样才可以快速方便的解决开机问题,如果没有Log,我们几乎很难分析解决开机问题。
开机问题抓取 Log 流程如下:如何抓取开机问题Log二、开机问题Log 分析流程通过不同阶段的Log,我们进行不同的重点分析。
开机问题Log 分析流程如下:开机问题Log分析流程三、 kernel Log 搜索关键字 fs_mgr在 kernel Log 中搜索关键字 fs_mgr 初步分析定位分区问题。
1. fs_mgr: __mount(source=/dev/block/dm-0,target=/system,type=ext4)=-1System分区mount失败debug方法•a.Kernel log中有emmc “I/O error” ,需要检查emmc相关供电,替换物料交叉实验•b.Log中没有I/O error,回读system分区对比正常机器system.img看文件是否被破坏2.fs_mgr: __mount(source=/dev/block/dm-0,target=/data,type=ext4)=-1Data分区mount失败debug方法a.一般加密的情况/userdata分区会先出现 mount fail情况,然后才解密节点/dev/block/dm-0 or dm-1 , target=/data mount成功,正常userdata mount失败,可能是分区数据毁损,请先readback img, 然后手动进recovery mode 做factory reset 看能否恢复Readback 的userdata img可以对比正常机器看是哪个文件毁损3. init: fs_mgr_mount_all returned an errorDm-verity配置错误引起user版本开不了机(eng版本正常)同步打开/kernel-3.18/arch/arm/configs/${project}_defconfig 中如下两个定义:1.CONFIG_DM_VERITY=y2.CONFIG_DM_VERITY_FEC=y4.fs_mgr: Error loading verity table (Invalid argument) 跳转至3四、uart log中搜索关键字 SBC,是否存在error/ fail/ assert五、开始时间长的问题分析方法1. MTK 平台开机时间保存的文件a.手机中(/proc/bootprof)mobile Log中的bootprof文件。
Android窗口管理服务WindowManagerService显示Activity 组件的启动窗口(Starting Window)的过程分析在Android系统中,Activity组件在启动之后,并且在它的窗口显示出来之前,可以显示一个启动窗口。
这个启动窗口可以看作是Activity组件的预览窗口,是由WindowManagerService服务统一管理的,即由WindowManagerService服务负责启动和结束。
在本文中,我们就详细分析WindowManagerService服务启动和结束Activity组件的启动窗口的过程。
Activity组件的启动窗口是由ActivityManagerService服务来决定是否要显示的。
如果需要显示,那么ActivityManagerService服务就会通知WindowManagerService服务来为正在启动的Activity组件显示一个启动窗口,而WindowManagerService服务又是通过窗口管理策略类PhoneWindowManager来创建这个启动窗口的。
这个过程如图1所示。
窗口管理策略类PhoneWindowManager创建完成Activity组件的启动窗口之后,就会请求WindowManagerService服务将该启动窗口显示出来。
当Activity组件启动完成,并且它的窗口也显示出来的时候,WindowManagerService服务就会结束显示它的启动窗口。
注意,Activity组件的启动窗口是由ActivityManagerService服务来控制是否显示的,也就是说,Android应用程序是无法决定是否要要Activity组件显示启动窗口的。
接下来,我们就分别分析Activity组件的启动窗口的显示和结束过程。
一. Activity组件的启动窗口的显示过程从前面一文可以知道,Activity组件在启动的过程中,会调用ActivityStack类的成员函数startActivityLocked。
Android 6.0启动过程详细解析从代码角度仔细学习Android系统的启动过程,同时,学习Android启动过程中的初始化脚本语言,即init.rc中的语言语法。
在这里,不在详细介绍Linux内核的启动过程,主要学习从Linux内核启动之后,init初始化是如何工作的,他是如何启动Android系统的第一个进程–Zygote进程。
并且还会继续了解后面其他的进程是如何通过Zygote进程启动的。
话不多说,我们现在就来气Android系统启动之路。
## Android系统启动流程图我们都知道,Android系统内核是基于Linux内核,所以在Android系统启动过程中,首先启动Linux内核,Bootloader加载并启动Linux内核,内核启动完成之后,内核开始启动Android系统的init进程,然后init进程通过init.rc启动脚本语言的执行,来启动Zygote进程,作为Android其他进程的父进程,Zygote进程做完初始化工作之后,启动SystemServer 来启动其他系统服务。
下面我们从init进程的启动开始学习。
int main(int argc, char** argv) {if (!strcmp(basename(argv[0]), "ueventd")) {return ueventd_main(argc, argv);}if (!strcmp(basename(argv[0]), "watchdogd")) {return watchdogd_main(argc, argv);}// Clear the umask.umask(0);add_environment("PA TH", _PATH_DEFPATH);bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);// Get the basic filesystem setup we need put together in the initramdisk// on / and then we'll let the rc file figure out the rest.if (is_first_stage) {mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");mkdir("/dev/pts", 0755);mkdir("/dev/socket", 0755);mount("devpts", "/dev/pts", "devpts", 0, NULL);mount("proc", "/proc", "proc", 0, NULL);mount("sysfs", "/sys", "sysfs", 0, NULL);}// We must have some place other than / to create the device nodes for// kmsg and null, otherwise we won't be able to remount / read-only// later on. Now that tmpfs is mounted on /dev, we can actually talk// to the outside world.open_devnull_stdio();klog_init();klog_set_level(KLOG_NOTICE_LEVEL);NOTICE("init%s started!\n", is_first_stage ? "" : " second stage");if (!is_first_stage) {// Indicate that booting is in progress to background fw loaders, etc.close(open("/dev/.booting", O_WRONL Y | O_CREAT | O_CLOEXEC, 0000));property_init();// If arguments are passed both on the command line and in DT,// properties set in DT always have priority over the command-line ones.process_kernel_dt();process_kernel_cmdline();// Propogate the kernel variables to internal variables// used by init as well as the current required properties.export_kernel_boot_props();}// Set up SELinux, including loading the SELinux policy if we're in the kernel domain. selinux_initialize(is_first_stage);// If we're in the kernel domain, re-exec init to transition to the init domain now// that the SELinux policy has been loaded.if (is_first_stage) {if (restorecon("/init") == -1) {ERROR("restorecon failed: %s\n", strerror(errno));security_failure();}char* path = argv[0];char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };if (execv(path, args) == -1) {ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));security_failure();}}// These directories were necessarily created before initial policy load// and therefore need their security context restored to the proper value.// This must happen before /dev is populated by ueventd.INFO("Running restorecon...\n");restorecon("/dev");restorecon("/dev/socket");restorecon("/dev/__properties__");restorecon_recursive("/sys");epoll_fd = epoll_create1(EPOLL_CLOEXEC);if (epoll_fd == -1) {ERROR("epoll_create1 failed: %s\n", strerror(errno));exit(1);}signal_handler_init();property_load_boot_defaults();start_property_service();init_parse_config_file("/init.rc");action_for_each_trigger("early-init", action_add_queue_tail);// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");// ... so that we can start queuing up actions that require stuff from /dev.queue_builtin_action(mix_hwrng_into_linux_rng_action,"mix_hwrng_into_linux_rng");queue_builtin_action(keychord_init_action, "keychord_init");queue_builtin_action(console_init_action, "console_init");// Trigger all the boot actions to get us started.action_for_each_trigger("init", action_add_queue_tail);// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random// wasn't ready immediately after wait_for_coldboot_donequeue_builtin_action(mix_hwrng_into_linux_rng_action,"mix_hwrng_into_linux_rng");// Don't mount filesystems or start core system services in charger mode.char bootmode[PROP_V ALUE_MAX];if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) {action_for_each_trigger("charger", action_add_queue_tail);} else {action_for_each_trigger("late-init", action_add_queue_tail);}// Run all property triggers based on current state of the properties.queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");while (true) {if (!waiting_for_exec) {execute_one_command();restart_processes();}int timeout = -1;if (process_needs_restart) {timeout = (process_needs_restart - gettime()) * 1000;if (timeout < 0)timeout = 0;}if (!action_queue_empty() || cur_action) {timeout = 0;}bootchart_sample(&timeout);epoll_event ev;int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));if (nr == -1) {ERROR("epoll_wait failed: %s\n", strerror(errno));} else if (nr == 1) {((void (*)()) ev.data.ptr)();}}return 0;}该文件位于system/core/init/init.cpp中,我们来看看init进程都做了哪些工作。
Android教程之开机流程全⾯解析本⽂详细讲述了Android的开机流程。
分享给⼤家供⼤家参考,具体如下:开机过程中⽆线模块的初始化过程;如果sim卡锁开启,或者pin被锁住的时候,会要求输⼊pin或者puk,但是这个解锁动作必须在系统初始化完成以后才能进⾏。
(图形系统都还没有初始化怎么输⼊密码阿?)当系统初始化完成以后会调⽤wm.systemReady()来通知⼤家。
这时候该做什么就做什么。
开机过程中⽆线模块的初始化过程:rild 调⽤参考实现 Reference-ril.c (hardware\ril\reference-ril) 中的函数:const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **argv)ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);static void *mainLoop(void *param)ret = at_open(fd, onUnsolicited);RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);在 initializeCallback 函数中对猫进⾏了初始化。
static void initializeCallback(void *param){ATResponse *p_response = NULL;int err;setRadioState (RADIO_STATE_OFF);at_handshake();/* note: we don't check errors here. Everything important willbe handled in onATTimeout and onATReaderClosed *//* atchannel is tolerant of echo but it must *//* have verbose result codes */at_send_command("ATE0Q0V1", NULL);/* No auto-answer */at_send_command("ATS0=0", NULL);/* Extended errors */at_send_command("AT+CMEE=1", NULL);/* Network registration events */err = at_send_command("AT+CREG=2", &p_response);/* some handsets -- in tethered mode -- don't support CREG=2 */if (err < 0 || p_response->success == 0) {at_send_command("AT+CREG=1", NULL);}at_response_free(p_response);/* GPRS registration events */at_send_command("AT+CGREG=1", NULL);/* Call Waiting notifications */at_send_command("AT+CCWA=1", NULL);/* Alternating voice/data off */at_send_command("AT+CMOD=0", NULL);/* Not muted */at_send_command("AT+CMUT=0", NULL);/* +CSSU unsolicited supp service notifications */at_send_command("AT+CSSN=0,1", NULL);/* no connected line identification */at_send_command("AT+COLP=0", NULL);/* HEX character set */at_send_command("AT+CSCS=\"HEX\"", NULL);/* USSD unsolicited */at_send_command("AT+CUSD=1", NULL);/* Enable +CGEV GPRS event notifications, but don't buffer */at_send_command("AT+CGEREP=1,0", NULL);/* SMS PDU mode */at_send_command("AT+CMGF=0", NULL);#ifdef USE_TI_COMMANDSat_send_command("AT%CPI=3", NULL);/* TI specific -- notifications when SMS is ready (currently ignored) */at_send_command("AT%CSTAT=1", NULL);#endif /* USE_TI_COMMANDS *//* assume radio is off on error */if (isRadioOn() > 0) {setRadioState (RADIO_STATE_SIM_NOT_READY);}}默认状况下假设射频模块是好的,通过 setRadioState (RADIO_STATE_SIM_NOT_READY) 来触发对⽆线模块的初始化。
Android开机log分析1.分析开机log,一是可以理一下android启动流程,二是可以通过log定位错误,下面列举一些常见android程序发生错误时抛出的异常,查找关键字xxxException可以快速定位android层错误以及原因:ng.NullPointerException:空指针异常ng.ClassNotFoundException:找不到类抛出的异常ng.ArithmeticException:一个整数“除以零”时抛出的异常ng.ArrayIndexOutOfBoundsException:数组越界访问以后抛出的异常ng.IllegalArgumentException:传入非法参数抛出的异常ng.IllegalAccessException:当应用程序要调用一个类,但当前的方法即没有对该类的访问权限便会出现这个异常ng.SecturityException:安全异常。
由安全管理器抛出,用于指示违反安全情况的异常ng.RuntimeException运行时异常。
是所有Java虚拟机正常操作期间可以被抛出的异常的父类。
ng.NumberFormatException:字符串转换为数字异常:ng.StackOverflowError:堆栈溢出错误。
当一个应用递归调用的层次太深而导致堆栈溢出时抛出该错误。
ng.RuntimeExceptionng.OutOfMemoryError:内存不足错误。
当可用内存不足以让Java虚拟机分配给一个对象时抛出该错误。
ng.IOException:输入输出异常ng.AbstractMethodError:抽象方法错误。
当应用试图调用抽象方法时抛出。
ng.ClassFormatError:类格式错误。
当Java虚拟机试图从一个文件中读取Java类,而检测到该文件的内容不符合类的有效格式时抛出。
ng.InstantiationError:实例化错误。
Android系统的开机画面显示过程分析分类:Android2012-07-0900:561252人阅读评论(39)收藏举报好几个月都没有更新过博客了,从今天开始,老罗将尝试对Android系统的UI实现作一个系统的分析,也算是落实之前所作出的承诺。
提到Android系统的UI,我们最先接触到的便是系统在启动过程中所出现的画面了。
Android系统在启动的过程中,最多可以出现三个画面,每一个画面都用来描述一个不同的启动阶段。
本文将详细分析这三个开机画面的显示过程,以便可以开启我们对Android系统UI实现的分析之路。
第一个开机画面是在内核启动的过程中出现的,它是一个静态的画面。
第二个开机画面是在init进程启动的过程中出现的,它也是一个静态的画面。
第三个开机画面是在系统服务启动的过程中出现的,它是一个动态的画面。
无论是哪一个画面,它们都是在一个称为帧缓冲区(frame buffer,简称fb)的硬件设备上进行渲染的。
接下来,我们就分别分析这三个画面是如何在fb上显示的。
1.第一个开机画面的显示过程Android系统的第一个开机画面其实是Linux内核的启动画面。
在默认情况下,这个画面是不会出现的,除非我们在编译内核的时候,启用以下两个编译选项:CONFIG_FRAMEBUFFER_CONSOLECONFIG_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中,它的初始化函数如下所示:1/**2*fbmem_init-init frame buffer subsystem3*4*Initialize the frame buffer subsystem.5*6*NOTE:This function is_only_to be called by drivers/char/mem.c.7*8*/910static int__init11fbmem_init(void)12{13proc_create("fb",0,NULL,&fb_proc_fops);1415if(register_chrdev(FB_MAJOR,"fb",&fb_fops))16printk("unable to get major%d for fb devs\n",FB_MAJOR);1718fb_class=class_create(THIS_MODULE,"graphics");19if(IS_ERR(fb_class)){20printk(KERN_WARNING"Unable to create fb class;errno=%ld\n", PTR_ERR(fb_class));21fb_class=NULL;22}23return0;24}这个函数首先调用函数proc_create在/proc目录下创建了一个fb文件,接着又调用函数register_chrdev来注册了一个名称为fb的字符设备,最后调用函数class_create在/sys/class目录下创建了一个graphics目录,用来描述内核的图形系统。
模块fbmem除了会执行上述初始化工作之外,还会导出一个函数register_framebuffer:25EXPORT_SYMBOL(register_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*/3536int37register_framebuffer(struct fb_info*fb_info)38{39int i;40struct fb_event event;41......4243if(num_registered_fb==FB_MAX)44return-ENXIO;4546......4748num_registered_fb++;49for(i=0;i<FB_MAX;i++)50if(!registered_fb[i])51break;52fb_info->node=i;53mutex_init(&fb_info->lock);54fb_info->dev=device_create(fb_class,fb_info->device,55MKDEV(FB_MAJOR,i),NULL,"fb%d",i);56if(IS_ERR(fb_info->dev)){57/*Not fatal*/58printk(KERN_WARNING"Unable to create device for framebuffer%d; errno=%ld\n",i,PTR_ERR(fb_info->dev));59fb_info->dev=NULL;60}else61fb_init_device(fb_info);6263......6465registered_fb[i]=fb_info;66=fb_info;68fb_notifier_call_chain(FB_EVENT_FB_REGISTERED,&event);69return0;70}由于系统中可能会存在多个帧缓冲区硬件设备,因此,fbmem模块使用一个数组registered_fb保存所有已经注册了的帧缓冲区硬件设备,其中,每一个帧缓冲区硬件都是使用一个结构体fb_info来描述的。
我们知道,在Linux内核中,每一个硬件设备都有一个主设备号和一个从设备号,它们用来唯一地标识一个硬件设备。
对于帧缓冲区硬件设备来说,它们的主设备号定义为FB_MAJOR(29),而从设备号则与注册的顺序有关,它们的值依次等于0,1,2等。
每一个被注册的帧缓冲区硬件设备在/dev目录和/sys/class/graphics目录下都有一个对应的设备文件fb<minor>,其中,<minor>表示一个从设备号。
例如,第一个被注册的帧缓冲区硬件设备在/dev目录和/sys/class/graphics目录下都有一个对应的设备文件fb0。
用户空间的应用程序通过这个设备文件就可以操作帧缓冲区硬件设备了,即将要显示的画面渲染到帧缓冲区硬件设备上去。
这个函数最后会通过调用函数fb_notifier_call_chain来通知帧缓冲区控制台,有一个新的帧缓冲区设备被注册到内核中来了。
帧缓冲区控制台在内核中对应的驱动程序模块为fbcon,它实现在文件kernel/goldfish/drivers/video/console/fbconn.c中,它的初始化函数如下所示:71static struct notifier_block fbcon_event_notifier={72.notifier_call=fbcon_event_notify,73};7475......7677static int__init fb_console_init(void)78{79int i;8081acquire_console_sem();82fb_register_client(&fbcon_event_notifier);83fbcon_device=device_create(fb_class,NULL,MKDEV(0,0),NULL,84"fbcon");8586if(IS_ERR(fbcon_device)){87printk(KERN_WARNING"Unable to create device"88"for fbcon;errno=%ld\n",89PTR_ERR(fbcon_device));90fbcon_device=NULL;91}else92fbcon_init_device();9394for(i=0;i<MAX_NR_CONSOLES;i++)95con2fb_map[i]=-1;9697release_console_sem();98fbcon_start();99return0;100}这个函数除了会调用函数device_create来在/sys/class/graphics目录下创建一个fbconn文件之外,还会调用函数fb_register_client来监听帧缓冲区硬件设备的注册事件,这是由函数fbcon_event_notify来实现的,如下所示:101static int fbcon_event_notify(struct notifier_block*self,102unsigned long action,void*data)103{104struct fb_event*event=data;105struct fb_info*info=event->info;106......107int ret=0;108109......110111switch(action){112......113case FB_EVENT_FB_REGISTERED:114ret=fbcon_fb_registered(info);115break;116......117118}119120done:121return ret;122}帧缓冲区硬件设备的注册事件最终是由函数fbcon_fb_registered来处理的,它的实现如下所示:123static int fbcon_fb_registered(struct fb_info*info)124{125int ret=0,i,idx=info->node;126127fbcon_select_primary(info);128129if(info_idx==-1){130for(i=first_fb_vc;i<=last_fb_vc;i++){131if(con2fb_map_boot[i]==idx){132info_idx=idx;133break;134}135}136137if(info_idx!=-1)138ret=fbcon_takeover(1);139}else{140for(i=first_fb_vc;i<=last_fb_vc;i++){141if(con2fb_map_boot[i]==idx)142set_con2fb_map(i,idx,0);143}144}145146return ret;147}函数fbcon_select_primary用来检查当前注册的帧缓冲区硬件设备是否是一个主帧缓冲区硬件设备。