Android系统在加载linux内核后,便启动第一个进程————init进程。
init进程的入口为main函数,文件路径位于/system/core/init/init.cpp中。该进程的执行可分为两个阶段。
/system/core/init/init.cpp
int main(int argc, char** argv) {
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
...
argv[0]是程序的全路径名,如果文件名(basebname表示去掉路径和后缀名)是ueventd,则进入ueventd_main函数。ueventd主要负责创建设备节点文件。
/system/core/init/init.cpp
int ueventd_main(int argc, char **argv)
{
/*
* init sets the umask to 077 for forked processes. We need to
* create files with exact permissions, without modification by
* the umask.
*/
//使创建的设备文件具有完全权限
umask(000);
/* Prevent fire-and-forget children from becoming zombies.
* If we should need to wait() for some children in the future
* (as opposed to none right now), double-forking here instead
* of ignoring SIGCHLD may be the better solution.
*/
//对子进程退出的信号处理设置为忽略
signal(SIGCHLD, SIG_IGN);
//SELinux为我们提供了一个/dev/null,这里将标准输入,标准输出,标准错误输出均重定位到/dev/null
open_devnull_stdio();
//打开/dev/kmsg日志设备
klog_init();
//设置kernel log等级
klog_set_level(KLOG_NOTICE_LEVEL);
NOTICE("ueventd started!\n");
//与SEAndroid有关
selinux_callback cb;
cb.func_log = selinux_klog_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
//hardware保存了ro.hardware的属性值
char hardware[PROP_VALUE_MAX];
property_get("ro.hardware", hardware);
//解析文件ueventd.rc
ueventd_parse_config_file("/ueventd.rc");
//解析文件ueventd.(hardware).rc
ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware).c_str());
//设备初始化
device_init();
//监听设备上的事件
pollfd ufd;
ufd.events = POLLIN;
ufd.fd = get_device_fd();
while (true) {
ufd.revents = 0;
int nr = poll(&ufd, 1, -1);
if (nr <= 0) {
continue;
}
if (ufd.revents & POLLIN) {
handle_device_fd();
}
}
return 0;
}
继续分析main函数。
/system/core/init/init.cpp
//启动watchdog
if (!strcmp(basename(argv[0]), "watchdogd")) {
return watchdogd_main(argc, argv);
}
// Clear the umask.
umask(0);
//添加环境变量
add_environment("PATH", _PATH_DEFPATH);
//没有额外参数或者第一个参数不是"--second-stage"启动时为第一阶段
bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
如果处于第一阶段,init进程会挂载一些目录,将tmpfs(虚拟内存文件系统,由Real Memory和swap构成)挂载在/dev上,devpts(远程虚拟终端)挂载在/dev/pts上,proc(访问内核信息的虚拟文件系统)挂载在/proc上,sysfs(设备驱动模型文件系统)挂载到/sys上。
/system/core/init/init.cpp
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);
}
/system/core/init/init.cpp
open_devnull_stdio();
klog_init();
klog_set_level(KLOG_NOTICE_LEVEL);
...
selinux_initialize(is_first_stage);
/system/core/init/init.cpp
static void selinux_initialize(bool in_kernel_domain) {
Timer t;
//设置回调函数
selinux_callback cb;
cb.func_log = selinux_klog_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
cb.func_audit = audit_callback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
//SElinux没有启动,直接返回
if (selinux_is_disabled()) {
return;
}
//第一阶段
if (in_kernel_domain) {
//加载SELinux策略
INFO("Loading SELinux policy...\n");
if (selinux_android_load_policy() < 0) {
ERROR("failed to load policy: %s\n", strerror(errno));
security_failure();
}
//读取/proc/cmdline文件,根据androidboot.selinux的值决定返回值
bool is_enforcing = selinux_is_enforcing();
security_setenforce(is_enforcing);
//向/sys/fs/selinux/checkreqprot写入1
if (write_file("/sys/fs/selinux/checkreqprot", "0") == -1) {
security_failure();
}
NOTICE("(Initializing SELinux %s took %.2fs.)\n",
is_enforcing ? "enforcing" : "non-enforcing", t.duration());
} else {//第二阶段
selinux_init_all_handles();
}
}
看看selinux_is_enforcing()函数。
/system/core/init/init.cpp
static bool selinux_is_enforcing(void)
{
if (ALLOW_DISABLE_SELINUX) {
return selinux_status_from_cmdline() == SELINUX_ENFORCING;
}
return true;
}
/system/core/init/init.cpp
static selinux_enforcing_status selinux_status_from_cmdline() {
selinux_enforcing_status status = SELINUX_ENFORCING;
std::function<void(char*,bool)> fn = [&](char* name, bool in_qemu) {
char *value = strchr(name, '=');
if (value == nullptr) { return; }
*value++ = '\0';
if (strcmp(name, "androidboot.selinux") == 0) {
if (strcmp(value, "disabled") == 0) {
status = SELINUX_DISABLED;
} else if (strcmp(value, "permissive") == 0) {
status = SELINUX_PERMISSIVE;
}
}
};
import_kernel_cmdline(false, fn);
return status;
}
import_kernel_cmdline的作用在于读取/proc/cmdline文件的内容,以’ ’ 为分隔符,分割成一系列的字符串,依次作为第二个参数表示的函数的第一个参数传入。
selinux_status_from_cmdline函数中的fn函数将这些分割过后的字符串再次以’=’作为分隔符进行分割,依照androidboot.selinux的值返回不同的值,默认是返回SELINUX_ENFORCING状态值。
/system/core/init/init.cpp
void import_kernel_cmdline(bool in_qemu, std::function<void(char*,bool)> import_kernel_nv)
{
char cmdline[2048];
char *ptr;
int fd;
fd = open("/proc/cmdline", O_RDONLY | O_CLOEXEC);
if (fd >= 0) {
int n = read(fd, cmdline, sizeof(cmdline) - 1);
if (n < 0) n = 0;
/* get rid of trailing newline, it happens */
if (n > 0 && cmdline[n-1] == '\n') n--;
cmdline[n] = 0;
close(fd);
} else {
cmdline[0] = 0;
}
ptr = cmdline;
while (ptr && *ptr) {
char *x = strchr(ptr, ' ');
if (x != 0) *x++ = 0;
import_kernel_nv(ptr, in_qemu);
ptr = x;
}
}
返回main函数。
/system/core/init/init.cpp
if (is_first_stage) {
//恢复SELinux文件属性即恢复文件的安全上下文
if (restorecon("/init") == -1) {
ERROR("restorecon failed: %s\n", strerror(errno));
security_failure();
}
char* path = argv[0];
char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
//重新执行main函数,并传入"--second-stage"参数,进入第二阶段。execv会停止执行当前的进程,并且以新的应用进程替换被停止执行的进程,进程ID没有改变。
if (execv(path, args) == -1) {
ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
security_failure();
}
}
除了一部分是第一阶段和第二阶段都要执行的操作外,很多property的初始化是在第二阶段完成的。
/system/core/init/init.cpp
if (!is_first_stage) {
// Indicate that booting is in progress to background fw loaders, etc.
//测试/dev/.booting是否正常打开关闭
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
//打开/dev/__properties__文件
property_init();
// If arguments are passed both on the command line and in DT,
// properties set in DT always have priority over the command-line ones.
//先初始化DT,DT优先级比cmdline高
process_kernel_dt();
process_kernel_cmdline();
// Propogate the kernel variables to internal variables
// used by init as well as the current required properties.
export_kernel_boot_props();
}
读取/proc/device-tree/firmware/android/compatible文件内容,如果是”android,firmware”则报错直接返回。dir为打开/proc/device-tree/firmware/android目录的DIR类型,unique_ptr第二个模板参数为删除器。接下来读取目录下的文件,跳过文件类型不是DT_REG或者文件名为”compatible”的文件。依次读这些文件内容,将里面的’,’换成’.’。最后以ro.boot.(文件名)为key,读取的文件内容为value设置property。
/system/core/init/init.cpp
static void process_kernel_dt(void)
{
static const char android_dir[] = "/proc/device-tree/firmware/android";
std::string file_name = android::base::StringPrintf("%s/compatible", android_dir);
std::string dt_file;
android::base::ReadFileToString(file_name, &dt_file);
if (!dt_file.compare("android,firmware")) {
ERROR("firmware/android is not compatible with 'android,firmware'\n");
return;
}
std::unique_ptr<DIR, int(*)(DIR*)>dir(opendir(android_dir), closedir);
if (!dir)
return;
struct dirent *dp;
while ((dp = readdir(dir.get())) != NULL) {
if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible"))
continue;
file_name = android::base::StringPrintf("%s/%s", android_dir, dp->d_name);
android::base::ReadFileToString(file_name, &dt_file);
std::replace(dt_file.begin(), dt_file.end(), ',', '.');
std::string property_name = android::base::StringPrintf("ro.boot.%s", dp->d_name);
property_set(property_name.c_str(), dt_file.c_str());
}
}
前面提到import_kernel_cmdline将/proc/cmdline文件内容按’ ‘分割开来,这里将分割后的字符串传给import_kernel_nv的name参数。这里讲name按’=’进行分割,name设为’=’号前面部分,value设为’=’后面部分。name为”qemu”时,将对应的value拷贝到qemu数组中。name的前12个字符为”androidboot.”时,boot_prop_name记为name中”androidboot.”后面部分,以ro.boot.(boot_prop_name)为key,value字符串为value设置property。
/system/core/init/init.cpp
static void process_kernel_cmdline(void)
{
/* don't expose the raw commandline to nonpriv processes */
chmod("/proc/cmdline", 0440);
/* first pass does the common stuff, and finds if we are in qemu.
* second pass is only necessary for qemu to export all kernel params
* as props.
*/
import_kernel_cmdline(false, import_kernel_nv);
if (qemu[0])
import_kernel_cmdline(true, import_kernel_nv);
}
/system/core/init/init.cpp
static void import_kernel_nv(char *name, bool for_emulator)
{
char *value = strchr(name, '=');
int name_len = strlen(name);
if (value == 0) return;
*value++ = 0;
if (name_len == 0) return;
if (for_emulator) {
/* in the emulator, export any kernel option with the
* ro.kernel. prefix */
char buff[PROP_NAME_MAX];
int len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name );
if (len < (int)sizeof(buff))
property_set( buff, value );
return;
}
if (!strcmp(name,"qemu")) {
strlcpy(qemu, value, sizeof(qemu));
} else if (!strncmp(name, "androidboot.", 12) && name_len > 12) {
const char *boot_prop_name = name + 12;
char prop[PROP_NAME_MAX];
int cnt;
cnt = snprintf(prop, sizeof(prop), "ro.boot.%s", boot_prop_name);
if (cnt < PROP_NAME_MAX)
property_set(prop, value);
}
}
export_kernel_boot_props用于根据prop_map这个结构体数组设置property。以src_prop为key获取属性值,保存在value中。若获取成功,以dst_prop为key,value值为value设置property。若获取不成功,则以dst_prop为key,对应的default_value为value设置property。
/system/core/init/init.cpp
static void export_kernel_boot_props() {
struct {
const char *src_prop;
const char *dst_prop;
const char *default_value;
} prop_map[] = {
{ "ro.boot.serialno", "ro.serialno", "", },
{ "ro.boot.mode", "ro.bootmode", "unknown", },
{ "ro.boot.baseband", "ro.baseband", "unknown", },
{ "ro.boot.bootloader", "ro.bootloader", "unknown", },
{ "ro.boot.hardware", "ro.hardware", "unknown", },
{ "ro.boot.revision", "ro.revision", "0", },
};
for (size_t i = 0; i < ARRAY_SIZE(prop_map); i++) {
char value[PROP_VALUE_MAX];
int rc = property_get(prop_map[i].src_prop, value);
property_set(prop_map[i].dst_prop, (rc > 0) ? value : prop_map[i].default_value);
}
}
接着回到main函数分析init的第二阶段。接着创建一个epoll句柄。
/system/core/init/init.cpp
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {
ERROR("epoll_create1 failed: %s\n", strerror(errno));
exit(1);
}
socketpair函数建立一对匿名的已经连接的套接字,相当于创建一个双向管道。不过这里将s[0]作为写端,s[1]作为读端。接下来对SIGCHLD信号作处理,SIGCHLD_handler作为信号处理函数,功能是往s[0]端写入一个数字”1”。SA_NOCLDSTOP表示进程若处于停止状态,不对信号作处理。reap_any_outstanding_children对之前退出的子进程作处理。register_epoll_handler将是s[0]套接字注册到epoll中,监听其EPOLLIN事件。例如,当servicemanager退出时,会向父进程init发送一个SIGCHLD信号,信号处理函数SIGCHLD_handler会向管道里写入一个”1”,因为管道读端已被epoll监控,所以相应的epoll_wait会返回。register_epoll_handler在注册epoll_event时同时也在epoll_event的联合体成员epoll_data中保存了handle_signal的函数指针,这样在别的地方epoll_wait返回之后便可以获得这个函数指针。handle_signal中调用了reap_any_outstanding_children,会对退出的子进程对应的service作处理,在reap_any_outstanding_children里面的wait_for_one_process的末尾会进行restart zygote的操作。
/system/core/init/init.cpp
signal_handler_init();
/system/core/init/signal_handler.cpp
static void handle_signal() {
// Clear outstanding requests.
char buf[32];
read(signal_read_fd, buf, sizeof(buf));
reap_any_outstanding_children();
}
/system/core/init/init.cpp
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;
sigaction(SIGCHLD, &act, 0);
reap_any_outstanding_children();
register_epoll_handler(signal_read_fd, handle_signal);
}
看看reap_any_outstanding_children如何处理退出的子进程。
/system/core/init/signal_handler.cpp
static void reap_any_outstanding_children() {
while (wait_for_one_process()) {
}
}
wait_for_one_process中,首先使用waitpid等待子进程结束,参数中-1表示等待任意的子进程,WNOHANG为非阻塞标志。根据waitpid返回的子进程pid,找到对应的struct service变量svc。如果svc标志位有SVC_RESTART,或者没有SVC_ONESHOT,杀掉进程组号为pid的所有进程,因为这些进程都是需要自动重启的,之前的进程组也就没有必要留下来了。
kill函数的pid参数决定其函数功能。1.pid大于零时,pid是信号欲送往的进程的标识。2. pid等于零时,信号将送往所有与调用kill()的那个进程属同一个使用组的进程。3. pid等于-1时,信号将送往所有调用进程有权给其发送信号的进程,除了进程1(init)。4. pid小于-1时,信号将送往以-pid为组标识的进程。
struct service的sockets成员是一个链表结构。tmp字符串为”/dev/socket/(svc->name)”。之后使用unlink删除这些tmp字符串表示的套接字文件。
如果svc有SVC_EXEC标志位,说明这个service已经执行完毕,则释放掉svc,返回true。之后将svc的pid成员置0,取消svc的SVC_RUNNING标志位。如果svc设有SVC_ONESHOT标志位而没有设SVC_RESTART标志位,为svc增设SVC_DISABLED标志位。如果svc同时设立了SVC_DISABLED和SVC_RESET标志位,增设key为init.svc.(svc->name),value为”stopped”的property,返回true。如果svc设立了SVC_CRITICAL和SVC_RESTART标志,4分钟内发生crash次数大于4次,则重启设备进入recovery。
之后取消svc的SVC_RESTART标志,增设SVC_RESTARTING的标志。svc->onrestart.commands是重启要执行的commands链表,具体表现为init.rc里面service部分关于onrestart的选项。接下来将这些commands依次执行。设置“restarting”的property之后,返回true。设置了SVC_RESTARTING的标志的service会在主循环中根据具体情况被重启。
/system/core/init/signal_handler.cpp
static bool wait_for_one_process() {
int status;
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;
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;
}
// TODO: all the code from here down should be a member function on service.
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);
}
// Remove any sockets we may have created.
for (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;
}
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;
}
// Disabled and reset processes do not get restarted automatically.
if (svc->flags & (SVC_DISABLED | SVC_RESET)) {
svc->NotifyStateChange("stopped");
return true;
}
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);
}
svc->NotifyStateChange("restarting");
return true;
}
回到main函数。init_parse_config_file用来解析init.rc文件。解析规则大致说明一下:
init.rc文件包含三部分内容:import,Action,Service。import类似于C语言中的include,将其他rc文件load进来读取。Action的语法格式如下,对应struct action。trigger是触发器,满足这个触发器就会执行下面的command。init.rc目前支持五类触发器。
1.boot
这是init执行后第一个被触发Trigger,也就是在/init.rc被装载之后执行该Trigger。
2.name=value
当属性name被设置成value时触发。例如:onproperty:vold.decrypt=trigger_reset_main。
3.device-added
当设备节点被添加时触发。
4.device-removed
当设备节点被移除时添加。
5.service-exited
会在一个特定的服务退出时触发。
Services的选项(option)是服务的修饰符,可以影响服务如何以及怎样运行。服务支持的选项如下:
1.critical
表明这是一个非常重要的服务。如果该服务4分钟内退出大于4次,系统将会重启并进入 Recovery (恢复)模式。
2.disabled
表明这个服务不会自动启动。该服务必须被明确的按名启动。
3.setenv
在进程启动时设置环境变量。
4.socket {name type perm user group}
创建一个unix域的名为/dev/socket/(name) 的套接字,并传递它的文件描述符给已启动的进程。type 必须是 “dgram”,”stream” 或”seqpacket”。用户user和组group默认是0。
5.user {username}
在启动这个服务前改变该服务的用户名。此时默认为 root。
6.group {groupname} {groupname}
在启动这个服务前改变该服务的组名。除了(必需的)第一个组名,附加的组名通常被用于设置进程的补充组(通过setgroups函数),默认是root。
7.oneshot
服务退出时不重启。
8.class name
指定一个服务类。所有同一类的服务可以同时启动和停止。如果不通过class选项指定一个类,则默认为”default”类服务。在Action语法中,我们可以使用class_start, class_reset, class_stop等操作来操作这些同属一类的service。
9.onrestart
当服务重启,执行一个命令。
/system/core/init/init.cpp
init_parse_config_file("/init.rc");
/system/core/rootdir/init.rc
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.${ro.zygote}.rc
import /init.trace.rc
Action语法格式
on <trigger>
<command>
<command>
<command>
Service语法格式
service <name> <pathname> [ <argument> ]*
<option>
<option>
回到main函数。action_list是所有解析出来的 action的alist成员组成的链表,这些alist在读取init.rc的时候被加入到链表中,链表记录着所有的action。node_to_item根据链表每个alist的地址确定action的地址act。
遍历action_list中的所有对应的action,再遍历每个action中的trigger链表,找到trigger name为”early-init”对应的action。使用action_add_queue_tail将这个action的qlist成员添加到action_queue中。
/system/core/init/init.cpp
action_for_each_trigger("early-init", action_add_queue_tail);
/system/core/init/init_parser.cpp
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);
}
}
}
}
返回到main函数中。接下来调用四个queue_builtin_action更新action_list和action_queue链表。queue_builtin_action分配了一个action,trigger,command。trigger的name为第二个参数name,添加到action的triggers链表中。command的func为第一个参数func,args[0]为第二个参数name,nargs为1,表示这个command是一个以nargs和args[0]为参数的函数。将该command添加到action的commands链表中。最后,将这些初始化了trigger和command的action的alist成员添加到action_list中,qlist成员添加到action_queue中。
/system/core/init/init_parser.cpp
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");
/system/core/init/init_parser.cpp
void queue_builtin_action(int (*func)(int nargs, char **args), const char *name)
{
action* act = (action*) calloc(1, sizeof(*act));
trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
cur_trigger->name = name;
list_init(&act->triggers);
list_add_tail(&act->triggers, &cur_trigger->nlist);
list_init(&act->commands);
list_init(&act->qlist);
command* cmd = (command*) calloc(1, sizeof(*cmd));
cmd->func = func;
cmd->args[0] = const_cast<char*>(name);
cmd->nargs = 1;
list_add_tail(&act->commands, &cmd->clist);
list_add_tail(&action_list, &act->alist);
action_add_queue_tail(act);
}
可见,在”early-init”这一阶段,将trigger为”early-init”的action和新建的command分别为wait_for_coldboot_done_action,mix_hwrng_into_linux_rng_action,keychord_init_action,console_init_action的action加入到action_queue中去。
init阶段可以按同样的方法分析。
/system/core/init/init.cpp
action_for_each_trigger("init", action_add_queue_tail);
// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
// wasn't ready immediately after wait_for_coldboot_done
queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
ro.bootmode的属性值为”charger”时,表示要进入充电模式,此时将trigger的name为charger的action添加到action_queue中去。否则,进入到late-init阶段。值得指出的是,这时候action_list里面是解析init.rc得到的和内建的action,action_queue里面是使用action_for_each_trigger和queue_builtin_action添加进去的action,在execute_one_command中被依次取出执行。
/system/core/init/init.cpp
char bootmode[PROP_VALUE_MAX];
if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) {
action_for_each_trigger("charger", action_add_queue_tail);
} else {
action_for_each_trigger("late-init", action_add_queue_tail);
}
// Run all property triggers based on current state of the properties.
queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
接下来进入循环部分。
/system/core/init/init.cpp
while (true) {
if (!waiting_for_exec) {
execute_one_command();
restart_processes();
}
int timeout = -1;
if (process_needs_restart) {
timeout = (process_needs_restart - gettime()) * 1000;
if (timeout < 0)
timeout = 0;
}
if (!action_queue_empty() || cur_action) {
timeout = 0;
}
bootchart_sample(&timeout);
epoll_event ev;
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
if (nr == -1) {
ERROR("epoll_wait failed: %s\n", strerror(errno));
} else if (nr == 1) {
((void (*)()) ev.data.ptr)();
}
}
return 0;
}
waiting_for_exec是一个bool变量,它的值被初始化为false,只会在下面的restart_processes函数中改变。首先调用的是execute_one_command函数。action_remove_queue_head从action_queue取下头结点对应的action赋给cur_action。get_first_command取出cur_action的command节点的下一个作为cur_command。is_last_command判断cur_command的下一个command节点是否是cur_action的command节点,如果是返回true,则说明对cur_action的command链表已经遍历完毕。当is_last_command返回false,且cur_action和cur_command均存在对应节点时,使用get_next_command获取cur_command链表的下一个作为cur_command,cur_command链表遍历完时返回null,cur_command为null就重新从action_queue中摘下一个新的action头节点开始新的遍历。拿到一个cur_command后,以command的成员nargs和args为参数调用command的成员func函数。
/system/core/init/init.cpp
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;
}
int result = cur_command->func(cur_command->nargs, cur_command->args);
if (klog_get_level() >= KLOG_INFO_LEVEL) {
for (int i = 0; i < cur_command->nargs; i++) {
strlcat(cmd_str, cur_command->args[i], sizeof(cmd_str));
if (i < cur_command->nargs - 1) {
strlcat(cmd_str, " ", sizeof(cmd_str));
}
}
char source[256];
if (cur_command->filename) {
snprintf(source, sizeof(source), " (%s:%d)", cur_command->filename, cur_command->line);
} else {
*source = '\0';
}
INFO("Command '%s' action=%s%s returned %d took %.2fs\n",
cmd_str, cur_action ? name_str : "", source, result, t.duration());
}
}
service_list在读取init.rc的时候被添加了各种service。service_for_each_flags就是遍历service_list链表,对其中有SVC_RESTARTING标志的service调用restart_service_if_needed函数。process_needs_restart是一个time_t变量,标记了service重启的预定时间,service的time_t成员time_started标记了上次service启动的事件。restart_service_if_needed将next_start_time设为time_started + 5,若next_start_time比当前时间小,则立刻启动service。否则,若next_start_time比process_needs_restart小或者process_needs_restart为0时,将process_needs_restart设置为next_start_time。
/system/core/init/init.cpp
static void restart_processes()
{
process_needs_restart = 0;
service_for_each_flags(SVC_RESTARTING,
restart_service_if_needed);
}
/system/core/init/init_parser.cpp
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);
}
}
}
/system/core/init/init.cpp
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);
service_start(svc, NULL);
return;
}
if ((next_start_time < process_needs_restart) ||
(process_needs_restart == 0)) {
process_needs_restart = next_start_time;
}
}
service_start中,首先取消掉SVC_DISABLED,SVC_RESTARTING,SVC_RESET,SVC_RESTART,SVC_DISABLED_START的标志位,time_started记为0。当svc设置了标志位SVC_RUNNING,直接返回,因为已经在运行中。当svc设置了标志位SVC_CONSOLE又没有对应的console时,设置标志位SVC_DISABLED,本次不需要启动该service,返回。
读取service的args成员信息失败时,设置标志位SVC_DISABLED,返回。如果没有设置SVC_ONESHOT而又传进了dynamic_args时,设置标志位SVC_DISABLED,返回。这个dynamic_args是用来手动启动设置了SVC_ONESHOT的service的。
接下来是对SELinux处理的内容,此处先跳过。接下来fork出一个子进程,在子进程设置了一些service的属性,并运行这个service。父进程将time_started设为当前时间,并为service添加SVC_RUNNING标志位,将waiting_for_exec设成true,最后将这个service对应的属性值设为”running”。
/system/core/init/init.cpp
void service_start(struct service *svc, const char *dynamic_args)
{
// Starting a service removes it from the disabled or reset state and
// immediately takes it out of the restarting state if it was in there.
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;
}
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;
}
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;
}
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;
}
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 {
char *mycon = NULL, *fcon = NULL;
INFO("computing context for service '%s'\n", svc->args[0]);
int rc = getcon(&mycon);
if (rc < 0) {
ERROR("could not get context while starting '%s'\n", svc->name);
return;
}
rc = getfilecon(svc->args[0], &fcon);
if (rc < 0) {
ERROR("could not get context while starting '%s'\n", svc->name);
freecon(mycon);
return;
}
rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
if (rc == 0 && !strcmp(scon, mycon)) {
ERROR("Warning! Service %s needs a SELinux domain defined; please fix!\n", svc->name);
}
freecon(mycon);
freecon(fcon);
if (rc < 0) {
ERROR("could not get context while starting '%s'\n", svc->name);
return;
}
}
}
NOTICE("Starting service '%s'...\n", svc->name);
pid_t pid = fork();
if (pid == 0) {
struct socketinfo *si;
struct svcenvinfo *ei;
char tmp[32];
int fd, sz;
umask(077);
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);
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);
}
}
freecon(scon);
scon = NULL;
if (svc->writepid_files_) {
std::string pid_str = android::base::StringPrintf("%d", pid);
for (auto& file : *svc->writepid_files_) {
if (!android::base::WriteStringToFile(pid_str, file)) {
ERROR("couldn't write %s to %s: %s\n",
pid_str.c_str(), file.c_str(), strerror(errno));
}
}
}
if (svc->ioprio_class != IoSchedClass_NONE) {
if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno));
}
}
if (needs_console) {
setsid();
open_console();
} else {
zap_stdio();
}
if (false) {
for (size_t n = 0; svc->args[n]; n++) {
INFO("args[%zu] = '%s'\n", n, svc->args[n]);
}
for (size_t n = 0; ENV[n]; n++) {
INFO("env[%zu] = '%s'\n", n, ENV[n]);
}
}
setpgid(0, getpid());
// As requested, set our gid, supplemental gids, and uid.
if (svc->gid) {
if (setgid(svc->gid) != 0) {
ERROR("setgid failed: %s\n", strerror(errno));
_exit(127);
}
}
if (svc->nr_supp_gids) {
if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) {
ERROR("setgroups failed: %s\n", strerror(errno));
_exit(127);
}
}
if (svc->uid) {
if (setuid(svc->uid) != 0) {
ERROR("setuid failed: %s\n", strerror(errno));
_exit(127);
}
}
if (svc->seclabel) {
if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) {
ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno));
_exit(127);
}
}
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);
}
_exit(127);
}
freecon(scon);
if (pid < 0) {
ERROR("failed to start '%s'\n", svc->name);
svc->pid = 0;
return;
}
svc->time_started = gettime();
svc->pid = pid;
svc->flags |= SVC_RUNNING;
if ((svc->flags & SVC_EXEC) != 0) {
INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...\n",
svc->pid, svc->uid, svc->gid, svc->nr_supp_gids,
svc->seclabel ? : "default");
waiting_for_exec = true;
}
svc->NotifyStateChange("running");
}
回到main函数,末尾使用epoll_wait监听子进程退出事件,若成功返回,调用handle_signal函数。handle_signal会读取管道的内容,把管道读端的内容清空,然后调用reap_any_outstanding_children处理退出的进程,在前面已有介绍。
/system/core/init/init.cpp
...
epoll_event ev;
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
if (nr == -1) {
ERROR("epoll_wait failed: %s\n", strerror(errno));
} else if (nr == 1) {
((void (*)()) ev.data.ptr)();
}
}
return 0;
}
/system/core/init/init_parser.cpp
static void handle_signal() {
// Clear outstanding requests.
char buf[32];
read(signal_read_fd, buf, sizeof(buf));
reap_any_outstanding_children();
}