이전 포스팅에서는 ZygoteInit 클래스의 메인 함수가 호출되는 부분까지 설명하였다. 본 포스팅에서는 ZygoteInit Main 클래스에서 동작하는 플로우를 기술하도록 하겠다.
ZygoteInit Java 클래스는 frameworks/base/core/java/com/android/internal/os 에 ZygoteINit.java 소스 코드가 있다. 다음은 main 메소드 소스 코드이다.
public static void main(String argv[]) { try { // Start profiling the zygote initialization. SamplingProfilerIntegration.start(); registerZygoteSocket(); //1) EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START, SystemClock.uptimeMillis()); preload(); //2) 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(); // Disable tracing so that forked processes do not inherit stale tracing tags from // Zygote. Trace.setTracingEnabled(false); // 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(); //3) } else if (!argv[1].equals("")) { throw new RuntimeException(argv[0] + USAGE_STRING); } Log.i(TAG, "Accepting command socket connections"); runSelectLoop();//4) closeServerSocket(); } catch (MethodAndArgsCaller caller) { caller.run(); } catch (RuntimeException ex) { Log.e(TAG, "Zygote died with exception", ex); closeServerSocket(); throw ex; } } |
1) 새로운 안드로이드 어플리케이션을 실행하기 위한 요청 수신을 위한 소켓 등록, ActivityManager로부터 새로운 안드로이드 어플리케이션을 실행하기 위한 요청을 수신하기 위하여 UDS(Unix Domain Socket)을 사용하며 init 프로세스가 init.rc에서 app_process를 실행할 때 등록한 소켓을 이용한다. (socket zygote stream 660 root system) 다음은 메소드 코드이다.
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); } }
|
2) 애플리케이션 프레임워크에 포함된 클래스와 플랫폼 자원(이미지, XML 정보, 문자열등)을 메모리에 로딩한다. 이때 로딩된 클래스와 자원은 새로 생성되는 프로세스에서 다시 로딩하는 것이 아니라 그대로 사용되어 안드로이드 어플리케이션이 빠르게 생성될 수 있다. 다음은 preload 메소드이다.
static void preload() { preloadClasses(); //클래스 로딩 2-1) preloadResources(); //리소스 로딩 2-2) preloadOpenGL(); //OpenGL 관련 로딩 } |
2-1) 에서는 사전에 로딩될 클래스들 목록을 framework/base/preloaded-classes를 읽어서 로딩한다. (여기에 ServiceManager도 포함된다)
2-2) 에서는 사전에 로딩될 리소스(문자열, 색, 이미지파일, 사운드 파일)를 로딩한다. 리소스는 어플리케이션에 직접 참조되지 않으며 R 클래스를 통해 접근한다.
private static void preloadResources() { final VMRuntime runtime = VMRuntime.getRuntime(); Debug.startAllocCounting(); try { System.gc(); runtime.runFinalizationSync(); mResources = Resources.getSystem(); mResources.startPreloading(); [---------- 중략 ----------] } |
3) 시스템 서버를 실행한다. 새로운 프로세스가 생성되며, 이 시스템 서버는 안드로이드 플랫폼에 필요한 주요한 네이티브 서비스들을 실행하게 된다. 시스템서버는 자바 서비스로 JNI로 Audio Flinger, Surface Flinger와 같은 네티이브 서비스를 실행하고, 이후 안드로이드 프레임워크의 서비스들을 시작한다. 안드로이드 프레임워크 서비스는 ActivityManager, Package Manager등이 있다.
private static boolean startSystemServer() throws MethodAndArgsCaller, RuntimeException { long capabilities = posixCapabilitiesAsBits( OsConstants.CAP_KILL, OsConstants.CAP_NET_ADMIN, OsConstants.CAP_NET_BIND_SERVICE, OsConstants.CAP_NET_BROADCAST, OsConstants.CAP_NET_RAW, OsConstants.CAP_SYS_MODULE, OsConstants.CAP_SYS_NICE, OsConstants.CAP_SYS_RESOURCE, OsConstants.CAP_SYS_TIME, OsConstants.CAP_SYS_TTY_CONFIG ); /* 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,1032,3001,3002,3003,3006,3007", "--capabilities=" + capabilities + "," + capabilities, "--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; } |
SystemServer.java는 frameworks/base/services/java/com/android/server 폴더에 존재한다.
public static void main(String[] args) { SystemProperties.set("persist.sys.dalvik.vm.lib", VMRuntime.getRuntime().vmLibrary()); if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) { 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); Environment.setUserRequired(true); System.loadLibrary("android_servers"); Slog.i(TAG, "Entered the Android system server!"); // Initialize native services. nativeInit(); //private static native void nativeInit(); // This used to be its own separate thread, but now it is // just the loop we run on the main thread. ServerThread thr = new ServerThread(); thr.initAndLoop(); } |
System.loadLibrary 를 통해서 androd_server Native 라이브러리를 로딩하고, nativeInit()을 호출한다. nativeInit() 함수는 framework/base/services/jni/com_android_server_SystemServer.cpp 에 존재한다. 여기에서는 SensorService::instantiate() 하여 센서 서비스를 시작한다.
(2.2 Froyo 는 네이티브 서비스의 경우 framework/base/cmds/system_server/library/system_init.cpp에서 SurfaceFlinger 등을 instantiate 하였지만 4.0 부터는 init.rc 에 해당 service들이 등록되어 네이티브 서비스를 실행해 준다.)
그리고 ServerThread를 실행시키는데, 여기에서 안드로이드 프레임워크의 주요 서비스들이 실행된다. 이 서비스들은 ServiceManager.addService 로 ServiceManager에 서비스를 등록시킨다. 다음은 ServerThread의 코드 예이다.
Slog.i(TAG, "System Content Providers"); ActivityManagerService.installSystemProviders(); Slog.i(TAG, "Lights Service"); lights = new LightsService(context); Slog.i(TAG, "Battery Service"); battery = new BatteryService(context, lights); ServiceManager.addService("battery", battery); Slog.i(TAG, "Vibrator Service"); vibrator = new VibratorService(context); ServiceManager.addService("vibrator", vibrator); Slog.i(TAG, "Consumer IR Service"); consumerIr = new ConsumerIrService(context); ServiceManager.addService(Context.CONSUMER_IR_SERVICE, consumerIr); |
4) UDS를 모니터링 하면서 새로운 안드로이드 어플리케이션 생성 요청을 받으면 이를 처리하는 루프 생성한다.
다음의 두 그림을 참고하면 도움이 될듯 하다.
아래 그림에서 Android 4.0 이상에서는 System Server에서 SurfaceFliger 등의 native service를 실행하지 않는다. (init.rc 처리할때 실행)
SystemServer가 실행되고 나면 Zygote는 사용자 요청에 따라 새로운 어플리케이션을 실행할 수 있다.
ZygoteInit.java의 Main에서 UDS 소켓을 모니터링 하면서 runSelectLoopMode가 동작한다.
private static void runSelectLoop() throws MethodAndArgsCaller { ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>(); ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>(); FileDescriptor[] fdArray = new FileDescriptor[4]; fds.add(sServerSocket.getFileDescriptor());//zygote unix socket에 바인딩된 FD 추가 peers.add(null); int loopCount = GC_LOOP_COUNT; while (true) { int index; if (loopCount <= 0) { gc(); loopCount = GC_LOOP_COUNT; } else { loopCount--; } try { fdArray = fds.toArray(fdArray); index = selectReadable(fdArray); //Socket Select } catch (IOException ex) { throw new RuntimeException("Error in select()", ex); } if (index < 0) { throw new RuntimeException("Error in select()"); } else if (index == 0) { //0번 index 이벤트 발생 -> 새로운 연결 요청 처리 ZygoteConnection newPeer = acceptCommandPeer(); peers.add(newPeer); fds.add(newPeer.getFileDesciptor()); //다음 loop에서 처리 } else { boolean done; done = peers.get(index).runOnce(); //runOnce 실행 if (done) { peers.remove(index); fds.remove(index); } } } }
|
위 코드의 동작은 다음의 두 그림을 참고하기 바란다.
ZygoteInit 에서 Socket의 이벤트를 체크하고 0번 소켓에 이벤트가 들어오면 ZygoteConnectionObject 를 생성, 해당 오브젝트에서 PID를 ActivityManagerServer로 넘겨준다. ActivitManagerServer 에서는 해당 PID의 Socket에 프로세스 생성 파라미터를 넘겨준다. 해당 PID의 Socket에 이벤트가 발생하면 ZygoteConnection Object의 runOnce 코드를 실행한다.
ZygoteConnection Object의 runOnce 코드는 다음과 같다.
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller { String args[]; Arguments parsedArgs = null; FileDescriptor[] descriptors; try { args = readArgumentList(); //Socket에 수신된 메시지를 읽는다. descriptors = mSocket.getAncillaryFileDescriptors(); } catch (IOException ex) { Log.w(TAG, "IOException on command socket " + ex.getMessage()); closeSocket(); return true; } if (args == null) { // EOF reached. closeSocket(); return true; } /** the stderr of the most recent request, if avail */ PrintStream newStderr = null; if (descriptors != null && descriptors.length >= 3) { newStderr = new PrintStream( new FileOutputStream(descriptors[2])); } int pid = -1; FileDescriptor childPipeFd = null; FileDescriptor serverPipeFd = null; try { parsedArgs = new Arguments(args); //요청을 파싱한다. applyUidSecurityPolicy(parsedArgs, peer, peerSecurityContext); applyRlimitSecurityPolicy(parsedArgs, peer, peerSecurityContext); applyCapabilitiesSecurityPolicy(parsedArgs, peer, peerSecurityContext); applyInvokeWithSecurityPolicy(parsedArgs, peer, peerSecurityContext); applyseInfoSecurityPolicy(parsedArgs, peer, peerSecurityContext); applyDebuggerSystemProperty(parsedArgs); applyInvokeWithSystemProperty(parsedArgs); int[][] rlimits = null; if (parsedArgs.rlimits != null) { rlimits = parsedArgs.rlimits.toArray(intArray2d); } if (parsedArgs.runtimeInit && parsedArgs.invokeWith != null) { FileDescriptor[] pipeFds = Libcore.os.pipe(); childPipeFd = pipeFds[1]; serverPipeFd = pipeFds[0]; ZygoteInit.setCloseOnExec(serverPipeFd, true); } pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo, parsedArgs.niceName); //새로운 프로세스를 생성한다. } catch (IOException ex) { logAndPrintError(newStderr, "Exception creating pipe", ex); } catch (ErrnoException ex) { logAndPrintError(newStderr, "Exception creating pipe", ex); } catch (IllegalArgumentException ex) { logAndPrintError(newStderr, "Invalid zygote arguments", ex); } catch (ZygoteSecurityException ex) { logAndPrintError(newStderr, "Zygote security policy prevents request: ", ex); } try { if (pid == 0) { // in child IoUtils.closeQuietly(serverPipeFd); serverPipeFd = null; handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr); //새로운 어플리케이션을 실행 // should never get here, the child is expected to either // throw ZygoteInit.MethodAndArgsCaller or exec(). return true; } else { // in parent...pid of < 0 means failure IoUtils.closeQuietly(childPipeFd); childPipeFd = null; return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs); } } finally { IoUtils.closeQuietly(childPipeFd); IoUtils.closeQuietly(serverPipeFd); } }
|