[컴][안드로이드] ZygoteInit main() 의 동작


DVM 에 의해 Zygote::main() 이 실행된다. ZygoteInit의 main()(source code) 에서는 아래 순서로 동작한다.
  1. registerZygoteSocket() : socket (UDS) 에 binding
  2. preloadClasses() : 필요한 class 로딩 (preload() 에서 시작)
  3. preloadResouces() : 필요한 resource 로딩 (preload() 에서 시작)
  4. startSystemServer() : system server 를 시작
  5. 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;
}
system_init() 에 있던 아래 부분은 2011년 정도에 사라졌다.(source diff)
  • 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 {
    ArrayList fds = 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);
            }
        }
    }
}
selectRedable() 통해 event 가 들어온 녀석의 index 를 얻게 된다.

application 의 실행요청은 sServerSocket 으로 오게 되는데, 이녀석은 가장 먼저 array fds에 add() 되었기 때문에 index 가 '0' 이다.

이 요청을 받으면 ZygoteConnection 을 생성하고, 이 녀석의 socket의 file descriptor 를 array fds 에 add() 하게 된다.

그리고 나서 runOnce() 에 들어가게 되면, 이제 여기서 process 를 하나 fork() 하고 app 의 main() 을 실행하게 된다.


References

  1. 인사이드 안드로이드
  2. https://sites.google.com/site/io/anatomy--physiology-of-an-android
  3. http://elinux.org/Android_Zygote_Startup

댓글 없음:

댓글 쓰기