本文尝试对着 《深入理解 Android 5.0 系统》来对 android 9.0 的启动代码进行分析,但是分析过程中发现自己缺乏操作系统方面的知识,以致于只能做一些简单分析。最近也买了一本操作系统的书 《操作系统:精髓与设计原理》(第9版) ,等后续基础提升后,会继续进行分析。

虽然 Init 进程是 Linux 内核启动后创建的第一个用户进程,地位非常重要。Init 进程在初始化过程中会启动很多重要的守护进程,因此,了解 Init 进程的启动过程将有助于我们更好地理解 Android系统。Init 除了完成系统的初始化之外,本身也是一个守护进程,担负着系统部分很重要的职责。本文将详细介绍 Init 进程的初始化以及它作为守护进程的功能。

先简单介绍 Android 的启动过程。从系统角度看,Android 的启动过程可分为 bootloader 引导、装载和启动Linux 内核、启动 Android 系统 3 个大的阶段。其中 Android系统的启动还可以细分为启动 Init 进程、启动 Zygote、启动 SystemService、启动 SystemServer、启动 Home 等多个阶段。下图揭示了整个 Android 的启动过程。

下面简单介绍设备的启动过程。

(1)Bootloader 引导。

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

如果用户在 Bootloader 运行期间,按下预定义的组合键,可以进入系统的更新模块。Android的下载更新可以选择进入 Fastboot 模式或者 Recover 模式。

  • Fastboot 是 Android设计的一套通过 USB 来更新手机分区映像的协议,方便开发人员能快速更新指定的手机分区。但是一般的零售机上往往去掉了 Fastboot,Google 销售的开发机则带有 Fastboot 模块。
  • Recovery 模式是 Android 特有的升级系统。利用 Recovery 模式,手机可以进行恢复出厂设置,或者执行 OTA、补丁和固件升级。进入 Recovery 模式实际上是启动了一个文本模式的 Linux。

(2)装载和启动 Linux 内核。

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

(3)启动Init 进程。

Linux 内核加载完毕后,会首先启动 Init 进程,Init 进程是系统的第一个进程。在 Init 进程的启动过程中,会解析 Linux 的配置脚本 init.rc 文件。根据 init.rc 文件的内容,Init 进程会装载 Android的文件系统、创建系统目录、初始化属性系统、启动 Android 系统重要的守护进程,这些进程包括 USB 守护进程、adb 守护进程、vold 守护进程、rild 守护进程等。

最后 Init 进程也会作为守护进程来执行修改属性请求,重启崩溃的进程等操作。

(4)启动 ServiceManager。

ServiceManager 由 Init 进程启动。它主要的作用是管理 Binder 服务,负责 Binder服务的注册与查找。

(5)启动 Zygote进程。

Init 进程初始化结束时,会启动 Zygote 进程。Zygote 进程负责 fork 出应用进程,是所有应用进程的父进程。Zygote 进程初始化时会创建 Dalivik 虚拟机、预装载系统的资源文件和 Java 类。所有从Zygote进程 fork 出的用户进程将继承和共享这些预加载的资源,不用浪费时间重新加载,加快了应用程序的启动过程。启动结束后,Zygote 进程也将变为守护进程,负责响应启动 APK应用程序的请求。

(6)启动 SystemServer。

SystemServer 是 Zygote 进程 fork 出的第一个进程,也是整个 Android 系统的核心进程。在 SystemServer 中运行着 Android 系统大部分的 Binder 服务。SystemServer 首先启动本地服务 Sensor Service;接着启动包括 ActivityManagerService、WindowsMangerService、PackageManagerService在内的所有 Java 服务。

(7)启动 MediaServer。

MediaServer 由 Init 进程启动。它包含了一些多媒体相关的本地 Binder 服务,包括∶ CameraService、AudioFlingerService、MediaPlayerService 和 AudioPolicyService。

(8)启动 Launcher。

SystemServer 加载完所有 Java 服务后,最后会调用 ActivityManagerService 的 SystemReady)方法。在这个方法的执行中,会发出 Intent"android.intent.category.HOME"。凡是响应这个 Intent的 apk 应用都会运行起来,Launcher 应用是 Android 系统默认的桌面应用,一般只有它会响应这个 Intent,因此,系统开机后,第一个运行的应用就是 Launcher。

Init 进程的初始化过程

Init 进程的源码位于目录 system/core/init下。程序的入口函数 main() 位于文件 init.cpp 中。 pie 9.0 main 函数的流程 main()函数比较长,整个 Init 进程的启动流程都在这个函数中。下面我们把 main()函数分成小段,一段段地介绍其功能和作用。 具体可以看下面的代码:

  1 // /system/core/init/init.cpp
2 int main(int argc, char** argv) {
3 if (!strcmp(basename(argv[0]), "ueventd")) {
4 return ueventd_main(argc, argv);
5 }
6
7 if (!strcmp(basename(argv[0]), "watchdogd")) {
8 return watchdogd_main(argc, argv);
9 }
10
11 if (argc > 1 && !strcmp(argv[1], "subcontext")) {
12 InitKernelLogging(argv);
13 const BuiltinFunctionMap function_map;
14 return SubcontextMain(argc, argv, &function_map);
15 }
16
17 if (REBOOT_BOOTLOADER_ON_PANIC) {
18 InstallRebootSignalHandlers();
19 }
20
21 bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
22
23 if (is_first_stage) {
24 boot_clock::time_point start_time = boot_clock::now();
25
26 // Clear the umask.
27 umask(0);
28
29 clearenv();
30 setenv("PATH", _PATH_DEFPATH, 1);
31 // Get the basic filesystem setup we need put together in the initramdisk
32 // on / and then we'll let the rc file figure out the rest.
33 mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
34 mkdir("/dev/pts", 0755);
35 mkdir("/dev/socket", 0755);
36 mount("devpts", "/dev/pts", "devpts", 0, NULL);
37 #define MAKE_STR(x) __STRING(x)
38 mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
39 // Don't expose the raw commandline to unprivileged processes.
40 chmod("/proc/cmdline", 0440);
41 gid_t groups[] = { AID_READPROC };
42 setgroups(arraysize(groups), groups);
43 mount("sysfs", "/sys", "sysfs", 0, NULL);
44 mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
45
46 mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
47
48 if constexpr (WORLD_WRITABLE_KMSG) {
49 mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
50 }
51
52 mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
53 mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
54
55 // Mount staging areas for devices managed by vold
56 // See storage config details at http://source.android.com/devices/storage/
57 mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
58 "mode=0755,uid=0,gid=1000");
59 // /mnt/vendor is used to mount vendor-specific partitions that can not be
60 // part of the vendor partition, e.g. because they are mounted read-write.
61 mkdir("/mnt/vendor", 0755);
62
63 // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
64 // talk to the outside world...
65 InitKernelLogging(argv);
66
67 LOG(INFO) << "init first stage started!";
68
69 if (!DoFirstStageMount()) {
70 LOG(FATAL) << "Failed to mount required partitions early ...";
71 }
72
73 SetInitAvbVersionInRecovery();
74
75 // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
76 global_seccomp();
77
78 // Set up SELinux, loading the SELinux policy.
79 SelinuxSetupKernelLogging();
80 SelinuxInitialize();
81
82 // We're in the kernel domain, so re-exec init to transition to the init domain now
83 // that the SELinux policy has been loaded.
84 if (selinux_android_restorecon("/init", 0) == -1) {
85 PLOG(FATAL) << "restorecon failed of /init failed";
86 }
87
88 setenv("INIT_SECOND_STAGE", "true", 1);
89
90 static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
91 uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
92 setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
93
94 char* path = argv[0];
95 char* args[] = { path, nullptr };
96 execv(path, args);
97
98 // execv() only returns if an error happened, in which case we
99 // panic and never fall through this conditional.
100 PLOG(FATAL) << "execv(\"" << path << "\") failed";
101 }
102
103 // At this point we're in the second stage of init.
104 InitKernelLogging(argv);
105 LOG(INFO) << "init second stage started!";
106
107 // Set up a session keyring that all processes will have access to. It
108 // will hold things like FBE encryption keys. No process should override
109 // its session keyring.
110 keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
111
112 // Indicate that booting is in progress to background fw loaders, etc.
113 close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
114
115 property_init();
116
117 // If arguments are passed both on the command line and in DT,
118 // properties set in DT always have priority over the command-line ones.
119 process_kernel_dt();
120 process_kernel_cmdline();
121
122 // Propagate the kernel variables to internal variables
123 // used by init as well as the current required properties.
124 export_kernel_boot_props();
125
126 // Make the time that init started available for bootstat to log.
127 property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
128 property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
129
130 // Set libavb version for Framework-only OTA match in Treble build.
131 const char* avb_version = getenv("INIT_AVB_VERSION");
132 if (avb_version) property_set("ro.boot.avb_version", avb_version);
133
134 // Clean up our environment.
135 unsetenv("INIT_SECOND_STAGE");
136 unsetenv("INIT_STARTED_AT");
137 unsetenv("INIT_SELINUX_TOOK");
138 unsetenv("INIT_AVB_VERSION");
139
140 // Now set up SELinux for second stage.
141 SelinuxSetupKernelLogging();
142 SelabelInitialize();
143 SelinuxRestoreContext();
144
145 epoll_fd = epoll_create1(EPOLL_CLOEXEC);
146 if (epoll_fd == -1) {
147 PLOG(FATAL) << "epoll_create1 failed";
148 }
149
150 sigchld_handler_init();
151
152 if (!IsRebootCapable()) {
153 // If init does not have the CAP_SYS_BOOT capability, it is running in a container.
154 // In that case, receiving SIGTERM will cause the system to shut down.
155 InstallSigtermHandler();
156 }
157
158 property_load_boot_defaults();
159 export_oem_lock_status();
160 start_property_service();
161 set_usb_controller();
162
163 const BuiltinFunctionMap function_map;
164 Action::set_function_map(&function_map);
165
166 subcontexts = InitializeSubcontexts();
167
168 ActionManager& am = ActionManager::GetInstance();
169 ServiceList& sm = ServiceList::GetInstance();
170
171 LoadBootScripts(am, sm);
172
173 // Turning this on and letting the INFO logging be discarded adds 0.2s to
174 // Nexus 9 boot time, so it's disabled by default.
175 if (false) DumpState();
176
177 am.QueueEventTrigger("early-init");
178
179 // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
180 am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
181 // ... so that we can start queuing up actions that require stuff from /dev.
182 am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
183 am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
184 am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
185 am.QueueBuiltinAction(keychord_init_action, "keychord_init");
186 am.QueueBuiltinAction(console_init_action, "console_init");
187
188 // Trigger all the boot actions to get us started.
189 am.QueueEventTrigger("init");
190
191 // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
192 // wasn't ready immediately after wait_for_coldboot_done
193 am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
194
195 // Don't mount filesystems or start core system services in charger mode.
196 std::string bootmode = GetProperty("ro.bootmode", "");
197 if (bootmode == "charger") {
198 am.QueueEventTrigger("charger");
199 } else {
200 am.QueueEventTrigger("late-init");
201 }
202
203 // Run all property triggers based on current state of the properties.
204 am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
205
206 while (true) {
207 // By default, sleep until something happens.
208 int epoll_timeout_ms = -1;
209
210 if (do_shutdown && !shutting_down) {
211 do_shutdown = false;
212 if (HandlePowerctlMessage(shutdown_command)) {
213 shutting_down = true;
214 }
215 }
216
217 if (!(waiting_for_prop || Service::is_exec_service_running())) {
218 am.ExecuteOneCommand();
219 }
220 if (!(waiting_for_prop || Service::is_exec_service_running())) {
221 if (!shutting_down) {
222 auto next_process_restart_time = RestartProcesses();
223
224 // If there's a process that needs restarting, wake up in time for that.
225 if (next_process_restart_time) {
226 epoll_timeout_ms = std::chrono::ceil<std::chrono::milliseconds>(
227 *next_process_restart_time - boot_clock::now())
228 .count();
229 if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
230 }
231 }
232
233 // If there's more work to do, wake up again immediately.
234 if (am.HasMoreCommands()) epoll_timeout_ms = 0;
235 }
236
237 epoll_event ev;
238 int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
239 if (nr == -1) {
240 PLOG(ERROR) << "epoll_wait failed";
241 } else if (nr == 1) {
242 ((void (*)()) ev.data.ptr)();
243 }
244 }
245
246 return 0;
247 }

(1)进入 main()函数后,首先检查启动程序的文件名。如果文件名是"ueventd",执行守护进程 ueventd 的主函数 ueventd main(O,如果文件名是"watchdogd",执行看门狗守护进程的主函数 watchdogd_main()。都不是则继续执行。

   if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
} if (!strcmp(basename(argv[0]), "watchdogd")) {
return watchdogd_main(argc, argv);
}
   // 参数个数大于1,就会获取多个
if (argc > 1 && !strcmp(argv[1], "subcontext")) {
InitKernelLogging(argv);
const BuiltinFunctionMap function_map;
return SubcontextMain(argc, argv, &function_map);
}

从这里可以看出 init 进程的代码里也包含了另外两个守护进程的代码,因为这几个守护进程的代码重合度高,所以,开发人员干脆把它们都放在一起了。但是在编译时,Android 生成了两个指向 init 文件的符号连接ueventd 和 watchdogd,这样启动时如果执行的是这两个符号连接,main函数就能判断出到底要启动哪个守护进程。

(2)创建一些基本的目录,包括/dev、/porc、/sys等;同时把一些文件系统,如 tmpfs、devpt、 proc、sysfs 等 mount 到相应的目录。

     mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
mkdir("/dev/pts", 0755);
mkdir("/dev/socket", 0755);
mount("devpts", "/dev/pts", "devpts", 0, NULL);
#define MAKE_STR(x) __STRING(x)
mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
// Don't expose the raw commandline to unprivileged processes.
chmod("/proc/cmdline", 0440);
gid_t groups[] = { AID_READPROC };
setgroups(arraysize(groups), groups);
mount("sysfs", "/sys", "sysfs", 0, NULL);
mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL); mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)); if constexpr (WORLD_WRITABLE_KMSG) {
mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
} mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));

tmpfs 是一种基于内存的文件系统,mount 后就可以使用。tmpfs 文件系统下的文件都存放在内存中,访问速度快,但是关机后所有内容都会丢失,因此 tmpfs 文件系统比较适合存放一些临时性的文件。tmpfs 文件系统的大小是动态变化的,刚开始占用空间很小,随着文件的增多会随之变大,很节省空间。Android 将 tmpfs 文件系统 mount 到/dev目录,/dev 目录用来存放系统创造的设备节点,正好符合 tmpfs 文件系统的特点。

  • devpts 是虚拟终端文件系统,它通常 mount 在目录/dev/pts 下。

  • proc 也是一种基于内存的虚拟文件系统,它可以看作是内核内部数据结构的接口,通过它可以获得系统的信息,同时能够在运行时修改特定的内核参数。

  • sysfs 文件系统和 proc 文件系统类似,它是 Linux 2.6 内核引入的,作用是把系统的设备和总线按层次组织起来,使得它们可以在用户空间存取,用来向用户空间导出内核的数据结构及它们的属性。

(3)在/dev 目录下创建一个空文件".booting"表示初始化正在进行。

close(open("/dev/.booting",o_WRONLY I o_CREAT,0000));

is_bootingO函数会依靠空文件".booting"来判断是否进程处于初始化中。初始化结束后这个文件将被删除。

(4)调用 property init()函数来初始化 Android 的属性系统。 property_init();

property_init()函数主要作用是创建一个共享区域来存储属性值。下节分析属性系统时会详细介绍。

(5)调用 property load boot defaults()函数。

is_charger = !strcmp (bootmode,"charger"); property_load_boot_defaults ();

property_load_boot_defaults()函数将解析设备根目录下的 default.prop 文件,把文件中定义的属性值读出来设置到属性系统中。

所谓充电模式是指插着充电器开机时设备会进入的状态。这时 kernel 和 init 进程会启动,但是大部分的服务都不会启动。

(6)main() 函数最后会进入一个无限 while 循环,每次循环开始都会调用 ExecuteOneCommand()函数来执行命令列表中的一条命令,同时调用 RestartProcesses()函数来启动服务进程∶

while (true) {
// By default, sleep until something happens.
int epoll_timeout_ms = -1; if (do_shutdown && !shutting_down) {
do_shutdown = false;
if (HandlePowerctlMessage(shutdown_command)) {
shutting_down = true;
}
} if (!(waiting_for_prop || Service::is_exec_service_running())) {
am.ExecuteOneCommand();
}
if (!(waiting_for_prop || Service::is_exec_service_running())) {
if (!shutting_down) {
auto next_process_restart_time = RestartProcesses(); // If there's a process that needs restarting, wake up in time for that.
if (next_process_restart_time) {
epoll_timeout_ms = std::chrono::ceil<std::chrono::milliseconds>(
*next_process_restart_time - boot_clock::now())
.count();
if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
}
} // If there's more work to do, wake up again immediately.
if (am.HasMoreCommands()) epoll_timeout_ms = 0;
} epoll_event ev;
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
if (nr == -1) {
PLOG(ERROR) << "epoll_wait failed";
} else if (nr == 1) {
((void (*)()) ev.data.ptr)();
}
} return 0;

(7)Init 进程初始化系统后,会化身为守护进程来处理子进程的死亡信号,修改属性的请求和组合键盘键事件。

启动 Service 进程

在 main()函数的 for 循环中,会调用 RestartProcesses()函数来启动服务列表中的服务进程。函数 RestartProcesses()的代码如下所示∶

static std::optional<boot_clock::time_point> RestartProcesses() {
std::optional<boot_clock::time_point> next_process_restart_time;
for (const auto& s : ServiceList::GetInstance()) {
if (!(s->flags() & SVC_RESTARTING)) continue; auto restart_time = s->time_started() + 5s;
if (boot_clock::now() > restart_time) {
if (auto result = s->Start(); !result) {
LOG(ERROR) << "Could not restart process '" << s->name() << "': " << result.error();
}
} else {
if (!next_process_restart_time || restart_time < *next_process_restart_time) {
next_process_restart_time = restart_time;
}
}
}
return next_process_restart_time;
}

会检查 ServiceList 单例列表中的每个服务,凡是带有 SVC RESTARTING 标志的,进行启动,在启动前会判断当前时间是否已经到达启动时间。如果时间达到了,就会调用对应 service 的方法来进行启动:

// /system/core/init/service.cpp
Result<Success> Service::Start() {
bool disabled = (flags_ & (SVC_DISABLED | SVC_RESET));
// 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.
flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START)); // 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. For ONESHOT service, if it's in
// stopping status, we just set SVC_RESTART flag so it will get restarted
// in Reap().
if (flags_ & SVC_RUNNING) {
if ((flags_ & SVC_ONESHOT) && disabled) {
flags_ |= SVC_RESTART;
}
// It is not an error to try to start a service that is already running.
return Success();
}
......

SVC_DISABLED、SVC_RESTARTING、SVC_RESET、SVC RESTART、SVC_DISABLED_START 这 5 个标志都是和启动进程相关,需要先清除掉。如果服务带有 SVC RUNNING 标志,说明服务进程已经运行,这里就不重复启动了。

fork 子进程

 pid_t pid = -1;
if (namespace_flags_) {
pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
} else {
pid = fork();
}

fork 调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:

  • (1)在父进程中,fork返回新创建子进程的进程ID;

  • (2)在子进程中,fork返回0;

  • (3)如果出现错误,fork返回一个负值。

在 fork 函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。

解析启动脚本 init.rc

Init 进程启动时最重要的工作是解析并执行启动文件 init.rc。本节将介绍 init.rc 文件的格式,以及Init 进程解析脚本文件的流程。

init.rc 文件格式介绍

init.rc 文件是以块(section)为单位组织的,一个"块"可以包含多行。"块"分成两大类;一类称为"行为(action)";另一类称为"服务(service)"。"行为"块以关键字"on"开始,表示一堆命令的集合,"服务"块以关键字"service"开始,表示启动某个进程的方式和参数。"块"以关键字"on"或"service"开始,直到下一个"on"或"service"结束,中间所有行都属于这个"块"(空行或注释行没有分割作用)。注释以'#'号开始(如下所示是一份格式样本)。

# Copyright (C) 2012 The Android Open Source Project
#
# IMPORTANT: Do not create world writable files or directories.
# This is a common source of Android security bugs.
# import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc on early-init
# Set init and its forked children's oom_adj.
write /proc/1/oom_score_adj -1000 # Disable sysrq from keyboard
write /proc/sys/kernel/sysrq 0 # Set the security context of /adb_keys if present.
restorecon /adb_keys # Set the security context of /postinstall if present.
restorecon /postinstall # Mount cgroup mount point for cpu accounting
mount cgroup none /acct nodev noexec nosuid cpuacct
mkdir /acct/uid # root memory control cgroup, used by lmkd
mkdir /dev/memcg 0700 root system
mount cgroup none /dev/memcg nodev noexec nosuid memory
# app mem cgroups, used by activity manager, lmkd and zygote
mkdir /dev/memcg/apps/ 0755 system system
# cgroup for system_server and surfaceflinger
mkdir /dev/memcg/system 0550 system system start ueventd on init
sysclktz 0 # Mix device-specific information into the entropy pool
copy /proc/cmdline /dev/urandom
copy /default.prop /dev/urandom symlink /system/bin /bin
symlink /system/etc /etc # Backward compatibility.
symlink /sys/kernel/debug /d

无论是"行为"块还是"服务"块,并不是按照文件中的编排顺序逐一执行的。它们只是一份放在这里的定义,至于执行与否以及何时执行要由 init 进程在运行时决定。

"行为(action)"的关键字"on"后面跟的字串称为"触发器(trigger)",例如,实例中的"boot"和"nonencrypted"。"触发器"后面是命令列表。命令列表中的每一行都是一条命令,命令的种类非常多。

第一个用户进程 - Android 的 Init 进程的更多相关文章

  1. Android系统init进程启动及init.rc全解析

    转:https://blog.csdn.net/zhonglunshun/article/details/78615980 服务启动机制system/core/init/init.c文件main函数中 ...

  2. 从linux看Android之一--init进程

    准备环境: 熟悉linux环境和shell脚本 用SSHDROID和XShell搭建android的命令行环境(帮助找到熟悉的linux界面,因为android删除了很多标准linux平台上很多的sh ...

  3. Android Init进程命令的执行和服务的启动

    这里开始分析init进程中配置文件的解析,在配置文件中的命令的执行和服务的启动. 首先init是一个可执行文件,它的对应的Makfile是init/Android.mk. Android.mk定义了i ...

  4. Android系统开机启动流程及init进程浅析

    Android系统启动概述 Android系统开机流程基于Linux系统,总体可分为三个阶段: Boot Loader引导程序启动Linux内核启动Android系统启动,Launcher/app启动 ...

  5. Android系统启动流程(一)解析init进程启动过程

    整体流程大致如下:     1.init简介 init进程是Android系统中用户空间的第一个进程,作为第一个进程,它被赋予了很多极其重要的工作职责,比如创建zygote(孵化器)和属性服务等.in ...

  6. Android4.4的init进程

    1背景 前些日子需要在科室内做关于Android系统启动流程的培训.为此,我在几年前的技术手记的基础上,重新改了一份培训文档.在重新整理文档期间,我也重读了一下Android 4.4的相关代码,发现还 ...

  7. Android实现双进程守护 (转)

    做过android开发的人应该都知道应用会在系统资源匮乏的情况下被系统杀死!当后台的应用被系统回收之后,如何重新恢复它呢?网上对此问题有很多的讨论.这里先总结一下网上流传的各种解决方案,看看这些办法是 ...

  8. 动静结合学内核:linux idle进程和init进程浅析

    刘柳 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 + titer1@qq.com 退休的贵族进程 ...

  9. Linux下1号进程的前世(kernel_init)今生(init进程)----Linux进程的管理与调度(六)

    前面我们了解到了0号进程是系统所有进程的先祖, 它的进程描述符init_task是内核静态创建的, 而它在进行初始化的时候, 通过kernel_thread的方式创建了两个内核线程,分别是kernel ...

随机推荐

  1. 安装Boost库

    获取方式 官网下载合适版本:https://www.boost.org/ 此处用的是boost_1_75_0版本 开发环境 推荐使用GCC 7.x.x或以上编译器 安装Boost库 此处采用简易安装, ...

  2. Python-Redis-常用操作&管道

    常用操作 1.1 delete(*names) ? 1 2 3 4 5 6 7 8 9 # 根据删除redis中的任意数据类型   print(r.get('name')) r.delete('nam ...

  3. ALD和CVD晶体管薄膜技术

    ALD和CVD晶体管薄膜技术 现代微处理器内的晶体管非常微小,晶体管中的一些关键薄膜层甚至只有几个原子的厚度,光是英文句点的大小就够容纳一百万个晶体管还绰绰有余.ALD 是使这些极细微结构越来越普遍的 ...

  4. 深度学习LiDAR定位:L3-Net

    深度学习LiDAR定位:L3-Net 摘要 本文提出L3-Net--一种新颖的基于学习的LiDAR定位系统,可实现厘米级的定位,与现有最高水平的传统定位算法相媲美.与传统定位算法不同,本文创新地实现了 ...

  5. 快手推荐系统及 Redis 升级存储

    快手推荐系统及 Redis 升级存储  借傲腾 补上 DRAM 短板 内容简介: 作为短视频领域的领先企业,快手需要不断导入更先进的技术手段来调整和优化其系统架构,以应对用户量和短视频作品数量的爆炸式 ...

  6. 保存数据到csv文件报错:Permission denied: './train_data.csv'

    如果你此前已经输出,创建了文件,很有可能是你打开了此文件,导致写入不进去报错,关掉文件重新运行程序即可!

  7. [翻译]Go与C#的比较,第二篇:垃圾回收

    Go vs C#, part 2: Garbage Collection | by Alex Yakunin | ServiceTitan - Titan Tech | Medium 目录 译者注 什 ...

  8. 解决git冲突

    多个开发者同时操作git中的同一个文件,第一个人在commit和push的时候是可以正常提交的,而之后的开发者执行pull,就会报冲突异常conflict. 解决方案: 全部采用当前更改 之后再去gi ...

  9. 还在担心写的一手烂SQL,送你4款工具

    对于正在运行的mysql,性能如何,参数设置的是否合理,账号设置的是否存在安全隐患,你是否了然于胸呢? 俗话说工欲善其事,必先利其器,定期对你的MYSQL数据库进行一个体检,是保证数据库安全运行的重要 ...

  10. winform/WPF 多语言的实现

    WPF实现起来非常现代化,可以参考 https://www.cnblogs.com/yang-fei/p/4854460.html winform主要说一下实现过程和注意点,实现参考AutoUpdat ...