Android startup process

As shown in Figure 1, the startup process of Android is roughly as follows: loading BootLoader -> starting Linux kernel -> creating Init process (native layer -> Framework -> APP).

This article will focus on the process of starting the Init process, where the end point of the Init process is to create the child process that parses the file, and then daemon the child process (for reboot). First from the overall layout analysis

Android boots up the Linux kernel

As shown in Figure 1 above, when Android boots up, ROM loads the program BootLoader, which then checks RAM, initializes hardware parameters, and starts the Linux kernel. After the Linux Kernel is started, various hardware environments are initialized and drivers are loaded. When the Linux kernel is loaded, the Init process, the first process in user space, is started. The Linux kernel layer here is called kernel space, and the native layer above is called user space. So the Init process is the parent of all user processes.

Init Starts the Init process

This code is based on Android6.0 source code analysis (as long as a version, other versions of the basic can also understand), online source code read address: androidxref.com/, Init process code directory is mainly in: androidxref.com/

/system/core/init/Init.cpp /system/core/rootdir/init.rc /system/core/init/init_parser.cpp /system/core/init/builtins.cpp  /system/core/init/signal_handler.cppCopy the code

The Init process is the first process of the user process, and its PID = 1. It is mainly used to initialize and start the property service, and create child processes such as Zygote process. In the Linux kernel after completion of loading, the first thing to do is create the Init process, let’s see the Init process executed after the creation of the first main () function/system/core/Init/Init. CPP

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 umask umask(0); 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); Klog_init (); if (! Is_first_stage) {// 1. Initialize the property service, allocate space property_init(); } // 2. If the child process exits abnormally, all signals of the child process are cleared and restart the restart service defined by the child process signal_handler_init(); // 3. Start the property service start_property_service(); Rc configuration file init_parse_config_file("/init.rc"); while (true) { if (! waiting_for_exec) { execute_one_command(); // 5. Restart the dead child restart_processes(); } } return 0; }Copy the code

In the main() function, you first create and mount the required file directories, which are the directories of the system at run time. Then there are the scenarios we focus on:

  1. Call property_init in comment 1 to initialize the property service, and call start_property_service in comment 3 to start the property service.
  2. In note 2, signal_handler_init is used for signal processing of the child process.
  3. In comment 4 parse the init.rc configuration script file, which states what process to create and start;
  4. In comment 5, restart_processes loops through the restart daemon child process.

1. Handle sub-process exceptions

Signal_handler_init is called to process the semaphore of the child process. When the child process pauses or exits, it emits a terminated SIGCHLD signal. The signal_handler_init function listens for this signal and deletes and resets the information of the child process. And execute the subprocess in the script file for the configuration of onrestart service. Signal_handler_init /system/core/init/signal_handler.cpp

void signal_handler_init() { int s[ 2]; / / create a socket pair if (socketpair (AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == - ERROR("socketpair failed: %s\n", strerror(errno)); exit(1); } signal_write_fd = s[0]; signal_read_fd = s[1]; Signal_write_fd struct sigaction act; memset( & act, 0, sizeof(act)); act.sa_handler = SIGCHLD_handler; SIGCHLD act.sa_flags = SA_NOCLDSTOP; SIGCHLD act.sa_flags = SA_NOCLDSTOP; SIGCHLD act.sa_flags = SA_NOCLDSTOP; sigaction(SIGCHLD, & act, 0); // Determine and handle terminated processes reap_any_outstanding_children(); // Register the signal_read_fd state to call handle_signal. Register_epoll_handler (signal_read_fd, handle_signal); } /system/core/init/init.cpp void register_epoll_handler(int fd, void (*fn)()) { epoll_event ev; ev.events = EPOLLIN; ev.data.ptr = reinterpret_cast < void*>(fn); If (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, & ev) ==-1){ERROR("epoll_ctl failed: %s\n", strerror(errno)); }}Copy the code

Next, let’s look at the handle_signal callback. What happens to the child process that signals termination?

Static void handle_signal() {// Clear outstanding requests. // Write signal_read_fd to buf char buf[32]; read(signal_read_fd, buf, sizeof(buf)); Reap_any_outstanding_children (); reap_any_outstanding_children(); } static void reap_any_outstanding_children() {// through wait_for_one_process(), While (wait_for_one_process()) {}} // Check to see if it is exiting the process, if so, remove the properties and start the onrestart service for the configuration file. static bool wait_for_one_process() { int status; Pid =0 pid_t pid= TEMP_FAILURE_RETRY(waitPID (-1, & status, WNOHANG)); if (pid == 0) { return false; } else if (pid == -1) { ERROR("waitpid failed: %s\n", strerror(errno)); return false; } service * SVC = service_find_by_pid(pid); std::string name; // Exit if (! svc) { return true; } // When flags is RESTART and not ONESHOT, kill all child processes or threads in the process group first if (! (svc -> flags & SVC_ONESHOT) || (svc -> flags & SVC_RESTART)) { NOTICE("Service '%s' (pid %d) killing any children in process group\n", svc -> name, pi kill(-pid, SIGKILL); Socketinfo * si = SVC -> sockets; si; si = si -> next) { char tmp[ 128]; snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si -> name); unlink(tmp); If (SVC -> flags & SVC_EXEC) {INFO("SVC_EXEC PID %d finished... \n", svc -> pid); waiting_for_exec = false; list_remove( & svc -> slist); free(svc -> name); free(svc); return true; } // execute serive onrestart to start the corresponding service struct listNode *node; list_for_each(node, & svc -> onrestart.commands){ command * cmd = node_to_item(node, struct command, clist); cmd -> func(cmd -> nargs, cmd -> args); } svc -> NotifyStateChange("restarting"); return true; }Copy the code

Signal_handler_init Completes the signal_handler_init signal processing for the child process, which can be a registration callback to terminate the child process, remove the related attributes, and start the onrestart configuration service for the revice file.

2. Analytical Init. Rc

The very important configuration file is init.rc, located in the /system/core/rootdir/ directory. It is a script written by the Android initialization language. The Android initialization language can be learned by referring to this article. There are five types of Action, Command, Service, Option, and Import, but init. rc is mainly divided into Import, Service start Service, and on Command.

// import import import import /init.environ.rc import /init.usb.rc... on early-init write /proc/1/oom_score_adj -1000 restorecon /adb_keys start ueventd ... On nonencrypted class_start main // Start the service whose classname is mian class_start late_start... service servicemanager /system/bin/servicemanager class core user system group system critical onrestart restart healthd  onrestart restart zygote onrestart restart media onrestart restart surfaceflinger onrestart restart drm service surfaceflinger /system/bin/surfaceflinger service media /system/bin/mediaserver service installd /system/bin/installdCopy the code

In on we have class_start main which means to start a service with classname mian. In Init we start a very important Zygote child process. Now we’ll look at some definitions of Zygote service scripts. The Zygote service service is not in the Init script. The rc, it defines the Zygote service script file alone, in the/system/core/rootdir/Init. Zygote64. Rc

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
  class main
  socket zygote stream 660 root system
  onrestart write /sys/android_power/request_state wake
  onrestart write /sys/power/state on
  onrestart restart media
  onrestart restart netd
  writepid /dev/cpuset/foreground/tasks
Copy the code

The script here starts a process named zygote at the path /system/bin/app_process64, followed by the parameters. Class main: is the classname of this process called main, and onrestart is the process that will restart when the child processes signal processing. So zygote is started in the init. rc file.

In the init. rc file, in addition to zygote, some important processes such as Servicemanager, SurfaceFlinger, Media, and Installd are also started.

What about the process of creating these processes? See the next section for starting the parsed service.

3. Start the parsed service

Start a service with ClassName main in the configured script file by class_start main. Start of class_start corresponding function for do_class_start, located in the/system/core/init/builtins. CPP directory.

int do_class_start(int nargs, char **args) { service_for_each_class(args[1], service_start_if_not_disabled); return 0; } // look for a classname with the same name, then execute the argument function, Service_start_if_not_disabled void service_for_each_class(const char *classname, void (*func)(struct service *svc)) { struct listnode * node; struct service * svc; list_for_each(node, & service_list) { svc = node_to_item(node, struct service, slist); if (! strcmp(svc->classname, classname)) { func(svc); } } } static void service_start_if_not_disabled(struct service *svc) { if (! (SVC ->flags & SVC_DISABLED)) {// Create process service_start(SVC, NULL) if no disabled option is set; } else { svc -> flags | = SVC_DISABLED_START; } } void service_start(struct service *svc, const char *dynamic_args) { ... Pid_t pid = fork(); If (pid == 0) {// Indicates the child process. The child process inherits all resources from the parent process. / process/promoter execve (SVC - > the args [0], (char * *) arg_ptrs, ENV (char * *)); }}Copy the code

Fork () to create a child with pid=0, or parent if not. The execve() function is called to start the child process.

4. Initialize and start the property service

The properties service is similar to a registry, where properties are filled in and initialized to load when the system or software restarts. The main() function in the init.cpp file is initialized and started using these two functions, respectively

property_init()

start_property_service()

property_init

void property_init() { if (property_area_initialized) { return; } property_area_initialized = true; if (__system_property_area_init()) { return; } pa_workspace.size = 0; pa_workspace.fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW | O_CLOEXEC); if (pa_workspace.fd == -1) { ERROR("Failed to open %s: %s\n", PROP_FILENAME, strerror(errno)); return; }}Copy the code

The main thing the property_init() function does is create memory using the __system_property_area_init method.

start_property_service

Void start_property_service() {// Create socket property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0666, 0, 0, NULL); if (property_set_fd == -1) { ERROR("start_property_service socket creation failed: %s\n", strerror(errno)); exit(1); } listen(property_set_fd, 8); // Listen for property_set_fd and call the handle_property_set_fd callback register_epoll_handler(property_set_fd, handle_property_set_fd); }Copy the code

5. Daemon parsed services

Finally, through the while loop, the child process that can be restarted is restarted when a child process terminates

while (true) { if (! waiting_for_exec) { execute_one_command(); restart_processes(); } } static void restart_processes() { process_needs_restart = 0; service_for_each_flags( SVC_RESTARTING, restart_service_if_needed ); } // look for it, Void service_for_each_flags(unsigned matchflags, void (*func)(struct service * SVC)) {struct listNode * node; struct service * svc; list_for_each(node, & service_list) { svc = node_to_item(node, struct service, slist); if (svc->flags & matchflags) { func(svc); } } } static void restart_service_if_needed(struct service *svc) { time_t next_start_time = svc->time_started+5; if (next_start_time <= gettime()) { svc -> flags & = (~SVC_RESTARTING); // Restart the process service_start(SVC, NULL); return; } if ((next_start_time < process_needs_restart) || (process_needs_restart == 0) ) { process_needs_restart = next_start_time; }}Copy the code

conclusion

The Init process creation process, is mainly by parsing the Init. Rc file to create some important child processes such as Zagote, surfaceflinger process, the start is to use the fork, exec to start the child process, then is the subsidiary of process termination listen callback, Restart and create processes for services that can be restarted.