menu

Android Zygote에 대해서 조금만 알아보자

Zygote 란?

안드로이드 애플리케이션을 빠르게 실행하기 위한 도구로 DEX 중간 언어로 컴파일된 서비스를 위한 가상머신과 런타임 환경을 생성 해 줌.

DEX IL로 컴파일된 코드를 실행하려면 시스템은 가상머신(Dalvik, ART)을 초기화 해야하며, 앱이 시작되기 위한 새로운 런타임 환경을 시작하고 초기화할 수 있는 방법을 제공하는 녀석이 바로 Zygote다.

Zygote는 Desktop 용 JVM의 문제 중 느린 앱 실행 속도를 해결 하기 위한 해결책이었다. 만약 안드로이드에 Zygote가 없었다고 가정해보면, 느린 앱 실행과 앱이 실행될 때마다 발생하는 리소스는 모바일에서 가장 중요한 배터리 전력을 정말 야무지게 갉아 먹었을 것이다.

그럼 Zygote는 어떻게 가볍고 빠르게 실행되는걸까?

  • 미리 로드된 런타임: 안드로이드 기기가 부팅될 때, Zygote 프로세스는 미리 ART를 로드하여 메모리에 상주시킨다. 이로 인해 새로운 앱을 실행할 때마다 ART를 다시 로드할 필요가 없기 때문에 리소스와 실행 시간이 절약됨
  • Process Fork: Zygote는 새로운 앱 프로세스를 생성 할 때 Fork 매커니즘을 사용한다. Zygote는 자기 자신을 복제(clone)해서 시스템 수준의 프로세스인 Zygote`를 생성한다.
    • 그렇게 복제된 Zygote` 프로세스와 부모 Zygote 프로세스가 완전히 동일한 메모리를 공유하기 때문에 자식 프로세스는 순식간에 시작하게 된다.
    • 커널은 새로운 프로세스를 위해 많은 리소스를 사용할 필요도 없으며, 안드로이드 프레임워크 라이브러리를 로드할 필요도 없다.(Zygote가 이미 모든 것을 로드해 두었기 때문에)

fork 된 Zygote` 프로세스와 부모 Zygote 프로세스는 동일한 물리적 메모리 주소를 갖기 때문에 두 프로세스 중 하나에서 메모리 내용을 변경시 다른 프로세스도 영향을 받는 상황이 생길 수 있기 때문에 COW(Copy-on-Write) 페이징 기법을 사용한다.

Zygote 시작

Zygote는 init에 의해 실행되며 init에 대해 짧게 설명한 글이 존재한다. 부끄럽지만 한번 참고해보는것도 나쁘지 않을것같다.Android init Process에 대해서(짧게) 알아보자

init은 app_process를 실행하고 결과 프로세스에 zygote라는 이름을 부여한다.

대부분의 최신 안드로이드는 32비트, 64비트 애플리케이션용으로 각각 한 개씩, 총 2개의 Zygote를 실행하고 64비트 버전을 기본으로 만든다.

최신 안드로이드 디바이스는 4개의 zygote 중에서 디바이스에 알맞게 기본 값이 설정된다. zygote 종류 확인

32비트 앱을 기본으로 설정한 구형 시스템의 init.zygote32_64.rc

32비트 zygote(zygote)를 기본으로 선 실행후, 64비트 zygote(zygote_secondary) 실행

service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd
service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
    class main
    socket zygote_secondary stream 660 root system
    onrestart restart zygote
app_process 실행 규칙

app_process [java-options] cmd-dir start-class-name [options]

  • java-options: 가상머신으로 전달되는 옵션
  • cmd-dir: 프로세스가 실행될 디렉토리
  • start-class-name: 가상머신에서 생성할 클래스 이름, app_process는 전달받은 클래스를 가상머신으로 로딩한 후 해당 클래스의 main() 메소드 호출
  • options: 실행될 클래스로 전달될 옵션

init이 구동하는 app_process 애플리케이션은 명령을 해독하고 지시된 클래스를 구동하는 작은 크기의 앱일 뿐이다.

Zygote는 시작 단계에서 세 가지 주요 임무를 수행한다.

  • 시스템이 새로운 앱을 시작하기 위해 연결해야 하는 소켓 등록
  • 필요 시 안드로이드 리소스와 라이브러리 사전 로딩
  • 안드로이드 시스템 서버 시작

Zygote는 이 세 가지 임무를 완료한 후 소켓 연결을 기다리는 루프에 진입한다.

  • init 스크립트에서 최초로 정의한 파라미터를 통해 들어온 이름을 사용해서 소켓을 생성함.
  • preload 메서드를 호출하여 클래스, 라이브러리, 리소스, 웹뷰 등등 미리 로딩(첫 번째 애플리케이션이 복제될 때까지 프레임워크를 미리 로딩하는 비용 발생을 연기시킬 수 있는 최소화된 시스템을 지원한다.) preload 메서드 실행이 완료되면 Zygote는 완전히 초기화되고 새로운 앱을 매우 빠르게 복제해서 자신의 가상 메모리를 새 앱과 공유할 준비가 끝난다.
  • 마지막 SystemServer는 바로 아래에 설명해놓았다.

SystemServer란?

안드로이드에서 제공하는 프레임워크 API와 하드웨어의 통신을 담당하는 서비스로 네이티브 서비스, 코어 플랫폼 서비스, 하드웨어 서비스 등 다양한 서비스를 담고 있는 컨테이너라고 보면 된다.

Context.getSystemService() 의 메소드로 원하는 시스템 서비스를 사용할 수 있다.

참고한 문서 보러가기

결론
  1. 디바이스 부팅: 디바이스 전원이 켜지면 BootLoader가 시작된다. BootLoader는 디바이스의 하드웨어와 기본적인 소프트웨어 초기화를 수행하며, 이후 Android OS를 부팅하기 위해 커널 이미지를 로드한다.
  2. 리눅스 커널 로딩: BootLodaer가 커널 이미지를 로드하고 실행시키면, 리눅스 커널이 초기화되고 디바이스의 하드웨어와 상호작용할 수 있는 기반을 제공한다.
  3. init 프로세스 시작: 리눅스 커널이 초기화되면, init 프로세스가 시작된다. init 프로세스는 시스템 초기화 및 프로세스 관리를 담당하는 첫 번째 프로세스로, Android 운영체제의 초기 설정과 부팅 서비스를 시작하는 역할을 한다.
  4. init 프로세스 설정 파일 읽기: init 프로세스는 /init.rc 혹은 /init.zygote32_64.rc와 같은 설정 파일을 읽어 시스템의 초기 구성 및 서비스 시작을 정의한다.
  5. Zygote Process(app_process) 시작: init 프로세스가 실행되면서, Zygote 프로세스가 시작된다. Zygote 프로세스는 새로운 app_process를 초기화 하고 관리하기 위한 기반을 제공하며, 앱의 실행 환경을 설정한다.
  6. VM 초기화: Zygote 프로세스 내에서는 VM(Dalvik, ART)이 초기화 되며, 이 단계에서 VM은 앱의 실행 환경을 제공하고, 앱 코드를 실행할 수 있도록 준비한다.
  7. SystemServer 시작: Zygote Process(app_process)가 시작된 후, ZygoteInit.java 클래스를 사용하여 SystemServer를 시작한다.
  8. SystemServer 초기화: SystemServer가 시작되면, 해당 서비스 및 컴포넌트가 초기화된다. 각 서비스는 Android 시스템의 다양한 측면을 관리하고, 필요한 리소스를 할당한다.
  9. 사용자 인터페이스 시작: SystemServer가 초기화되고 필요한 핵심 서비스가 시작되면, 사용자는 앱을 실행 할 수 있는 상태가 된 것이다.

참고 문서