안드로이드 앱 프로세스 생성까지
2025년 4월 24일
앱을 실행하는 방법 #
- 런처에서 아이콘 클릭
- am start
- app_process (직접 실행)
- dalvikvm?
앱실행 준비과정부터 실행까지 #
대략적으로 아래 그림과 같이 앱이 실행된다.
검정색 선은 시스템 실행부터 Zygote와 SystemServer가 켜지기까지를 나타낸 것이고, 빨간색 선이 앱을 터치했을때 런처에서부터 실행되는 순서이다.
Zygote #
Zygote 프로세스는 부팅 초기에 init.rc를 통해 루트권한으로 실행되는 프로세스이며, 앱 실행에 필요한 안드로이드 런타임이나 필수 라이브러리들을 미리 로드한 프로세스이다.
미리 로드한 이후엔 ZygoteServer 역할을 하여 SystemServer나 소켓으로 전달받은 파라미터 속성의 App 프로세스를 fork 해주는 역할을 한다.
app_process로 Zygote 실행 #
안드로이드 커널 부팅 과정에서 init.zygote64.rc 가 실행되며 app_process 가 실행된다.
1// init.zygote64.rc 일부
2// https://android.googlesource.com/platform/system/core/+/refs/heads/android11-release/rootdir/init.zygote64.rc
3service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
4 class main
5 priority -20
6 user root
7 group root readproc reserved_disk
8 socket zygote stream 660 root system // zygote 소켓은 여기에서 열림
9 socket usap_pool_primary stream 660 root system
10...
11
12// /system/bin/app_process64 --zygote --start-system-server
13// app_main.cpp (app_process 코드)
14int main(int argc, char* const argv[]) {
15 AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
16 // ...
17 ++i; // Skip unused "parent dir" argument.
18 while (i < argc) {
19 const char* arg = argv[i++];
20 if (strcmp(arg, "--zygote") == 0) {
21 zygote = true;
22 niceName = ZYGOTE_NICE_NAME;
23 } else if (strcmp(arg, "--start-system-server") == 0) {
24 startSystemServer = true;
25 } else if (strcmp(arg, "--application") == 0) {
26 application = true;
27 } else if (strncmp(arg, "--nice-name=", 12) == 0) {
28 niceName.setTo(arg + 12);
29 } else if (strncmp(arg, "--", 2) != 0) {
30 className.setTo(arg);
31 break;
32 } else {
33 --i;
34 break;
35 }
36 }
37 // ...
38 if (zygote) {
39 // ZygoteInit 실행
40 runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
41 } else if (!className.empty()) {
42 // 일반 앱 실행. zygote를 타지 않고 실행되는 경우
43 runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
44 } else {
45 fprintf(stderr, "Error: no class name or --zygote supplied.\n");
46 app_usage();
47 LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
48 }
49}
ZygoteInit의 main 함수가 실행된다.
Zygote는 ZygoteServer 소켓을 리슨상태로 만든 후 SystemServer를 fork하고 /dev/socket/zygote 소켓에서 SystemServer의 앱 실행 요청을 기다린다.
1// ZygoteInit.java
2public static void main(String[] argv) {
3 // ...
4 Os.setpgid(0, 0);
5 if (!enableLazyPreload) {
6 preload(...); // 클래스, 리소스 미리 로딩
7 }
8 // socket, signal, fork 제어 등을 위한 네이티브 환경을 설정함
9 Zygote.initNativeState(isPrimaryZygote);
10
11 // /dev/socket/zygote 소켓을 리슨하고 SystemServer의 앱 생성 요청 대기 (UNIX 스트림 소켓)
12 // 먼저 준비시켜놔야 시스템서버의 요청에대한 병목을 줄일 수 있다.
13 // SystemServer는 fork되자마자 zygoteServer.closeServerSocket() 을 호출한다.
14 zygoteServer = new ZygoteServer(isPrimaryZygote);
15
16 // SystemServer는 여기에서 실행된다.
17 if (startSystemServer) {
18 Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
19 if (r != null) {
20 // SystemServer.main() 함수 실행
21 r.run();
22 return;
23 }
24 }
25
26 // ZygoteServer 가 리슨하고있는 소켓으로 데이터가 들어오면 명령을 처리하는 루프.
27 caller = zygoteServer.runSelectLoop(abiList);
28 // ...
29}
Zygote Listen 과정 #
ZygoteServer 생성자에서 /dev/socket/zygote 와 /dev/socket/usap_pool_primary 소켓을 로컬서버소켓으로 생성해서 listen 상태로 만들어 요청을 대기한다.
시스템 프로세스이거나 풀 고갈 등 USAP를 사용할 수 없는 상황에서는 일반 zygote 소켓으로 요청하고, 사용가능하다면 USAP 소켓으로 요청해서 앱프로세스를 좀 더 빨리 초기화한다.
1// ZygoteServer.java
2ZygoteServer(boolean isPrimaryZygote) {
3 mUsapPoolEventFD = Zygote.getUsapPoolEventFD();
4 if (isPrimaryZygote) {
5 // 일반적인 앱 실행 용. 앱이나 SystemServer가 앱 실행요청을 보낼때 사용하는 소켓
6 mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME); // zygote
7 // 미리 포크된 프로세스 풀(USAP) 방식으로 프로세스 생성시
8 mUsapPoolSocket =
9 Zygote.createManagedSocketFromInitSocket(
10 Zygote.USAP_POOL_PRIMARY_SOCKET_NAME); // usap_pool_primary
11 } else {
12 // ...
13}
14
15// Zygote.java
16// 소켓의 파일디스크립터를 전달해서 로컬서버소켓으로 생성하는 함수
17static LocalServerSocket createManagedSocketFromInitSocket(String socketName) {
18 int fileDesc;
19 final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
20
21 String env = System.getenv(fullSocketName);
22 fileDesc = Integer.parseInt(env);
23
24 FileDescriptor fd = new FileDescriptor();
25 fd.setInt$(fileDesc);
26 return new LocalServerSocket(fd);
27}
28
29// LocalServerSocket.java
30public LocalServerSocket(FileDescriptor fd) throws IOException
31{
32 impl = new LocalSocketImpl(fd);
33 impl.listen(LISTEN_BACKLOG); // 이 위치에서 리슨 시작
34 localAddress = impl.getSockAddress();
35}
ZygoteServer 의 소켓 감시 #
무한루프 안에서 멀티플렉싱 poll 구조로 소켓을 감시하다가 요청이 들어오면 커넥션 후 데이터를 받아 처리하는 식으로 구현되어 있다.
앱에서 startActivity, 쉘에서 am start, 런처에서 터치하는 등 여러 방식으로 앱을 실행시킬 수 있지만, 모두 SystemServer를 거쳐서 zygote 프로세스에게 앱 실행요청을 보내는 방식이기 때문에 zygote 서버 소켓에 연결해서 커넥션을 맺는 클라이언트 소켓은 SystemServer 뿐 이다.
1// ZygoteServer.java
2Runnable runSelectLoop(String abiList) {
3 ArrayList<FileDescriptor> socketFDs = new ArrayList<>();
4 ArrayList<ZygoteConnection> peers = new ArrayList<>();
5 // 메인 zygote 서버 소켓 등록
6 socketFDs.add(mZygoteSocket.getFileDescriptor());
7
8 while (true) {
9 if (mUsapPoolEnabled) {
10 usapPipeFDs = Zygote.getUsapPipeFDs();
11 pollFDs = new StructPollfd[socketFDs.size() + 1 + usapPipeFDs.length];
12 } else {
13 pollFDs = new StructPollfd[socketFDs.size()];
14 }
15
16 // 감시할 소켓 초기화 (events 를 POLLIN으로 세팅)
17 int pollIndex = 0;
18 for (FileDescriptor socketFD : socketFDs) {
19 pollFDs[pollIndex] = new StructPollfd();
20 pollFDs[pollIndex].fd = socketFD;
21 pollFDs[pollIndex].events = (short) POLLIN;
22 ++pollIndex;
23 }
24
25 // poll 함수 실행
26 int pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
27 if (pollReturnValue == 0) {
28 // 받은 데이터가 없는 경우
29 } else {
30 // 하나의 소켓이라도 받을 데이터가 있는 경우
31 while (--pollIndex >= 0) {
32 // revents가 POLLIN이 아니라면 다음 소켓확인 (데이터 온 소켓 찾기)
33 if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
34 continue;
35 }
36 if (pollIndex == 0) {
37 // zygote 서버소켓인 경우
38 // 클라이언트 소켓 생성 (연결)
39 ZygoteConnection newPeer = acceptCommandPeer(abiList);
40 peers.add(newPeer);
41 // 커넥션이 완료된 클라이언트 소켓도 리슨 대상에 추가
42 socketFDs.add(newPeer.getFileDescriptor());
43 } else if (pollIndex < usapPoolEventFDIndex) {
44 // zygote 클라이언트 소켓인 경우
45 // processOneCommand 함수로 SystemServer에서 받은 데이터 파싱 후 프로세스 생성
46 ZygoteConnection connection = peers.get(pollIndex);
47 final Runnable command = connection.processOneCommand(this);
48 if (mIsForkChild) {
49 if (command == null) throw new IllegalStateException("command == null");
50 return command;
51 } else {
52 if (command != null) throw new IllegalStateException("command != null");
53 // 연결종료 요청이 왔다면 소켓 제거.
54 // SystemServer와의 소켓은 시스템 종료할 때 까지 유지된다.
55 // 입출력 시도 중 I/O Exception이 발생한 경우만 재연결하느라 닫는다.
56 if (connection.isClosedByPeer()) {
57 connection.closeSocket();
58 peers.remove(pollIndex);
59 socketFDs.remove(pollIndex);
60 ...
61 } else {
62 // 만약 USAP 소켓인 경우
63 long messagePayload;
64 ...
65 int readBytes = Os.read(pollFDs[pollIndex].fd, buffer, 0, buffer.length);
66 if (readBytes == Zygote.USAP_MANAGEMENT_MESSAGE_BYTES) {
67 DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(buffer));
68 messagePayload = inputStream.readLong();
69 }
70 ...
71 if (pollIndex > usapPoolEventFDIndex) {
72 Zygote.removeUsapTableEntry((int) messagePayload);
73 }
74 usapPoolFDRead = true;
75 }
76 // 이후에는 상황 봐서 usapPool을 리필하는 알고리즘이 있음.
77 ...
78}
SystemServer의 요청 파싱 및 프로세스 생성 #
zygote 서버 소켓에서 대기하다가 연결 요청이 오면 연결한 뒤 인자들을 문자열 형태로 받는다.
인자를 개행(\n) 단위로 파싱하고 첫 줄은 인자의 수, 나머지 줄은 그 수만큼의 인자를 파싱하여 저장한다. 그리고 인자에 맞게 앱 프로세스를 생성하는 함수를 호출한다.
1// ZygoteConnection.java
2Runnable processOneCommand(ZygoteServer zygoteServer) {
3 String[] args;
4 args = Zygote.readArgumentList(mSocketReader); // 여기에서 argc\narg[0]\narg[1] ... 형태로 들어온 데이터 파싱
5 ZygoteArguments parsedArgs = new ZygoteArguments(args);
6
7 // 파싱한 인자 사용하기 (상당수 생략)
8 if (parsedArgs.mBootCompleted) {
9 handleBootCompleted();
10 return null;
11 }
12 if (parsedArgs.mApiBlacklistExemptions != null) {
13 return handleApiBlacklistExemptions(zygoteServer, parsedArgs.mApiBlacklistExemptions);
14 }
15 Zygote.applyUidSecurityPolicy(parsedArgs, peer);
16 Zygote.applyInvokeWithSecurityPolicy(parsedArgs, peer);
17
18 // 프로세스 생성
19 pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
20 parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
21 parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
22 parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
23 parsedArgs.mPkgDataInfoList, parsedArgs.mWhitelistedDataInfoList,
24 parsedArgs.mBindMountAppDataDirs, parsedArgs.mBindMountAppStorageDirs);
25 // pid 에 따라 처리
26 if (pid == 0) {
27 // 자식 생성됐으면 zygoteServer에도 알리기.
28 // 이 함수가 리턴되면 zygoteServer 코드에서 계속 진행된다.
29 zygoteServer.setForkChild();
30 zygoteServer.closeServerSocket();
31 return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote);
32 } else {
33 handleParentProc(pid, serverPipeFd);
34 return null;
35 }
36}
37
38// Zygote.java
39// zygote 소켓으로 전달받은 명령 데이터 파싱
40static String[] readArgumentList(BufferedReader socketReader) throws IOException {
41 String argc_string = socketReader.readLine();
42 argc = Integer.parseInt(argc_string);
43
44 String[] args = new String[argc];
45 for (int arg_index = 0; arg_index < argc; arg_index++) {
46 args[arg_index] = socketReader.readLine();
47 if (args[arg_index] == null) {
48 // We got an unexpected EOF.
49 throw new IOException("Truncated request");
50 }
51 }
52 return args;
53}
SystemServer #
Zygote로부터 fork된 프로세스이며, system 권한으로 실행되어 여러 시스템 서비스(AMS, PMS, …)를 관리하는 하나의 프로세스이다.
SystemServer 프로세스 생성 #
Zygote에서 ZygoteServer 소켓 리슨 이후 forkSystemServer 함수를 호출해줬다.
1// ZygoteInit.java
2private static Runnable forkSystemServer(String abiList, String socketName,
3 ZygoteServer zygoteServer) {
4 // root권한을 주는 대신 SystemServer 에 필요한 커널권한만 넣음 (rootless)
5 long capabilities = posixCapabilitiesAsBits(
6 OsConstants.CAP_IPC_LOCK,
7 OsConstants.CAP_KILL,
8 OsConstants.CAP_NET_ADMIN,
9 OsConstants.CAP_NET_BIND_SERVICE,
10 OsConstants.CAP_NET_BROADCAST,
11 OsConstants.CAP_NET_RAW,
12 OsConstants.CAP_SYS_MODULE,
13 OsConstants.CAP_SYS_NICE,
14 OsConstants.CAP_SYS_PTRACE,
15 OsConstants.CAP_SYS_TIME,
16 OsConstants.CAP_SYS_TTY_CONFIG,
17 OsConstants.CAP_WAKE_ALARM,
18 OsConstants.CAP_BLOCK_SUSPEND
19 );
20 /* Containers run without some capabilities, so drop any caps that are not available. */
21 StructCapUserHeader header = new StructCapUserHeader(
22 OsConstants._LINUX_CAPABILITY_VERSION_3, 0);
23 StructCapUserData[] data;
24 try {
25 data = Os.capget(header);
26 } catch (ErrnoException ex) {
27 throw new RuntimeException("Failed to capget()", ex);
28 }
29 capabilities &= ((long) data[0].effective) | (((long) data[1].effective) << 32);
30
31 // SystemServer 의 인자들을 만들어서
32 String args[] = {
33 "--setuid=1000",
34 "--setgid=1000",
35 "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
36 + "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010,3011",
37 "--capabilities=" + capabilities + "," + capabilities,
38 "--nice-name=system_server",
39 "--runtime-args",
40 "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
41 "com.android.server.SystemServer",
42 };
43 ZygoteArguments parsedArgs = null;
44
45 int pid;
46 try {
47 parsedArgs = new ZygoteArguments(args);
48 // ...
49 // Zygote 자신이니까 소켓통신 없이 직접 fork 한다.
50 pid = Zygote.forkSystemServer(
51 parsedArgs.mUid, parsedArgs.mGid,
52 parsedArgs.mGids,
53 parsedArgs.mRuntimeFlags,
54 null,
55 parsedArgs.mPermittedCapabilities,
56 parsedArgs.mEffectiveCapabilities);
57 } catch (IllegalArgumentException ex) {
58 throw new RuntimeException(ex);
59 }
60
61 if (pid == 0) {
62 if (hasSecondZygote(abiList)) {
63 waitForSecondaryZygote(socketName);
64 }
65 // 이미 열려있던 zygote server 소켓 닫기
66 zygoteServer.closeServerSocket();
67 // SystemServer 로직 초기화 후 main을 실행할 수 있는 Runnable 리턴
68 return handleSystemServerProcess(parsedArgs);
69 }
70 return null;
71}
JNI native 코드로 넘겨서 fork 한다.
1static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
2 int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
3 ZygoteHooks.preFork();
4 int pid = nativeForkSystemServer(
5 uid, gid, gids, runtimeFlags, rlimits,
6 permittedCapabilities, effectiveCapabilities);
7
8 Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
9
10 ZygoteHooks.postForkCommon();
11 return pid;
12}
네이티브 코드. ZygoteServer 소켓에 쏘는형태는 아니고 그냥 바로 fork 한다.
1static jint com_android_internal_os_Zygote_nativeForkSystemServer(
2 JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
3 jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities,
4 jlong effective_capabilities) {
5
6 // ...
7 pid_t pid = ForkCommon(env, true,
8 fds_to_close,
9 fds_to_ignore,
10 true);
11 if (pid == 0) {
12 SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
13 permitted_capabilities, effective_capabilities,
14 MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
15 false, nullptr, nullptr, /* is_top_app= */ false,
16 /* pkg_data_info_list */ nullptr,
17 /* whitelisted_data_info_list */ nullptr, false, false);
18 } else if (pid > 0) {
19 ALOGI("System server process %d has been created", pid);
20 gSystemServerPid = pid; // 생성된 SystemServerPid 백업
21
22 int status;
23 // 자식프로세스가 종료됐는지 체크. 종료된 자식이 없으면 즉시 0이 반환됨
24 if (waitpid(pid, &status, WNOHANG) == pid) {
25 ALOGE("System server process %d has died. Restarting Zygote!", pid);
26 RuntimeAbort(env, __LINE__, "System server process has died. Restarting Zygote!");
27 }
28
29 // 앱별 메모리 cgroup을 사용하는 경우 SystemServer를 별도의 메모리 정책을 적용
30 if (UsePerAppMemcg()) {
31 if (!SetTaskProfiles(pid, std::vector<std::string>{"SystemMemoryProcess"})) {
32 ALOGE("couldn't add process %d into system memcg group", pid);
33 }
34 }
35 }
36 return pid;
37}
SystemServer 로직 #
SystemServer.main을 실행시킬 수 있는 Runnable 리턴하는 함수
1private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
2 // set umask to 0077 so new files and directories will default to owner-only permissions.
3 Os.umask(S_IRWXG | S_IRWXO);
4 // ps, top에 표시될 프로세스 이름 변경
5 if (parsedArgs.mNiceName != null) {
6 Process.setArgV0(parsedArgs.mNiceName);
7 }
8
9 // init.environ.rc: export SYSTEMSERVERCLASSPATH /system/framework/services.jar...
10 // SystemServer가 사용할 프레임워크/서비스
11 final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
12 if (systemServerClasspath != null) {
13 // AOT 컴파일 (DexOpt)
14 performSystemServerDexOpt(systemServerClasspath);
15 // ...
16 }
17
18 if (parsedArgs.mInvokeWith != null) {
19 // 래퍼프로그램 삽입하는 방식으로 AOS 빌드 시 --invoke-with 설정됨
20 } else {
21 // 클래스로더에 SystemServer의 클래스들을 추가하고 현재 스레드의 클래스로더로 지정
22 ClassLoader cl = null;
23 if (systemServerClasspath != null) {
24 cl = createPathClassLoader(systemServerClasspath, parsedArgs.mTargetSdkVersion);
25 Thread.currentThread().setContextClassLoader(cl);
26 }
27 // Java 레벨 런타임을 초기화 후 main 메서드를 호출할 수 있는 Runnable을 리턴
28 return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
29 parsedArgs.mDisabledCompatChanges,
30 parsedArgs.mRemainingArgs, cl);
31 }
32 /* should never reach here */
33}
메인함수를 찾아서 Runnable 형태로 리턴
1public static final Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
2 String[] argv, ClassLoader classLoader) {
3 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
4 RuntimeInit.redirectLogStreams();
5
6 RuntimeInit.commonInit();
7 ZygoteInit.nativeZygoteInit();
8 return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
9 classLoader);
10}
11
12protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
13 String[] argv, ClassLoader classLoader) {
14 VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
15 VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);
16
17 final Arguments args = new Arguments(argv);
18 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
19 // 메인함수 찾아서 Runnable로 리턴
20 return findStaticMain(args.startClass, args.startArgs, classLoader);
21}
메인함수 실행 #
1// ZygoteInit.main 에서 SystemServer fork 하고 main 함수 실행
2Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
3if (r != null) {
4 r.run();
5 return;
6}
메인함수에서 여러 시스템 서비스들을 실행시킨다.
1// SystemServer.java
2public static void main(String[] args) {
3 new SystemServer().run();
4}
5
6private void run() {
7 TimingsTraceAndSlog t = new TimingsTraceAndSlog();
8 try {
9 t.traceBegin("InitBeforeStartServices");
10 // ... timer, locale 등 초기화 ...
11
12 // 현재 스레드를 NORMAL에서 FOREGROUND 우선순위로 올려준다.
13 // SystemServer는 지연 없이 응답해줘야 하기 때문에 우선순위를 올림
14 android.os.Process.setThreadPriority(
15 android.os.Process.THREAD_PRIORITY_FOREGROUND);
16 // 시스템서버가 bg cgroup 으로 옮겨지지 않게
17 android.os.Process.setCanSelfBackground(false);
18 // 메시지큐를 생성해서 이벤트를 받을 수 있도록 준비
19 Looper.prepareMainLooper();
20 // 메시지의 dispatch, delivery 가 늦으면 로그로 남김
21 Looper.getMainLooper().setSlowLogThresholdMs(
22 SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
23
24 // Initialize native services.
25 System.loadLibrary("android_servers");
26
27 // ...
28
29 // 비정상 종료 등으로 처리되지 못해서 Pending 작업으로 남아있는 애들을 처리
30 performPendingShutdown();
31
32 // 시스템 컨텍스트 생성. 앱에서 호출하는 getSystemContext() 와는 다름
33 createSystemContext();
34
35 // Call per-process mainline module initialization.
36 ActivityThread.initializeMainlineModules();
37
38 // SystemServiceManager 생성
39 mSystemServiceManager = new SystemServiceManager(mSystemContext);
40 mSystemServiceManager.setStartInfo(mRuntimeRestart,
41 mRuntimeStartElapsedTime, mRuntimeStartUptime);
42 LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
43 // 병렬로 시작 가능한 서비스 일부를 비동기로 시작
44 SystemServerInitThreadPool.start();
45 } finally {
46 t.traceEnd(); // InitBeforeStartServices
47 }
48
49 // Setup the default WTF handler
50 RuntimeInit.setDefaultApplicationWtfHandler(SystemServer::handleEarlySystemWtf);
51
52 // Start services.
53 try {
54 t.traceBegin("StartServices");
55 startBootstrapServices(t); // 시스템에 반드시 필요한 서비스(AMS, Power, Display, PMS, User 등) 실행
56 startCoreServices(t); // 부팅에는 꼭 필요하진 않은것들 (배터리모니터링, 앱사용통계 등)
57 startOtherServices(t); // option 서비스 실행 (윈도우UI관리, 터치/키보드관리, 네트워크 등)
58 } catch (Throwable ex) {
59 Slog.e("System", "******************************************");
60 Slog.e("System", "************ Failure starting system services", ex);
61 throw ex;
62 } finally {
63 t.traceEnd(); // StartServices
64 }
65
66 // Loop forever. 이벤트 메시지를 받고 처리하는 루프. 절대 종료되지 않음
67 Looper.loop();
68 throw new RuntimeException("Main thread loop unexpectedly exited");
69}
시스템서비스 등록 및 부팅완료 #
SystemServer는 하나의 프로세스 안에 아주 많은 시스템 서비스를 동작시킨다. 예전에는 앱=화면=태스크 를 동일하게 봤기 때문에 AMS에서 많은 작업들을 담당했는데, 멀티윈도우나 pip 등의 기능이 생겨나며 AMS가 너무 커지게됐다.
그래서 Android 10.0 부터 논리적스택/태스크/포커스/전환 등 UI 관련 정책을 ActivityTaskManagerService로 분리시키게 됐다.
startBootstrapServices #
startBootstrapServices 함수에서 ActivityManagerService가 시작된다.
ActivityTaskManagerService도 함께 실행되며 두 서비스가 서로의 함수를 호출해서 앱 상태에 따른 작업을 해야하기 때문에 AMS를 시작할때 전달해준다.
- 앱실행요청 =
AMSfork, 생명주기 관리 →ATMS태스크, 스택에 액티비티, 윈도우 추가 - 프로세스 죽음 감지 =
AMS프로세스 kill, ANR 관리 →ATMS태스크, 액티비티 정리 - 화면전환/포커스변경 =
ATMS사용자의 입력을 인식해서 UI변경 →AMS필요시 액티비티 생명주기(onPause, onResume 등) 콜백 전달
1// SystemServer 코드
2private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
3 // ...
4 t.traceBegin("StartActivityManager");
5 // TODO: Might need to move after migration to WM.
6 ActivityTaskManagerService atm = mSystemServiceManager.startService(
7 ActivityTaskManagerService.Lifecycle.class).getService();
8 mActivityManagerService = ActivityManagerService.Lifecycle.startService(
9 mSystemServiceManager, atm);
10 mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
11 mActivityManagerService.setInstaller(installer);
12 mWindowManagerGlobalLock = atm.getGlobalLock();
13 t.traceEnd();
14 // ...
15}
시스템서비스 등록 #
안드로이드의 시스템서비스들은 SystemServiceManager의 생명주기 관리를 받기 위해 SystemService 라는 클래스로 필요한 콜백을 추상화해야한다.
핵심 서비스들은 오래전에 구현됐고 너무 크기가 크기 때문에 직접 SystemService를 상속받지 못하고 Lifecycle이라는 내부 클래스를 둬서 생명주기 관리를 위임하게된다.
start() 함수에서는 시스템서비스끼리의 통신을 위한 LocalServices에 등록한다. 나중에 system_server의 다른 시스템서비스(WMS, AMS 등)에서 LocalServices.getService(ActivityTaskManagerInternal.class) 와 같은 형식으로 서비스를 가져올 수 있다.
1public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
2 final ActivityTaskManagerInternal mInternal;
3
4 public ActivityTaskManagerService(Context context) {
5 mInternal = new LocalService();
6 // ...
7 }
8
9 private void start() {
10 // 서비스 등록
11 LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
12 }
13
14 public static final class Lifecycle extends SystemService {
15 private final ActivityTaskManagerService mService;
16 // ...
17 @Override
18 public void onStart() {
19 publishBinderService(Context.ACTIVITY_TASK_SERVICE, mService);
20 mService.start();
21 }
22 }
23
24 final class LocalService extends ActivityTaskManagerInternal {
25 }
26}
ActivityManagerService #
다른 앱들이 쿼리할 수 있도록 배터리 사용량이나 앱 권한 사용로그 등 을 Binder로 등록하고 ATMS처럼 LocalServices에 AMS 서비스를등록한다.
ATMS와 달리 Lifecycle에 onBootPhase가 보인다. 아직 init.rc 부터 시작돼서 계속 부팅중인 상태인데 시스템서비스 매니저가 부팅 페이즈마다 호출해서 실행해야하는 작업들이다.
1public class ActivityManagerService extends IActivityManager.Stub
2 implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
3 private void start() {
4 removeAllProcessGroups();
5 mProcessCpuThread.start(); // cpu 통계 수집하는 스레드 실행
6
7 // 바인더 등록
8 mBatteryStatsService.publish();
9 mAppOpsService.publish();
10 // 로컬서비스에 등록
11 LocalServices.addService(ActivityManagerInternal.class, mInternal);
12 mActivityTaskManager.onActivityManagerInternalAdded();
13 mPendingIntentController.onActivityManagerInternalAdded();
14
15 // cpu 통계 인프라가 켜질때까지 대기. (아직 시스템 부팅중이다)
16 mProcessCpuInitLatch.await();
17 }
18
19 public static final class Lifecycle extends SystemService {
20 private final ActivityManagerService mService;
21 private static ActivityTaskManagerService sAtm;
22 // ...
23 @Override
24 public void onStart() {
25 mService.start();
26 }
27
28 // 시스템서비스매니저의 페이즈에 따라 세팅된다.
29 @Override
30 public void onBootPhase(int phase) {
31 mService.mBootPhase = phase;
32 if (phase == PHASE_SYSTEM_SERVICES_READY) {
33 // 시스템 서비스들 준비된 타이밍
34 // 배터리서비스 등에 알려줌
35 mService.mBatteryStatsService.systemServicesReady();
36 mService.mServices.systemServicesReady();
37 } else if (phase == PHASE_ACTIVITY_MANAGER_READY) {
38 // 액티비티 매니저 준비된 타이밍. 이 페이즈부터 브로드캐스트 받을 수 있다. (startOtherServices 에서 세팅됨)
39 // 서드파티는 아직 실행되지 않기 때문에 BOOT_COMPLETED 브로드캐스트 같은것들이 큐에 쌓임
40 // 브로드캐스트는 AMS가 관리하는 것이고, 아래함수를 통해 ContentObserver 등록한다.
41 // Settings.Global등 설정 DB가 변경됐을때를 감시해서 브로드캐스트 세팅값을 변경하기 위함
42 mService.startBroadcastObservers();
43 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
44 // 서드파티 앱들 시작 가능한 타이밍
45 // 앱 헬스 감시를 켜는 시점
46 mService.mPackageWatchdog.onPackagesReady();
47 }
48 }
49 }
50}
startOtherServices #
갑자기 SystemServer의 startOtherServices 함수로 돌아왔는데 여기에서 AMS.systemReady 함수를 호출하고 첫 부팅 후 유저에게 보이는 시스템 초기화 작업이 진행된다.
systemReady는 콜백을 먼저 실행시키고 런처 액티비티를 실행시킨다.
1// ACTIVITY_MANAGER_READY 상태로 변경함
2mActivityManagerService.systemReady(() -> {
3 Slog.i(TAG, "Making services ready");
4 t.traceBegin("StartActivityManagerReadyPhase");
5 // 이때 각 SystemService에서 PHASE_ACTIVITY_MANAGER_READY 에 해당하는 작업 처리
6 mSystemServiceManager.startBootPhase(t, SystemService.PHASE_ACTIVITY_MANAGER_READY);
7
8 // 시스템 Ui 시작 (부팅 후 상태바, 잠금화면 등)
9 startSystemUi(context, windowManagerF);
10 if (safeMode) {
11 connectivityF.setAirplaneMode(true);
12 }
13 // 서드파티 앱 fork 허용
14 mSystemServiceManager.startBootPhase(t, SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
15 NetworkStackClient.getInstance().start();
16 // ...
17});
18
19public void systemReady(final Runnable goingCallback, @NonNull TimingsTraceAndSlog t) {
20 t.traceBegin("PhaseActivityManagerReady");
21 mSystemServiceManager.preSystemReady();
22 synchronized(this) {
23 goingCallback.run();
24 }
25 mActivityTaskManager.onSystemReady();
26 mAppOpsService.systemReady();
27 // ...
28 // SystemUser 라는건 그냥 user 0 번을 말하는것
29 final boolean bootingSystemUser = currentUserId == UserHandle.USER_SYSTEM;
30
31 // 전화서비스, IME 등 잠금해제 이전 실행되는 DirectBoot 앱 실행
32 startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_AWARE);
33 // 키오스크와 같은 앱들은 표시용 사용자가 따로있고 홈 화면이 두번 실행되는걸 방지하기 위해 0일때만 실행한다.
34 if (bootingSystemUser) {
35 // 홈 액티비티(런처)를 Zygote에게 fork 요청
36 // LocalServices를 통해 ATMS 메서드 호출하고 Itent를 직접 생성하여 실행
37 // mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason, taskDisplayArea);
38 mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady");
39 // 큐에 쌓여 있던 BOOT_COMPLETED 브로드캐스트가 각 앱으로 전달되고 BOOT_COMPLETED 로직을 구현해뒀으면 코드가 실행됨
40 broadcastIntentLocked(null, null, null, intent,
41 null, null, 0, null, null, null, OP_NONE,
42 null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
43 currentUserId);
44 }
45 // 홈 액티비티 onResume을 호출하여 사용자가 볼 수 있도록 함
46 mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
47 // 현재 UserId 가 foreground 유저라고 ACTION_USER_SWITCHING 브로드캐스트 전송
48 mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
49}
여기까지 하면 부팅 후 앱 실행에 필요한 프로세스는 다 준비됐다.
앱 실행 요청 #
앱 실행 요청은 ATMS에서 직접 로컬서비스를 통해 AMS의 함수를 호출한다.
1public ActivityManagerService(Context systemContext, ActivityTaskManagerService atm) {
2 mProcessList = mInjector.getProcessList(this);
3 // ATMS 를 접근할 수 있도록 멤버로 세팅
4 mActivityTaskManager = atm;
5 mActivityTaskManager.initialize(mIntentFirewall, mPendingIntentController,
6 DisplayThread.get().getLooper());
7 mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
8}
9
10// ActivityTaskManagerService.java
11void startProcessAsync(ActivityRecord activity, boolean knownToBeDead, boolean isTop,
12 String hostingType) {
13 final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::startProcess,
14 mAmInternal, activity.processName, activity.info.applicationInfo, knownToBeDead,
15 isTop, hostingType, activity.intent.getComponent());
16 mH.sendMessage(m);
17}
18
19// ActivityManagerService.java
20@Override
21public ProcessRecord startProcess(/* ... */) {
22 return mProcessList.startProcessLocked(processName, info, /* ... */);
23}
24
25// ProcessList.java
26boolean startProcessLocked(/* ... */) {
27 if (mService.mConstants.FLAG_PROCESS_START_ASYNC) {
28 mService.mProcStartHandler.post(() -> handleProcessStart(
29 app, entryPoint, gids, runtimeFlags, zygotePolicyFlags, mountExternal,
30 requiredAbi, instructionSet, invokeWith, startSeq));
31 return true;
32 } else {
33 final Process.ProcessStartResult startResult = startProcess(hostingRecord,
34 entryPoint, app, ... );
35 return app.pid > 0;
36 }
37}
38
39private void handleProcessStart(/* ... */) {
40 final Process.ProcessStartResult startResult = startProcess(app.hostingRecord,
41 entryPoint, app, app.startUid, gids, ...);
42}
43
44private Process.ProcessStartResult startProcess(/* ... */) {
45 if (hostingRecord.usesWebviewZygote()) {
46 startResult = startWebView(entryPoint, ... );
47 } else if (hostingRecord.usesAppZygote()) {
48 startResult = appZygote.getProcess().start(entryPoint, ... );
49 } else {
50 startResult = Process.start(entryPoint, ... );
51 }
52}
결국엔 Process 코드에서 프로세스 실행 요청이 모인다.
1// Process.java 코드
2// 이쪽에서 ZYGOTE_PROCESS에게 직접 start를 요청한다.
3public static final ZygoteProcess ZYGOTE_PROCESS = new ZygoteProcess();
4public static ProcessStartResult start(@NonNull final String processClass,
5 @Nullable final String niceName,
6 int uid, int gid, @Nullable int[] gids,
7 /* ... */
8 @Nullable String[] zygoteArgs) {
9 return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
10 ...,
11 bindMountAppStorageDirs, zygoteArgs);
12}
13
14// ZygoteProcess.java 코드
15public final Process.ProcessStartResult start(@NonNull final String processClass,
16 final String niceName,
17 int uid, int gid, @Nullable int[] gids,
18 /* ... */
19 @Nullable String[] zygoteArgs) {
20 // TODO (chriswailes): Is there a better place to check this value?
21 if (fetchUsapPoolEnabledPropWithMinInterval()) {
22 informZygotesOfUsapPoolStatus();
23 }
24
25 return startViaZygote(processClass, niceName, uid, gid, gids,
26 /* ... */
27 bindMountAppStorageDirs, zygoteArgs);
28}
startViaZygote #
전달받은 파라미터를 리스트 형태로 파싱해서 저장한다. zygote 소켓에 요청 파라미터 스트림을 쏘기위한 작업.
1private Process.ProcessStartResult startViaZygote(@NonNull final String processClass,
2 @Nullable final String niceName,
3 final int uid, final int gid,
4 @Nullable final int[] gids,
5 /* ... */
6 @Nullable String[] extraArgs)
7 throws ZygoteStartFailedEx {
8 ArrayList<String> argsForZygote = new ArrayList<>();
9 // 전달된 인자를 argsForZygote 에 cli 파라미터 형태로 저장
10 argsForZygote.add("--runtime-args");
11 argsForZygote.add("--setuid=" + uid);
12 argsForZygote.add("--setgid=" + gid);
13 argsForZygote.add("--runtime-flags=" + runtimeFlags);
14
15 if (niceName != null) {
16 argsForZygote.add("--nice-name=" + niceName);
17 }
18 if (invokeWith != null) {
19 argsForZygote.add("--invoke-with");
20 argsForZygote.add(invokeWith);
21 }
22 if (startChildZygote) {
23 argsForZygote.add("--start-child-zygote");
24 }
25 if (packageName != null) {
26 argsForZygote.add("--package-name=" + packageName);
27 }
28 // ...
29 // openZygoteSocketIfNeeded 함수를 호출하면서 zygote_server 소켓에 커넥션 요청을 한다.
30 return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
31 zygotePolicyFlags,
32 argsForZygote);
33}
zygote 소켓에 전송 #
ArrayList
1private Process.ProcessStartResult zygoteSendArgsAndGetResult(
2 ZygoteState zygoteState, int zygotePolicyFlags, @NonNull ArrayList<String> args)
3 throws ZygoteStartFailedEx {
4 // 개행 기준으로 인자를 묶음
5 String msgStr = args.size() + "\n" + String.join("\n", args) + "\n";
6 return attemptZygoteSendArgsAndGetResult(zygoteState, msgStr);
7}
8
9private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult(
10 ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {
11 final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;
12 final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;
13
14 // zygote에게 프로세스 실행 요청
15 zygoteWriter.write(msgStr);
16 zygoteWriter.flush();
17
18 // zygote_server의 응답 받아옴. 정상적으로 fork됐다면 result.pid 값이 세팅됨
19 Process.ProcessStartResult result = new Process.ProcessStartResult();
20 result.pid = zygoteInputStream.readInt();
21 result.usingWrapper = zygoteInputStream.readBoolean();
22
23 if (result.pid < 0) {
24 throw new ZygoteStartFailedEx("fork() failed");
25 }
26 return result;
27}
앱 실행과정 정리 #
- 시스템 부팅 시 init.rc 에 적힌대로 app_process 바이너리로 Zygote 프로세스를 실행한다.
- Zygote 프로세스에서는 ZygoteServer 소켓
/dev/socket/zygote을 열고 SystemServer를 fork하여 실행한다. - SystemServer는 여러 시스템 서비스(AMS, ATMS 등)를 초기화한다. (새로운 프로세스 실행이 아님)
- SystemServer.systemReady() 가 호출되면 SystemUi가 실행되고 부트페이즈가 변경되면서 시스템이 잠금화면까지 부팅된다.
- 홈런처도 ATMS의 LocalServices를 통해 ATMS에서 직접 실행된다. (Zygote에서 실행되는게 아님)
홈 런처 #
터치이벤트의 시작은 platform/packages/apps/Launcher3 레포에 있다.
Launcher → ActivityTaskManagerService → ActivityManagerService → ZYGOTE_PROCESS.start → Zygote에 fork 요청
- 부팅되면 홈런처가 띄워지고 터치이벤트를 읽을 수 있게된다.
- 터치이벤트가 발생하면 좌표를 확인해서
ItemClickHandler.onClick이 호출되면 tag에 따라 앱인 경우startAppShortcutOrInfoActivity를 호출한다. Launcher::startActivitySafely→ActivityContext::startActivitySafely→ContextImpl::startActivityActivityTaskManager.getService().startActivity→ActivityStartController::obtainStarter→ActivityStarter.DefaultFactory에서 ActivityStarter 하나 꺼내 인자 세팅 후 execute() 호출resumeFocusedStacksTopActivities→startSpecificActivityATMS.startProcessAsync→ActivityManagerInternal::startProcess계속 하다보면 위에서 정리한ZYGOTE_PROCESS실행 루트가 나온다.
am start #
AM → AMS::onShellCommand → ATMS::startActivityAsUser → AMS::startProcess → ZYGOTE_PROCESS.start → Zygote에 fork 요청
- shell에서 /system/bin/am 바이너리가 실행되고 ActivityManager 바인더를 통해 onShellCommand 실행(
mAm.asBinder().shellCommand()) ActivityManagerShellCommand::onCommand→runStartActivityAMS.startActivityAsUserWithFeature→ATMS.startActivityAsUser- 런처에서 봤던 것처럼
ActivityStartController::obtainStarter로직을 따라간다.
app_process #
이건 바로 jar앱을 실행하는 로직이라서 AMS, ATMS를 신경쓰지 않는다.
- app_main.cpp 에서 main 함수가 실행된다.
- 옵션이 아닌 첫 토큰이 className으로 지정되고, 이제 지정됐다면 일반 앱 실행 루틴을 탄다.
usage) app_process[–옵션…] [–] [main() 인자…]
ex) app_process /data/local/tmpcom.example.Mainhello runtime.start("com.android.internal.os.RuntimeInit", args, zygote=false);AndroidRuntime::start에서 여러 JVM 환경이 세팅되고 RuntimeInit 클래스의 main함수가 실행된다.- 어플리케이션을 실행시킬 수 있는 환경을 준비하고,
findStaticMain에서 target 클래스의 main함수를 찾아서 실행한다.