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进程,就先说这么多吧。当然还有很多知识点并没有说到,以后有机会可以逐个解析说明。
- init进程源码分析
- init进程源码分析
- Android Init进程源码分析
- Android Init进程源码分析
- androidL init 进程源码分析
- Android Init进程源码分析
- Android Init进程源码分析
- Android Init进程源码分析
- Android Init进程源码分析
- Android Init进程源码分析
- Android init 进程源码分析
- Android init进程——源码分析
- Android3.1 init进程启动源码分析
- Android7.0 init进程源码分析
- Android7.0 init进程源码分析
- init 进程源码分析(2013-05-05)
- Android M 启动源码分析笔记之 - Init 进程
- android init 进程分析
- jfinal中多文件上传的一个问题
- 关于PHP文件上传的一些知识点步骤整理
- 算法训练 前缀表达式
- 这些年,我身边的那些人和事
- Java NIO框架Netty教程(五)-Netty中OIO模型(对比NIO)
- init进程源码分析
- QTP自动化测试踩过的坑1
- Java多线程之对象及变量的并发访问
- Android 自定义 View 之 draw 原理分析
- webpack解决‘config unkown property postcss’
- asp.net mvc checkboxlist的简单实现
- 匈牙利算法求二分图的最大匹配
- Continuation 异步化机制
- Android开发之ViewPager