init进程源码分析

来源:互联网 发布:ecshop 3.6 数据字典 编辑:程序博客网 时间:2024/05/30 23:10

1, 概述

Android系统启动流程图如下,


1,Bootloader引导

  当手机按下电源键时,最先运行的就是bootloader。主要作用是初始化基本的硬件环境(如CPU,内存,Flash等),为装载Linux内核准备合适的运行环境。一旦Linux内核装载完毕,bootloader将会从内存中清除掉。

  Fastboot 是android设计的一套通过USB更新手机分区映像的协议,方便开发人员快速更新指定的手机分区。

  Recovery 是android特有的升级系统。手机可以进行恢复出厂设置或者执行OTA,补丁和固件升级等操作。

2,Linux内核

  Android的boot.img存放的就是Linux内核和一个跟文件系统。Bootloader会把boot.img映像装进内存,然后Linux内核会执行整个系统的初始化,完成后装载跟文件系统,最后启动init进程。

3,init进程

  Init进程是android系统的第一个用户空间进程,它的进程号为1.主要作用如下,

 A,启动android系统中重要的守护进程(USB守护进程,adb,vold,rild等守护进程)

B,启动Zygote进程,会创建Dalivik虚拟机,创建SystemServer进程,响应应用程序的请求。

C,启动ServiceManager,主要用于管理Binder服务,负责Binder服务的注册和查找。

2 init进程的初始化

Init进程的源码位于/system/core/init/下,程序的入口函数main()位于文件init.cpp中。

int main(int argc, char** argv) {    if (!strcmp(basename(argv[0]), "ueventd")) {        return ueventd_main(argc, argv); // 执行守护进程ueventd的主函数    }    if (!strcmp(basename(argv[0]), "watchdogd")) {        return watchdogd_main(argc, argv); // 执行看门狗守护进程的主函数    }umask(0); // 参数为0表示进程创建的文件属性是0777

Init进程代码里包含了另外2个守护进程的代码.

Android.mk文件的部分如下,

# Create symlinksLOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \    ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdog


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);    }  
selinux_initialize(is_first_stage); // 初始化SELinux

 init_parse_config_file("/init.rc"); // 解析 init.rc 文件    action_for_each_trigger("early-init", action_add_queue_tail);    // 将指定的action加入到action_queue 中    // 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(); // 启动服务进程        }

3 解析启动脚本init.rc

3.1init.rc文件介绍

Init.rc脚本使用的是一种初始化语言,其中包含了4类声明:

1)Action

2)Command

3)Service

4)Option

该语言规定,Action和Service是以一种“小节”(Section)的形式出现的,其中每个Action小节可以含有若干Command,而每个Service小节可以含有若干Option。小节只有起始标记,却没有明确的结束标记,也就是说,是用“后一个小节”的起始来结束“前一个小节”的。

 

脚本中的Action大体上表示一个“行动”,它用一系列Command共同完成该“行动”。Action需要有一个触发器(trigger)来触发它,一旦满足了触发条件,这个Action就会被加到执行队列的末尾。Action的形式如下:

on  <trigger> // trigger 是触发器 <command1>  // 命令 <command2>

例如,

on early-init    # Set init and its forked children's oom_adj.    write /proc/1/oom_score_adj -1000    # Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls.    write /sys/fs/selinux/checkreqprot 0    # Set the security context for the init process.    # This should occur before anything else (e.g. ueventd) is started.    setcon u:r:init:s0    # Set the security context of /adb_keys if present.    restorecon /adb_keys    start ueventd    # create mountpoints    mkdir /mnt 0775 root system

Service表示一个服务程序,会在初始化时启动。因为init.rc脚本中描述的服务往往都是核心服务,所以(基本上所有的)服务会在退出时自动重启。Service的形式如下:

service <name> <pathname> [<arguments>]* <option> // 决定服务何时以及如何运行 <option>

例如,

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

其实,除了Action和Service,Init.rc中还有一种小节,就是Import小节。该小节表达的意思有点儿像java中的import,也就是说,Init.rc中还可以导入其他.rc脚本文件的内容。

例如,

import /init.environ.rcimport /init.usb.rcimport /init.${ro.hardware}.rcimport /init.${ro.zygote}.rcimport /init.trace.rc

3.2解析

解析流程图如下,



主要解析init.rc以及其中import的文件,转化为两个双向链表,分别保存action_list和service_list中。

list_add_tail(&action_list, &act->alist);

list_add_tail(&service_list, &svc->slist);

经过解析一步,init.rc脚本中的actions被整理成双向链表了,但是这些action并没有被实际执行。

3.3执行action

在main方法中,

action_for_each_trigger("early-init", action_add_queue_tail);•••action_for_each_trigger("init", action_add_queue_tail);•••action_for_each_trigger("late-init", action_add_queue_tail);

手机的启动按照先后顺序分为很多子阶段。

action_for_each_trigger方法如下,

void action_for_each_trigger(const char *trigger,                             void (*func)(struct action *act)){    struct listnode *node, *node2;    struct action *act;    struct trigger *cur_trigger;    list_for_each(node, &action_list) {        act = node_to_item(node, struct action, alist);        list_for_each(node2, &act->triggers) {            cur_trigger = node_to_item(node2, struct trigger, nlist);            if (!strcmp(cur_trigger->name, trigger)) {                func(act);            }        }    }}

可以看到是在遍历action_list链表,找寻所有“action名”和“参数trigger”匹配的节点,并回调“参数func所指的回调函数”。

 

以boot子阶段为例来解析调用流程, boot子阶段是通过late-init触发的,

 init.rc中的boot部分代码如下,

on boot•••class_start core

Commands指令关键字和对应执行的方法在system/core/init/Keywords.h中定义,

  KEYWORD(class_start, COMMAND, 1, do_class_start)    KEYWORD(class_stop,  COMMAND, 1, do_class_stop)

3.4启动service

class_start关键字对应的方法为do_class_start,

system/core/init/Builtins.cpp中的do_class_start流程图如下,


和执行action的流程完全一模一样,在此会启动标签为core的一类服务,

例如,

service ueventd /sbin/ueventd   // 可执行文件    class core    critical    seclabel u:r:ueventd:s0  // 设置属性

Init.rc中core服务以及对应的执行文件如下,

ueventd

/sbin/ueventd

logd

/system/bin/logd

healthd

/sbin/healthd

console

/system/bin/sh

adbd

/sbin/adbd

lmkd

/system/bin/lmkd

servicemanager

/system/bin/servicemanager

vold

/system/bin/vold

surfaceflinger

/system/bin/surfaceflinger

bootanim

/system/bin/bootanimation


Init.rc中main服务以及对应的执行文件如下,

netd

/system/bin/netd

debuggerd

/system/bin/debuggerd

debuggerd64

/system/bin/debuggerd64

ril-daemon

/system/bin/rild

drm

/system/bin/drmserver

media

/system/bin/mediaserver

installd

/system/bin/installd

flash_recovery

/system/bin/install-recovery.sh

racoon

/system/bin/racoon

mtpd

/system/bin/mtpd

keystore

/system/bin/keystore /data/misc/keystore

dumpstate

/system/bin/dumpstate

mdnsd

/system/bin/mdnsd

uncrypt

/system/bin/uncrypt

pre-recovery

/system/bin/uncrypt

zygote

/system/bin/app_process


service_start方法步骤如下,

1,重置service结构中的标志

svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));    svc->time_started = 0;    // Running processes require no additional work --- if they're in the    // process of exiting, we've ensured that they will immediately restart    // on exit, unless they are ONESHOT.    if (svc->flags & SVC_RUNNING) {        return;    }

2,判断服务是否需要控制台

bool needs_console = (svc->flags & SVC_CONSOLE);    if (needs_console && !have_console) {        ERROR("service '%s' requires console\n", svc->name);        svc->flags |= SVC_DISABLED;        return;    }

3,检查服务的二进制文件是否存在

 struct stat s;    if (stat(svc->args[0], &s) != 0) {        ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);        svc->flags |= SVC_DISABLED;        return;    }
4,检查SVC_ONESHOT参数

if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) {        ERROR("service '%s' must be one-shot to use dynamic args, disabling\n",               svc->args[0]);        svc->flags |= SVC_DISABLED;        return;    }

5,设置安全上下文

char* scon = NULL;    if (is_selinux_enabled() > 0) {        if (svc->seclabel) {            scon = strdup(svc->seclabel);            if (!scon) {                ERROR("Out of memory while starting '%s'\n", svc->name);                return;            }        } else {•••

6,fork子进程

pid_t pid = fork();

7,准备环境变量

if (properties_initialized()) {            get_property_workspace(&fd, &sz);            snprintf(tmp, sizeof(tmp), "%d,%d", dup(fd), sz);            add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);        }        for (ei = svc->envvars; ei; ei = ei->next)            add_environment(ei->name, ei->value);

8,创建socket

for (si = svc->sockets; si; si = si->next) {            int socket_type = (                    !strcmp(si->type, "stream") ? SOCK_STREAM :                        (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));            int s = create_socket(si->name, socket_type,                                  si->perm, si->uid, si->gid, si->socketcon ?: scon);            if (s >= 0) {                publish_socket(si->name, s);            }        }

9,处理标准输出,输入,错误三个文件描述符

if (needs_console) {            setsid();            open_console();        } else {            zap_stdio();        }

10,执行execve

if (!dynamic_args) {            if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {                ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));            }        } else {            char *arg_ptrs[INIT_PARSER_MAXARGS+1];            int arg_idx = svc->nargs;            char *tmp = strdup(dynamic_args);            char *next = tmp;            char *bword;            /* Copy the static arguments */            memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));            while((bword = strsep(&next, " "))) {                arg_ptrs[arg_idx++] = bword;                if (arg_idx == INIT_PARSER_MAXARGS)                    break;            }            arg_ptrs[arg_idx] = NULL;            execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);        }

4 信号处理

系统中的所有其他进程都是init进程的后代,因此,init进程需要在这些后代死亡时负责清理他们,已防止他们变为僵尸进程。

僵尸进程危害:Linux系统对每个用户能运行的进程数量是有限制的,超过限制以后再创建新的进程将会失败。其实,僵尸进程仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息,僵尸进程并不占用任何内存空间。

流程图如下,


4.1 初始化SIGCHLD信号

signal_handler_init方法如下,

void signal_handler_init() {    // Create a signalling mechanism for SIGCHLD.    int s[2];    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {        ERROR("socketpair failed: %s\n", strerror(errno));        exit(1);    }    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; // 信号标志,当子进程终止时才会接收SIGCHLD信号    sigaction(SIGCHLD, &act, 0);    reap_any_outstanding_children();    register_epoll_handler(signal_read_fd, handle_signal); // 回到方法}
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));    }}

4.2 处理SIGCHLD信号

Init进程启动完毕后,会监听创建的socket,如果有数据到来,主线程会唤醒并调用handle_signal方法。当进程调用exit()方法退出时,会向父进程发出SIGCHLD信号.父进程收到信号后,会释放分配给子进程的资源。如果未发出SIGCHLD信号或者父进程未处理,那么子进程会一直保持当前的退出状态,成为僵尸进程。

handle_signal方法如下,

static void handle_signal() {    // Clear outstanding requests.    char buf[32];    read(signal_read_fd, buf, sizeof(buf));    reap_any_outstanding_children();}
static void reap_any_outstanding_children() {    while (wait_for_one_process()) { // 循环调用, wait_for_one_process一次仅处理一个子进程。    }}

wait_for_one_process方法步骤如下,

1,调用waitpid方法等待死亡子进程结束。

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;    }

2,检查死亡进程是否位于服务列表中

service* svc = service_find_by_pid(pid);    std::string name;    if (svc) {        name = android::base::StringPrintf("Service '%s' (pid %d)", svc->name, pid);    } else {        name = android::base::StringPrintf("Untracked pid %d", pid);    }    NOTICE("%s %s\n", name.c_str(), DescribeStatus(status).c_str());    if (!svc) {        return true;    }

3,如果死亡进程是服务进程,而且需要重启,会杀掉死亡进程的所有子进程。这样当服务进程重启的时候,不会因为子进程已经存在而导致错误。

if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {        NOTICE("Service '%s' (pid %d) killing any children in process group\n", svc->name, pid);        kill(-pid, SIGKILL);    }

4,清理死亡子进程的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);}

5,设置服务进程的属性

svc->pid = 0;    svc->flags &= (~SVC_RUNNING);    // Oneshot processes go into the disabled state on exit,    // except when manually restarted.    if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) {        svc->flags |= SVC_DISABLED;    }•••

6,到这一步骤意味着进程会重启,并加上重启标志。

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->flags &= (~SVC_RESTART);    svc->flags |= SVC_RESTARTING;    // Execute all onrestart commands for this 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);    }

5小结

关于init进程,就先说这么多吧。当然还有很多知识点并没有说到,以后有机会可以逐个解析说明。

0 0