DVM 에 의해 Zygote::main() 이 실행된다. ZygoteInit의 main()(source code) 에서는 아래 순서로 동작한다.
- registerZygoteSocket() : socket (UDS) 에 binding
- preloadClasses() : 필요한 class 로딩 (preload() 에서 시작)
- preloadResouces() : 필요한 resource 로딩 (preload() 에서 시작)
- startSystemServer() : system server 를 시작
- runSelectLoopMode() : 새로운 application 의 요청을 기다리는 polling 시작
ZygoteInit의 main() public static void main(String argv[]) { try { // Start profiling the zygote initialization. SamplingProfilerIntegration.start(); registerZygoteSocket(); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START, SystemClock.uptimeMillis()); preload(); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END, SystemClock.uptimeMillis()); // Finish profiling the zygote initialization. SamplingProfilerIntegration.writeZygoteSnapshot(); // Do an initial gc to clean up after startup gc(); // If requested, start system server directly from Zygote if (argv.length != 2) { throw new RuntimeException(argv[0] + USAGE_STRING); } if (argv[1].equals("start-system-server")) { startSystemServer(); } else if (!argv[1].equals("")) { throw new RuntimeException(argv[0] + USAGE_STRING); } Log.i(TAG, "Accepting command socket connections"); if (ZYGOTE_FORK_MODE) { runForkMode(); } else { runSelectLoopMode(); } closeServerSocket(); } catch (MethodAndArgsCaller caller) { caller.run(); } catch (RuntimeException ex) { Log.e(TAG, "Zygote died with exception", ex); closeServerSocket(); throw ex; } }
registerZygoteSocket()
init process 에 의해 zygote socket(/dev/socket/zygote) 가 생성된다.새로운 어플(application)이 실행될 때 이 zygote 가 이 요청(request)를 받을 수 있도록, zygote 를 이 UDS(Unix Domain Socket, /dev/socket/zygote) 에 binding 한다.
이 LocalServerSocket 의 변수 sServerSocket 를 기억하자. 이 변수가 runSelectLoopMode() 에서 file descriptor 의 array 에 추가된다.
private static void registerZygoteSocket() { if (sServerSocket == null) { int fileDesc; try { String env = System.getenv(ANDROID_SOCKET_ENV); fileDesc = Integer.parseInt(env); } catch (RuntimeException ex) { throw new RuntimeException( ANDROID_SOCKET_ENV + " unset or invalid", ex); } try { sServerSocket = new LocalServerSocket( createFileDescriptor(fileDesc)); } catch (IOException ex) { throw new RuntimeException( "Error binding to local socket '" + fileDesc + "'", ex); } } }
preloadClasses()
frameworks/base/preladed-classes 파일에 적혀있는 class 들을 load 한다.preloadResouces()
문자열, 레이아웃, 색, 이미지, 사운드 리소스들(resources) 들을 load 한다. 리소스들의 예는 버튼, 기본테마이미지, 색상 등등이 있겠다.사용하는 주체에 따른 리소스 분류
- 시스템 리소스
- application 리소스
frameworks/base/core/res/res/values/arrays.xml 에 적혀있는
- preloaded_drawables
- preloaded_color_state_lists
를 메모리에 로딩
startSystemServer()
시스템 서버를 실행(Zygote.forkSystemServer(), native 함수임) 한다. 그리고 이 서버가 com.android.server.SystemServer::main() 을 실행하게 된다. SystemServer 의 main() 에서 init1() 을 실행하고 다시 그 안에서 com.android.server.SystermServer::init2() 를 호출한다.native public static void init1(String[] args); public static void main(String[] args) { if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) { // If a device's clock is before 1970 (before 0), a lot of // APIs crash dealing with negative numbers, notably // java.io.File#setLastModified, so instead we fake it and // hope that time from cell towers or NTP fixes it // shortly. Slog.w(TAG, "System clock is before 1970; setting to 1970."); SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME); } if (SamplingProfilerIntegration.isEnabled()) { SamplingProfilerIntegration.start(); timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { SamplingProfilerIntegration.writeSnapshot("system_server", null); } }, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL); } // Mmmmmm... more memory! dalvik.system.VMRuntime.getRuntime().clearGrowthLimit(); // The system server has to run all of the time, so it needs to be // as efficient as possible with its memory usage. VMRuntime.getRuntime().setTargetHeapUtilization(0.8f); System.loadLibrary("android_servers"); init1(args); }
extern "C" status_t system_init() { ... if (strcmp(propBuf, "1") == 0) { // Start the SurfaceFlinger SurfaceFlinger::instantiate(); } property_get("system_init.startsensorservice", propBuf, "1"); if (strcmp(propBuf, "1") == 0) { // Start the sensor service SensorService::instantiate(); } // And now start the Android runtime. We have to do this bit // of nastiness because the Android runtime initialization requires // some of the core system services to already be started. // All other servers should just start the Android runtime at // the beginning of their processes's main(), before calling // the init function. ... AndroidRuntime* runtime = AndroidRuntime::getRuntime(); ... JNIEnv* env = runtime->getJNIEnv(); ... jclass clazz = env->FindClass("com/android/server/SystemServer"); ... jmethodID methodId = env->GetStaticMethodID(clazz, "init2", "()V"); ... env->CallStaticVoidMethod(clazz, methodId); ... ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); ... return NO_ERROR; }
- AudioFlinger::instantiate();
- MediaPlayerService::instantiate();
- CameraService::instantiate();
- AudioPolicyService::instantiate();
init2() 에서
- ServerThread::run()
을 실행하게 되는데, 여기서 여러 중요한 service 들이 실행되게 된다. 자세한 service 들의 list 는 [ref. 3]을 참조하도록 하자.
SystemServer 는 fork 된 process 이기 때문에 SytemServer 의 main 은 따로 실행되고, main process 는 계속해서 다음 동작인 runSelectLoop() 을 실행하게 된다.
private static boolean startSystemServer() throws MethodAndArgsCaller, RuntimeException { /* Hardcoded command line to start the system server */ String args[] = { "--setuid=1000", "--setgid=1000", "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,3001,3002,3003,3006,3007", "--capabilities=130104352,130104352", "--runtime-init", "--nice-name=system_server", "com.android.server.SystemServer", }; ZygoteConnection.Arguments parsedArgs = null; int pid; try { parsedArgs = new ZygoteConnection.Arguments(args); ZygoteConnection.applyDebuggerSystemProperty(parsedArgs); ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs); /* Request to fork the system server process */ pid = Zygote.forkSystemServer( parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, parsedArgs.debugFlags, null, parsedArgs.permittedCapabilities, parsedArgs.effectiveCapabilities); } catch (IllegalArgumentException ex) { throw new RuntimeException(ex); } /* For child process */ if (pid == 0) { handleSystemServerProcess(parsedArgs); } return true; }
runSelectLoopMode() : polling 시작
private static void runSelectLoopMode() throws MethodAndArgsCaller { ArrayListfds = new ArrayList(); ArrayList peers = new ArrayList(); FileDescriptor[] fdArray = new FileDescriptor[4]; fds.add(sServerSocket.getFileDescriptor()); peers.add(null); int loopCount = GC_LOOP_COUNT; while (true) { int index; /* * Call gc() before we block in select(). * It's work that has to be done anyway, and it's better * to avoid making every child do it. It will also * madvise() any free memory as a side-effect. * * Don't call it every time, because walking the entire * heap is a lot of overhead to free a few hundred bytes. */ if (loopCount <= 0) { gc(); loopCount = GC_LOOP_COUNT; } else { loopCount--; } try { fdArray = fds.toArray(fdArray); index = selectReadable(fdArray); } catch (IOException ex) { throw new RuntimeException("Error in select()", ex); } if (index < 0) { throw new RuntimeException("Error in select()"); } else if (index == 0) { ZygoteConnection newPeer = acceptCommandPeer(); peers.add(newPeer); fds.add(newPeer.getFileDesciptor()); } else { boolean done; done = peers.get(index).runOnce(); if (done) { peers.remove(index); fds.remove(index); } } } }
application 의 실행요청은 sServerSocket 으로 오게 되는데, 이녀석은 가장 먼저 array fds에 add() 되었기 때문에 index 가 '0' 이다.
이 요청을 받으면 ZygoteConnection 을 생성하고, 이 녀석의 socket의 file descriptor 를 array fds 에 add() 하게 된다.
그리고 나서 runOnce() 에 들어가게 되면, 이제 여기서 process 를 하나 fork() 하고 app 의 main() 을 실행하게 된다.
References
- 인사이드 안드로이드
- https://sites.google.com/site/io/anatomy--physiology-of-an-android
- http://elinux.org/Android_Zygote_Startup
댓글 없음:
댓글 쓰기