2. 第二个开机画面的显示过程

      由于第二个开机画面是在init进程启动的过程中显示的,因此,我们就从init进程的入口函数main开始分析第二个开机画面的显示过程。
      init进程的入口函数main实现在文件system/core/init/init.c中,如下所示:
  1. int main(int argc, char **argv)
  2. {
  3. int fd_count = 0;
  4. struct pollfd ufds[4];
  5. ......
  6. int property_set_fd_init = 0;
  7. int signal_fd_init = 0;
  8. int keychord_fd_init = 0;
  9. if (!strcmp(basename(argv[0]), "ueventd"))
  10. return ueventd_main(argc, argv);
  11. ......
  12. queue_builtin_action(console_init_action, "console_init");
  13. ......
  14. for(;;) {
  15. int nr, i, timeout = -1;
  16. execute_one_command();
  17. restart_processes();
  18. if (!property_set_fd_init && get_property_set_fd() > 0) {
  19. ufds[fd_count].fd = get_property_set_fd();
  20. ufds[fd_count].events = POLLIN;
  21. ufds[fd_count].revents = 0;
  22. fd_count++;
  23. property_set_fd_init = 1;
  24. }
  25. if (!signal_fd_init && get_signal_fd() > 0) {
  26. ufds[fd_count].fd = get_signal_fd();
  27. ufds[fd_count].events = POLLIN;
  28. ufds[fd_count].revents = 0;
  29. fd_count++;
  30. signal_fd_init = 1;
  31. }
  32. if (!keychord_fd_init && get_keychord_fd() > 0) {
  33. ufds[fd_count].fd = get_keychord_fd();
  34. ufds[fd_count].events = POLLIN;
  35. ufds[fd_count].revents = 0;
  36. fd_count++;
  37. keychord_fd_init = 1;
  38. }
  39. if (process_needs_restart) {
  40. timeout = (process_needs_restart - gettime()) * 1000;
  41. if (timeout < 0)
  42. timeout = 0;
  43. }
  44. if (!action_queue_empty() || cur_action)
  45. timeout = 0;
  46. ......
  47. nr = poll(ufds, fd_count, timeout);
  48. if (nr <= 0)
  49. continue;
  50. for (i = 0; i < fd_count; i++) {
  51. if (ufds[i].revents == POLLIN) {
  52. if (ufds[i].fd == get_property_set_fd())
  53. handle_property_set_fd();
  54. else if (ufds[i].fd == get_keychord_fd())
  55. handle_keychord();
  56. else if (ufds[i].fd == get_signal_fd())
  57. handle_signal();
  58. }
  59. }
  60. }
  61. return 0;
  62. }
         函数一开始就首先判断参数argv[0]的值是否等于“ueventd”,即当前正在启动的进程名称是否等于“ueventd”。如果是的话,那么就以ueventd_main函数来作入口函数。这是怎么回事呢?当前正在启动的进程不是init吗?它的名称怎么可能会等于“ueventd”?原来,在目标设备上,可执行文件/sbin/ueventd是可执行文件/init的一个符号链接文件,即应用程序ueventd和init运行的是同一个可执行文件。内核启动完成之后,可执行文件/init首先会被执行,即init进程会首先被启动。init进程在启动的过程中,会对启动脚本/init.rc进行解析。在启动脚本/init.rc中,配置了一个ueventd进程,它对应的可执行文件为/sbin/ueventd,即ueventd进程加载的可执行文件也为/init。因此,通过判断参数argv[0]的值,就可以知道当前正在启动的是init进程还是ueventd进程。       
          ueventd进程是作什么用的呢?它是用来处理uevent事件的,即用来管理系统设备的。从前面的描述可以知道,它真正的入口函数为ueventd_main,实现在system/core/init/ueventd.c中。ueventd进程会通过一个socket接口来和内核通信,以便可以监控系统设备事件。例如,在前面在Ubuntu上为Android系统编写Linux内核驱动程序一文中, 我们调用device_create函数来创建了一个名称为“hello”的字符设备,这时候内核就会向前面提到的socket发送一个设备增加事件。ueventd进程通过这个socket获得了这个设备增加事件之后,就会/dev目录下创建一个名称为“hello”的设备文件。这样用户空间的应用程序就可以通过设备文件/dev/hello来和驱动程序hello进行通信了。

接下来调用另外一个函数queue_builtin_action来向init进程中的一个待执行action队列增加了一个名称等于“console_init”的action。这个action对应的执行函数为console_init_action,它就是用来显示第二个开机画面的。

函数queue_builtin_action实现在文件system/core/init/init_parser.c文件中,如下所示:

  1. static list_declare(action_list);
  2. static list_declare(action_queue);
  3. void queue_builtin_action(int (*func)(int nargs, char **args), char *name)
  4. {
  5. struct action *act;
  6. struct command *cmd;
  7. act = calloc(1, sizeof(*act));
  8. act->name = name;
  9. list_init(&act->commands);
  10. cmd = calloc(1, sizeof(*cmd));
  11. cmd->func = func;
  12. cmd->args[0] = name;
  13. list_add_tail(&act->commands, &cmd->clist);
  14. list_add_tail(&action_list, &act->alist);
  15. action_add_queue_tail(act);
  16. }
  17. void action_add_queue_tail(struct action *act)
  18. {
  19. list_add_tail(&action_queue, &act->qlist);
  20. }

action_list列表用来保存从启动脚本/init.rc解析得到的一系列action,以及一系列内建的action。当这些action需要执行的时候,它们就会被添加到action_queue列表中去,以便init进程可以执行它们。

回到init进程的入口函数main中,最后init进程会进入到一个无限循环中去。在这个无限循环中,init进程会做以下五个事情:

A. 调用函数execute_one_command来检查action_queue列表是否为空。如果不为空的话,那么init进程就会将保存在列表头中的action移除,并且执行这个被移除的action。由于前面我们将一个名称为“console_init”的action添加到了action_queue列表中,因此,在这个无限循环中,这个action就会被执行,即函数console_init_action会被调用。

B. 调用函数restart_processes来检查系统中是否有进程需要重启。在启动脚本/init.rc中,我们可以指定一个进程在退出之后会自动重新启动。在这种情况下,函数restart_processes就会检查是否存在需要重新启动的进程,如果存在的话,那么就会将它重新启动起来。

C. 处理系统属性变化事件。当我们调用函数property_set来改变一个系统属性值时,系统就会通过一个socket(通过调用函数get_property_set_fd可以获得它的文件描述符)来向init进程发送一个属性值改变事件通知。init进程接收到这个属性值改变事件之后,就会调用函数handle_property_set_fd来进行相应的处理。后面在分析第三个开机画面的显示过程时,我们就会看到,SurfaceFlinger服务就是通过修改“ctl.start”和“ctl.stop”属性值来启动和停止第三个开机画面的。

D. 处理一种称为“chorded keyboard”的键盘输入事件。这种类型为chorded keyboard”的键盘设备通过不同的铵键组合来描述不同的命令或者操作,它对应的设备文件为/dev/keychord。我们可以通过调用函数get_keychord_fd来获得这个设备的文件描述符,以便可以监控它的输入事件,并且调用函数handle_keychord来对这些输入事件进行处理。

E. 回收僵尸进程。我们知道,在Linux内核中,如果父进程不等待子进程结束就退出,那么当子进程结束的时候,就会变成一个僵尸进程,从而占用系统的资源。为了回收这些僵尸进程,init进程会安装一个SIGCHLD信号接收器。当那些父进程已经退出了的子进程退出的时候,内核就会发出一个SIGCHLD信号给init进程。init进程可以通过一个socket(通过调用函数get_signal_fd可以获得它的文件描述符)来将接收到的SIGCHLD信号读取回来,并且调用函数handle_signal来对接收到的SIGCHLD信号进行处理,即回收那些已经变成了僵尸的子进程。

注意,由于后面三个事件都是可以通过文件描述符来描述的,因此,init进程的入口函数main使用poll机制来同时轮询它们,以便可以提高效率。

本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/967033,如需转载请自行联系原作者

Android系统的开机画面显示过程分析(5)相关推荐

  1. Android系统的开机画面显示过程分析(13)

          WindowManagerService类的成员函数performEnableScreen的实现如下所示: public class WindowManagerService extend ...

  2. Android系统的开机画面显示过程分析

    提到Android系统的UI,我们最先接触到的便是系统在启动过程中所出现的画面了.Android系统在启动的过程中,最多可以出现三个画面,每一个画面都用来描述一个不同的启动阶段.本文将详细分析这三个开 ...

  3. Android系统的开机画面显示过程分析(8)

         3. 第三个开机画面的显示过程         第三个开机画面是由应用程序bootanimation来负责显示的.应用程序bootanimation在启动脚本init.rc中被配置成了一个服 ...

  4. 安卓linux开机画面,Android系统的开机画面显示过程分析(1)

    好几个月都没有更新过博客了,从今天开始,老罗将尝试对Android系统的UI实现作一个系统的分析,也算是落实之前所作出的承诺.提到Android系统的UI,我们最先接触到的便是系统在启动过程中所出现的 ...

  5. Android系统手机开机画面各个阶段代码执行流程分析(Part2)

    3. 第三个开机画面的显示过程 第三个开机画面是由应用程序bootanimation来负责显示的.应用程序bootanimation在启动脚本init.rc中被配置成了一个服务,如下所示: servi ...

  6. Android系统定制开机logo和开机动画

    定制开机logo和开机动画 开机logo 开机动画 bootanimation.zip 生成zip文件 添加新的开机动画 开机logo MTK ASOP中lk阶段的logo是开机后的第一个界面,也被称 ...

  7. Android系统手机开机画面各个阶段代码执行流程分析(Part1)

    提到Android系统的UI,我们最先接触到的便是系统在启动过程中所出现的画面了.Android系统在启动的过程中,最多可以出现三个画面,每一个画面都用来描述一个不同的启动阶段.本文将详细分析这三个开 ...

  8. android系统修改开机动画

    前提须知 开机动画是bootanimation.zip文件,包含part0和desc.txt两部分. part0文件夹:存放与屏幕分辨率相同的图片,图片命名数字递增. desc.txt文本:配置图片如 ...

  9. Android系统Surface机制的SurfaceFlinger服务的启动过程分析

    在前面一篇文章中,我们简要介绍了Android系统Surface机制中的SurfaceFlinger服务.SurfaceFlinger服务是在System进程中启动的,并且负责统一管理设备的帧缓冲区. ...

最新文章

  1. python使用matplotlib可视化线图(line plot)、自定义可视化图像的四个边框的色彩、可以分别设置矩形每一条边的色彩(change the axis color)
  2. React Native 项目简单整理-组件优化
  3. AI专家Marcus质疑深度学习:面临十大挑战(含参考文献)
  4. Hadoop大数据--Mapreduce程序运行并发度
  5. Linq to Oracle 使用教程(八)使用 T4 模版生成代码
  6. php优化-》常用到的部分优化
  7. 第八节:Task的各类TaskTResult返回值以及通用线程的异常处理方案。
  8. 为什么要使用namedtuple?
  9. 添加类库引用后,命名空间出错的解决方案
  10. 【优化分类】基于matlab GA优化GRNN超参数分类【含Matlab源码 1399期】
  11. Android UI系列-----Dialog对话框
  12. arcmap创建空间索引_ArcGIS ArcMap编辑教程-创建新的点要素
  13. IE浏览器右下角小广告怎么去除
  14. python代码格式化神器_牛逼啊!一个随时随地写Python代码的神器
  15. 高以翔死因曝光!猝死前最后4分钟,他本还有一次活的机会...
  16. Python库安装之requirements.txt, environment.yml
  17. 基于眨眼状态的在线疲劳检测系统(Matlab-GUI设计)
  18. 猫、路由器、带宽、IP地址、子网掩码、网关以及公网与私网简介
  19. 生产服务器制作再生龙ghost系统安装完整教程
  20. 恢复照片软件推荐,照片恢复就这么做!

热门文章

  1. linux搭建h5学习日记
  2. 从自旋锁、睡眠锁、读写锁到 Linux RCU 机制讲解
  3. 小型超市库存管理系统c语言作业,C语言商品库存管理系统
  4. 计算音频一秒播放多少帧
  5. 如何写控制逻辑(三):模块级流水和valid/ready协议
  6. 土味网红崛起的背后,是不为人知的心酸
  7. 超强加载网络视频框架,支持任何地方使用,防火山小视频,今日头条视频等等
  8. 使用批处理__更改ip
  9. 07-ET框架的数据库连接
  10. java基于OpenCv图像处理_读取_保存图片