init进程源码分析

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 symlinks
LOCAL_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.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.${ro.zygote}.rc
import /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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值