안드로이드 /bin/init부터 zygote process 실행 까지

Galmaegi
7 min readApr 23, 2021

--

2021년 4월기준 최신 코드들을 기준으로 분석했다.

kernel source branch: android-4.14-p

aosp source branch : 11.0.0_r3

android version : android 11.0, AVD

각 관련 코드들은 링크해뒀으니 필요하면 클릭해서 보면 된다.

안드로이드는 리눅스 커널 기반 프레임워크이기 때문에 bootloader을 통해 메모리에 기본적인 init과정이 끝난 후 아래의 코드가 불린다.

int __ref kernel_init(void *unused) : (kernel/common/init/main.c)

...
if (!try_to_run_init_process("/sbin/init") || !try_to_run_init_process("/etc/init") || !try_to_run_init_process("/bin/init") || !try_to_run_init_process("/bin/sh")) return 0;
...

해당 코드를 통해 /bin/init이 불린다.

안드로이드에는 init binary가 있는데, 그 위치는 아래와 같다.

generic_x86_arm:/ # which init
/system/bin/init
generic_x86_arm:/ # ls -al bin
lrw-r--r-- 1 root root 11 2020-10-14 07:35 bin -> /system/bin
generic_x86_arm:/bin # ls -al init
-rwxr-xr-x 1 root shell 1288484 2020-10-14 07:21 init

이제 kernel space에서 init이 호출된 이후에는 user space로 넘어오게 된다.

init binary는 다음의 Android.mk 혹은 Android.bp를 통해 빌드된다

init binary의 main함수는 다음과 같다.

int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
__asan_set_error_report_callback(AsanReportCallback);
#endif
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
if (argc > 1) {
if (!strcmp(argv[1], "subcontext")) {
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
return SubcontextMain(argc, argv, &function_map);
}
if (!strcmp(argv[1], "selinux_setup")) {
return SetupSelinux(argv);
}
if (!strcmp(argv[1], "second_stage")) {
return SecondStageMain(argc, argv);
}
}
return FirstStageMain(argc, argv);
}

진행순서는 아래와 같다

  1. FristStageMain()
  2. SetupSelinux()
  3. SecondStageMain()

먼저 FirstStageMain은 시스템에 기본이 되는 FileSystem들(tmpfs, devpts, proc, sysfs)등을 mount하는 작업을 한다.

작업이 마무리되면 다음과 같은 코드를 통해 SetupSelinux를 호출한다.

...
const char* path = "/system/bin/init";
const char* args[] = {path, "selinux_setup", nullptr};
execv(path, const_cast<char**>(args));
...

SetupSelinux에서는 LoadPolicy를 통해 엑세스 권한을 제어하는 policy파일들을 읽고 세팅을 진행한다.(master branch(12.0.0 예비브랜치)에서는 ReadPolicy를 통해 policy파일들을 로드한다, 이걸로 예상하건데 12.0.0 release note에서는 SeLinux관련 패치가 진행되었다는 공지가 있을 것으로 보인다.)

작업이 마무리되면 FirstStageMain에서와 동일하게 다음과 같은 코드를 통해 SecondStageMain을 호출한다.

...
const char* path = "/system/bin/init";
const char* args[] = {path, "second_stage", nullptr};
execv(path, const_cast<char**>(args));
...

SecondStageMain

SecondStageMain에서도 여러가지 초기화 과정들을 수행한다.

여기서 우리가 집중해야하는 부분은

...
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
LoadBootScripts(am, sm);
...

init.rc파일을 load하고 각 action들을 수행하는 부분이다. (LoadBootScripts)

Parser 클래스 상속도

LoadBootScripts에서 CreateParser를 통해 Parser들을 생성하고 각 *.rc파일들을 읽어들여 SectionParser클래스로 인스턴스화를 진행한다. SecitonParser클래스는 총 5개의 자식클래스들을 가지고 있으나 여기서는 세가지만 사용한다.

그 중 우리가 집중해야하는 부분은 import zygote.rc를 load하고 수행하는 부분이다.

...
import /system/etc/init/hw/init.${ro.zygote}.rc
...

운영체제에서 지원하는 비트에 따라 다르지만 32비트의 init.zygote32.rc는 아래와 같다.

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
critical window=${zygote.critical_window.minute:-off} target=zygote-fatal

해당 스크립트를 로드해 app_process를 통해 zygote이 실행되도록 한다.

--

--

Galmaegi
Galmaegi

Written by Galmaegi

0 Followers

Android developer

No responses yet