第一个用户进程 - Android 的 Init 进程
本文尝试对着 《深入理解 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 进程的更多相关文章
- Android系统init进程启动及init.rc全解析
转:https://blog.csdn.net/zhonglunshun/article/details/78615980 服务启动机制system/core/init/init.c文件main函数中 ...
- 从linux看Android之一--init进程
准备环境: 熟悉linux环境和shell脚本 用SSHDROID和XShell搭建android的命令行环境(帮助找到熟悉的linux界面,因为android删除了很多标准linux平台上很多的sh ...
- Android Init进程命令的执行和服务的启动
这里开始分析init进程中配置文件的解析,在配置文件中的命令的执行和服务的启动. 首先init是一个可执行文件,它的对应的Makfile是init/Android.mk. Android.mk定义了i ...
- Android系统开机启动流程及init进程浅析
Android系统启动概述 Android系统开机流程基于Linux系统,总体可分为三个阶段: Boot Loader引导程序启动Linux内核启动Android系统启动,Launcher/app启动 ...
- Android系统启动流程(一)解析init进程启动过程
整体流程大致如下: 1.init简介 init进程是Android系统中用户空间的第一个进程,作为第一个进程,它被赋予了很多极其重要的工作职责,比如创建zygote(孵化器)和属性服务等.in ...
- Android4.4的init进程
1背景 前些日子需要在科室内做关于Android系统启动流程的培训.为此,我在几年前的技术手记的基础上,重新改了一份培训文档.在重新整理文档期间,我也重读了一下Android 4.4的相关代码,发现还 ...
- Android实现双进程守护 (转)
做过android开发的人应该都知道应用会在系统资源匮乏的情况下被系统杀死!当后台的应用被系统回收之后,如何重新恢复它呢?网上对此问题有很多的讨论.这里先总结一下网上流传的各种解决方案,看看这些办法是 ...
- 动静结合学内核:linux idle进程和init进程浅析
刘柳 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 + titer1@qq.com 退休的贵族进程 ...
- Linux下1号进程的前世(kernel_init)今生(init进程)----Linux进程的管理与调度(六)
前面我们了解到了0号进程是系统所有进程的先祖, 它的进程描述符init_task是内核静态创建的, 而它在进行初始化的时候, 通过kernel_thread的方式创建了两个内核线程,分别是kernel ...
随机推荐
- java面试一日一题:再谈垃圾回收器中的串行、并行、并发
问题:请讲下java中垃圾回收器的串行.并行.并发 分析:该问题主要考察在垃圾回收过程中垃圾回收线程和用户线程的关系 回答要点: 主要从以下几点去考虑, 1.串行.并行.并发的概念 2.如何考虑串行. ...
- 使用Jekyll + GitHub Pages免费搭建个人博客
使用Jekyll + GitHub Pages免费搭建个人博客 My Blog:无名の辈 | VectorX (vectorxxxx.github.io) Download Ruby:Download ...
- GO学习-(31) Go语言操作Elasticsearch
Elasticsearch 本文简单介绍了ES.Kibana和Go语言操作ES. Elasticsearch 介绍 Elasticsearch(ES)是一个基于Lucene构建的开源.分布式.REST ...
- Selenium 库的基本用法
Selenium库的基本使用 1.基本使用 from selenium import webdriver from selenium.webdriver.common.by import By f ...
- 'utf-8' codec can't decode byte 0xd5 in position XXX: invalid continuation byte问题
找了一下午,各种资料搜集,愣是没搜出来答案. 结果今天早上,做一个小小的改变,就整出来了... 步骤如下: 1.打开excel,全选数据 2.新建记事本,粘贴,选择脚本,更改字体: 3.新建Excel ...
- XLearning - 深度学习调度平台
XLearning - 深度学习调度平台 软件简介 XLearning **** 是奇虎 360 开源的一款支持多种机器学习.深度学习框架调度系统.基于 Hadoop Yarn 完成了对TensorF ...
- 深度学习与TensorFlow
深度学习与TensorFlow DNN(深度神经网络算法)现在是AI社区的流行词.最近,DNN 在许多数据科学竞赛/Kaggle 竞赛中获得了多次冠军. 自从 1962 年 Rosenblat 提出感 ...
- NX二次开发-从一个坐标系到另一个坐标系的转换
函数:UF_MTX4_csys_to_csys().UF_MTX4_vec3_multiply() 函数说明:从一个坐标系统到另一个坐标系统的转换.如下图红色坐标系下有个红色的点,将红色的点转到绿色的 ...
- Java必学MySQL数据库应用场景
Java教程分享Java必学之MySQL数据库应用场景,在当前的后台开发中,MySQL应用非常普遍,企业在选拔Java人才时也会考察求职者诸如性能优化.高可用性.备份.集群.负载均衡.读写分离等问题. ...
- 【模板】Noi-Linux 下的一些配置
Noi-Linux 下的一些配置(C++) vim 编程 来自远古的编程神器 针对网上其他博客的配置做了简化 配置 set t_Co=256 //开启256色模式 默认是16色 让你的vim更好看 s ...