android开机画面initlogo.rle

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

由于第二个开机画面是在init进程启动的过程中显示的,因此,我们就从init进程的入口函数main开始分析第二个开机画面的显示过程。

init进程的入口函数main实现在文件system/core/init/init.c中,如下所示:

[cpp] view plain

int main(int argc, char **argv){int fd_count = 0;struct pollfd ufds[4];……int property_set_fd_init = 0;int signal_fd_init = 0;int keychord_fd_init = 0;if (!strcmp(basename(argv[0]), "ueventd"))return ueventd_main(argc, argv);……queue_builtin_action(console_init_action, "console_init");……for(;;) {int nr, i, timeout = -1;execute_one_command();restart_processes();if (!property_set_fd_init && get_property_set_fd() > 0) {ufds[fd_count].fd = get_property_set_fd();ufds[fd_count].events = POLLIN;ufds[fd_count].revents = 0;fd_count++;property_set_fd_init = 1;}if (!signal_fd_init && get_signal_fd() > 0) {ufds[fd_count].fd = get_signal_fd();ufds[fd_count].events = POLLIN;ufds[fd_count].revents = 0;fd_count++;signal_fd_init = 1;}if (!keychord_fd_init && get_keychord_fd() > 0) {ufds[fd_count].fd = get_keychord_fd();ufds[fd_count].events = POLLIN;ufds[fd_count].revents = 0;fd_count++;keychord_fd_init = 1;}if (process_needs_restart) {timeout = (process_needs_restart – gettime()) * 1000;if (timeout < 0)timeout = 0;}if (!action_queue_empty() || cur_action)timeout = 0;……nr = poll(ufds, fd_count, timeout);if (nr <= 0)continue;for (i = 0; i < fd_count; i++) {if (ufds[i].revents == POLLIN) {if (ufds[i].fd == get_property_set_fd())handle_property_set_fd();else if (ufds[i].fd == get_keychord_fd())handle_keychord();else if (ufds[i].fd == get_signal_fd())handle_signal();}}}return 0;} 函数一开始就首先判断参数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文件中,如下所示:

[cpp] view plain

static list_declare(action_list);static list_declare(action_queue);void queue_builtin_action(int (*func)(int nargs, char **args), char *name){struct action *act;struct command *cmd;act = calloc(1, sizeof(*act));act->name = name;list_init(&act->commands);cmd = calloc(1, sizeof(*cmd));cmd->func = func;cmd->args[0] = name;list_add_tail(&act->commands, &cmd->clist);list_add_tail(&action_list, &act->alist);action_add_queue_tail(act);}void action_add_queue_tail(struct action *act){list_add_tail(&action_queue, &act->qlist);} 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就会检查是否存在需要重新启动的进程,如果存在的话,那么就会将它重新启动起来。

两粒种子,一片森林。

android开机画面initlogo.rle

相关文章:

你感兴趣的文章:

标签云: