Android - 开机启动 Init 进程

本文详细剖析了Android系统启动过程中init进程的角色和功能,包括创建目录、挂载分区、解析init.rc配置文件、启动服务以及守护子进程的机制。init进程首先执行on类型section的命令,然后解析并启动service类型section定义的服务,如zygote、servicemanager等。同时,init通过信号处理和epoll机制守护子进程,确保系统稳定运行。
摘要由CSDN通过智能技术生成

Android - 开机启动 Init 进程

init介绍

当 Linux 内核启动后会初始化各种软硬件环境,加载驱动程序,挂载根文件系统,Linux 内核加载的准备完毕后就开始加载一些特定的程序(进程)了。第一个加载的就是 init 进程。
相关源码文件:

/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.cpp

Init 进程

我们应该都知道不管是 Java 还是 C/C++ 去运行某一个程序(进程)都是 XXX.xxx 的 main 方法作为入口,因此我们找到system/core/init/init.cpp 的 main() 方法:

int main(int argc, char** argv) {
    ...
    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);
    }
    ...
    // 初始化 signal handler
    signal_handler_init();
    // 解析 init.rc 脚本文件
    init_parse_config_file("/init.rc"); 
    // 将解析脚本中对应的操作添加到 action_queue 队列中
    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");
    
	...
	// 这里是个死循环
    while (true) {
    	if (!waiting_for_exec) {
            // 执行命令
            execute_one_command();
            restart_processes();
        }
        ...
    }
    return 0;

main 方法具体分为四个步骤:1.创建目录,挂载分区,2.解析启动脚本,3.启动解析的服务,4.守护解析的服务。init.rc 文件是 Android 系统的重要配置文件,位于 /system/core/rootdir/init.rc

init.rc文件

nit.rc 基本单位是 section。
section 有三种类型:

  1. on
  2. service
  3. import

on 类型
on 类型 表示一系列的命令组合,eg:

on init
    mkdir /productinfo 0771 system system
    # SPRD: modify for storage manage @{
    mount tmpfs tmpfs /storage mode=0751,gid=1028
    # @}
    mkdir /mnt/media_rw/sdcard0 0700 media_rw media_rw
    mkdir /storage/sdcard0 0700 root root
    mkdir /mnt/shell/emulated 0700 shell shell
    mkdir /storage/emulated 0555 root root

    # SPRD: move this to board level init @{
    #export EXTERNAL_STORAGE /storage/emulated/legacy
    #export SECONDARY_STORAGE /storage/sdcard0
    # @}
    # SPRD: for storage manage @{
    export LEGACY_STORAGE /storage/emulated/legacy
    # @}

这样一个 section 里面包含了多个命令。命令的执行是以 section 为单位的。
上面这些命令都会一起顺序执行,不会单独执行。
他们的执行由 init.cpp 的 main() 决定。在当其中调用

 action_for_each_trigger("init", action_add_queue_tail);

时,就将 on init 开始的这样一个 section 里所有的命令加入到一个执行队列,在未来某个时候会顺序执行队列里的命令。

service 类型
service 类型 的section 表示一个可执行的程序,下面是个人工作内容中片段代码:
例一:

service gocsdk /system/bin/gocsdk
    class main
    disabled
    oneshot

on property:sys.boot_completed=1
    chmod 777 /dev/goc_serial
    start gocsdk

gocsdk 作为一个名字标识了这个 service,这个可执行程序的位置在 /system/bin/gocsdk。
下面的 disable、oneshot 被称为 options,options 是用来描述的 service 的特点,不同的 service 有不同的 options。

service 的执行时间是在property:sys.boot_completed=1 这个命令被执行的时候,这个命令总存在于某个 on 类型的section 中作为一个服务启动的动作(Action)。

就拿上面的代码来说property:sys.boot_completed=1满足条件被执行时,则会启动这个类型为 main的 gocsdk service。

例二:

service yo_service1 /system/bin/yo_service1
    class core
    user system
    disabled
    group system radio shell
    oneshot

on yo_fs
    class_start core

其中 yo_service1 这个 service 的类型是 core。
在 yo_fs 被调用的时候则将会 class_start 而执行所有 类型为 core 的 service。

import 类型
import 类型表示包含了另外一些 section,在解析完 init.rc 后会继续调用 init_parse_config_file 来解析引入的 .rc 文件。
eg:
比如我们在 init.sc8830.rc 的开始可以看到

import /init.usb.rc
import /init.trace.rc

表示在运行完本 rc 后还将继续运行 init.usb.rc 和 init.trace.rc。

解析 init.rc 的过程就是识别一个个 section 的过程。
在 init.c 中的 main() 中去执行一个个命令。
(android采用双向链表来存储section的信息,解析完成之后,会得到三个双向链表action_list、service_list、import_list来分别存储三种section的信息上。)

加到可执行队列

将解析后的文件内容加到可执行队列中去,如下:

 action_for_each_trigger("early-init", action_add_queue_tail);
1083
1084    // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
1085    queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
1086    // ... so that we can start queuing up actions that require stuff from /dev.
1087    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
1088    queue_builtin_action(keychord_init_action, "keychord_init");
1089    queue_builtin_action(console_init_action, "console_init");
1090
1091    // Trigger all the boot actions to get us started.
1092    action_for_each_trigger("init", action_add_queue_tail);
1093
1094    // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
1095    // wasn't ready immediately after wait_for_coldboot_done
1096    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
1097
1098    // Don't mount filesystems or start core system services in charger mode.
1099    char bootmode[PROP_VALUE_MAX];
1100    if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) {
1101        action_for_each_trigger("charger", action_add_queue_tail);
1102    } else {
1103        action_for_each_trigger("late-init", action_add_queue_tail);
1104    }
        while (true) {
1110        if (!waiting_for_exec) {
1111            execute_one_command();  //
1112            restart_processes();
1113        }
1114
1115        int timeout = -1;
1116        if (process_needs_restart) {
1117            timeout = (process_needs_restart - gettime()) * 1000;
1118            if (timeout < 0)
1119                timeout = 0;
1120        }
1121
1122        if (!action_queue_empty() || cur_action) {
1123            timeout = 0;
1124        }
1125
1126        bootchart_sample(&timeout);
1127
1128        epoll_event ev;
1129        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
1130        if (nr == -1) {
1131            ERROR("epoll_wait failed: %s\n", strerror(errno));
1132        } else if (nr == 1) {
1133            ((void (*)()) ev.data.ptr)();
1134        }
1135    }
1136

启动服务

接下来看下是如何执行的,这里我们主要了解服务是如何启动的:

void execute_one_command() {
    Timer t;

    char cmd_str[256] = "";
    char name_str[256] = "";

    if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
        cur_action = action_remove_queue_head();
        cur_command = NULL;
        if (!cur_action) {
            return;
        }

        build_triggers_string(name_str, sizeof(name_str), cur_action);

        INFO("processing action %p (%s)\n", cur_action, name_str);
        cur_command = get_first_command(cur_action);
    } else {
        cur_command = get_next_command(cur_action, cur_command);
    }

    if (!cur_command) {
        return;
    }
    // 服务的 func 跳转到 do_class_start,再到service_for_each_class
    int result = cur_command->func(cur_command->nargs, cur_command->args);
	...
}

//Service的启动是在do_class_start函数中完成:
int do_class_start(int nargs, char **args)
{
   /* Starting a class does not start services
   * which are explicitly disabled.  They must
   * be started individually.
   */
    service_for_each_class(args[1], service_start_if_not_disabled);
    return 0;
}
//遍历所有名称为classname,状态不为SVC_DISABLED的Service启动
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);
        // 如果名字是一致的执行 func ,这是一个回调函数也就是 service_start_if_not_disabled
        if (!strcmp(svc->classname, classname)) {
            func(svc);
        }
    }
}

static void service_start_if_not_disabled(struct service *svc)
{
    if (!(svc->flags & SVC_DISABLED)) {
        service_start(svc, NULL);
    } else {
        svc->flags |= SVC_DISABLED_START;
    }
}

void service_start(struct service *svc, const char *dynamic_args)
{
	...
	// fork 创建了一个子进程
	pid_t pid = fork();
    if (pid == 0) {
		// 代表子进程,子进程会继承父进程的所有资源,可简单理解为读时共享写时复制
		...
		// exc 函数族,这些都是 linux 基础知识
		execve(svc->args[0], (char**) arg_ptrs, (char**) ENV); //asgs[0]表示可执行文件,后面是参数,注意fork里执行execves,会一直执行,不会返回fork中执行后面代码了。
	}
}

do_class_start对应的命令:

KEYWORD(class_start, COMMAND, 1, do_class_start)

init.rc文件中搜索class_start:class_start main 、class_start core、……

main、core即为do_class_start参数classname
init.rc文件中Service class名称都是main:

ervice drm /system/bin/drmserver
    class main

 
  service surfaceflinger /system/bin/surfaceflinger
       class main

于是就能够通过main名称遍历到所有的Service,将其启动。
do_class_start调用:
init.rc中

    on boot  //action

      class_start core    //执行command 对应 do_class_start

          class_start main

守护解析的服务

看下init 进程是如何守护子进程的:

void signal_handler_init() {
    // Create a signalling mechanism for SIGCHLD.
    int s[2];
	// 创建 socket pair 用于通信
    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
        ERROR("socketpair failed: %s\n", strerror(errno));
        exit(1);
    }
	// 当捕获信号SIGCHLD,则写入signal_write_fd
    signal_write_fd = s[0];
    signal_read_fd = s[1];

    // Write to signal_write_fd if we catch SIGCHLD.
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = SIGCHLD_handler;
    act.sa_flags = SA_NOCLDSTOP;
	// 这里注册监听,SA_NOCLDSTOP 使 init 进程只有在其子进程终止时才会受到 SIGCHLD 信号,然后触发SIGCHLD_handler函数
    sigaction(SIGCHLD, &act, 0);
	// 进入 waitpid 来处理子进程是否退出的情况
    reap_any_outstanding_children();
	// 调用 epoll_ctl 方法来注册 epoll 的回调函数
    register_epoll_handler(signal_read_fd, handle_signal);
}

//读取数据
static void handle_signal() {
    // Clear outstanding requests.
    char buf[32];
	// 读取 signal_read_fd 中的数据,并放入 buf,这里读出并没有什么实际作用,只是用于阻塞等待,当fd中有数据写入时,就会解除阻塞
    read(signal_read_fd, buf, sizeof(buf));
    reap_any_outstanding_children();
}

//写入数据
static void SIGCHLD_handler(int) {
	// 向signal_write_fd写入1,为了把fd唤醒
    if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
        ERROR("write(signal_write_fd) failed: %s\n", strerror(errno));
    }
}

static void reap_any_outstanding_children() {
    while (wait_for_one_process()) { }
}

static bool wait_for_one_process() {
    int status;
    //等待任意子进程,如果子进程没有退出则返回 0,否则则返回该子进程 pid。
    pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
    if (pid == 0) {
        return false;
    } else if (pid == -1) {
        return false;
    }
    //根据 pid 查找到相应的 service
    service* svc = service_find_by_pid(pid);

    //当 flags 为 RESTART,且不是 ONESHOT 时, kill 进程组内所有的子进程或子线程
    if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {
        kill(-pid, SIGKILL);
    }

    //移除当前服务 svc 中的所有创建过的 socket
    for (socketinfo* si = svc->sockets; si; si = si->next) {
        char tmp[128];
        snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);
        unlink(tmp);
    }

    //当 flags 为 EXEC 时,释放相应的服务
    if (svc->flags & SVC_EXEC) {
        waiting_for_exec = false;
        list_remove(&svc->slist);
        free(svc->name);
        free(svc);
        return true;
    }
    ...

    // 对于 ONESHOT 服务,使其进入 disabled 状态
    if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) {
        svc->flags |= SVC_DISABLED;
    }
    // 禁用和重置的服务,都不再自动重启
    //总结性说明:一个服务进程在init.rc中只要没有声明SVC_ONESHOT和SVC_RESET标志,当该进程死亡时,就会被重启;
    if (svc->flags & (SVC_DISABLED | SVC_RESET))  {
        svc->NotifyStateChange("stopped"); //设置相应的service状态为stopped,即对于服务不需要自动重启则设置属性"init.svc.{$service_name}" "stopped"
        return true;
    }
    
/**
         如果一个服务进程带有SVC_CRITICAL标志,且没有SVC_RESTART标志,当它crash、
         一定时间内重启的次数超过4此时,系统会自动重启并进入recovery模式
         */
   time_t now = gettime();
    if ((svc->flags & SVC_CRITICAL) && !(svc->flags & SVC_RESTART)) {
       if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {
            if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {
                ERROR("critical process '%s' exited %d times in %d minutes; "
                     "rebooting into recovery mode\n", svc->name,
                      CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
                android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
                return true;
            }
        } else {
            svc->time_crashed = now;
            svc->nr_crashed = 1;
        }
   }
    // 如果svc服务中有onrestart选项,则遍历进程重启时需要执行的command列表,执行当前 service 中所有 onrestar t命令,这个就是重启了
    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);
    }
    // 设置相应的 service 状态为 restarting
    svc->NotifyStateChange("restarting");
    return true;
}

至此 init 进程已全部分析完毕,有四个步骤:1. 创建目录,挂载分区,2. 解析启动脚本,3. 启动解析的服务,4. 守护解析的服务。最需要注意的是 init 创建了 zygote(创建 App 应用的服务)、servicemanager (client 与 service 通信管理的服务)、surfaceflinger(显示渲染服务) 和 media(多媒体服务) 等 service 进程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值