최종 수정일 : 2019-03-22

안드로이드 init 프로세스가 수행하는 기능 4가지

  1. 자식 프로세스의 종료 처리
  2. 디바이스 노드 파일을 생성하여 애플리케이션 디바이스 드라이버에 접근할 때 사용
  3. 프로퍼티 서비스를 제공하여 시스템 동작에 필요한 환경 변수를 저장
  4. init.rc 파일을 분석해서 해당 파일에 담긴 내용에 따른 기능 수행 (init.rc 파일에는 init 프로세스가 해야 할 일이 기술되어 있다.)

main() 함수

시그널 : 리눅스의 프로세스들이 정보 교환을 위해 주고받는 메시지
시그널 핸들러 : 각 프로세스가 다른 프로세스에서 발생하는 시그널을 처리하기 위한 루틴

(1) SIGCHLD 시그널 핸들러 등록

sigaction(SIGCHLD, &act, 0);  

SIGCHLD 시그널은 init 프로세스가 생성한 프로세스가 종료됐을 때 발생한다.
시그널 등록 함수인 sigaction() 함수에 sigaction 구조체의 내용을 채워 인자로 전달한다.

(2) 부팅에 필요한 디렉토리를 생성하고 마운트
시스템 운용 중에만 필요한 디렉토리는 init 프로세스가 동작 중에 생성되고 시스템이 종료되면 사라진다.
init 프로세스 ->
mkdir(“/dev”, 0755) 디바이스 노드가 위치하는 /dev 디렉토리 생성
open_devnull_stdio() 함수 호출 : /dev 디록토리 이하에 null 이라는 디바이스 노드 파일을 생성하고, 표준 입력, 표준 출력, 표준 에러 출력을 모드 null 장치로 리다이렉션(redirection)한다. 입출력 파일이 null 장치로 변경되어 메시지를 출력할 수 없으므로 log_init() 함수를 이용한다. log_init() 함수 호출 : 로그 메시지를 출력하기 위한 새로운 출력 장치를 제공한다. “/dev/kmsg” 디바이스 노드 파일을 생성한다. kmsg 장치를 통해 커널의 메시지 출력 함수인 printk() 함수를 사용하여 로그 메시지를 출력한다.

(3) init.rc 파싱

parse_config_file(/init.rc);  

init.rc 파일을 파싱 : init.rc 파일은 init 프로세스가 수행할 기능들을 기술해 놓은 파일이다.
일반적인 리눅스 : 루트 파일 시스템의 “/etc/rc.d/” 디렉토리 밑에 부팅시 실행할 파일을 정의해 놓고, “etc” 이하에 환경 변수를 설정하는 스크립트를 둔다.
안드로이드 : init.rc, init.{hardware}.rc 두 파일만 사용하여 실행파일고 환경 변수를 정의한다.
init.rc : 안드로이드가 동작하는데 있어 공통적으로 필요한 환경 설정 및 프로세스에 대해 정의
init.{hardware}.rc : 안드로이드가 적용되는 플랫폼에 따라 특화된 프로세스나 환경 설정 등을 정의

init.rc 파일을 읽어 파싱한 후 서비스 리스트와 액션 리스트를 구성한다.
서비스 리스트와 액션 리스트 : init.rc 파일에서 service 키워드를 가진 내용들은 서비스 리스트라고 하며, 그외의 키워드들은 액션 리스트로 부르기로 한다. init 프로세스에서 전역 구조체로 선언된 service_list와 action_list에 연결리스트 형태로 등록된다.

(4) QEMU 초기화

qemu_init();  

에뮬레이터 환경을 위해 QEMU 장치를 초기화한다.

(5) init.{hardware}rc 파싱

snprintf(tmp, sizeof(tmp), /init.%s.rc, hardware);  
parse_config_file(tmp);  

init.{hardware}.rc 파일을 파싱한다. init.{hardware}.rc 파일로부터 서비스 리스트와 액션 리스트를 생성한다. 이 리스트들은 init.rc 파일에서 이미 생성한 서비스 리스트와 액션 리스트에 추가된다.

(6)

action_for_each_trigger(early-init, action_add_queue_tail);  
drain_action_queue();  

init.rc 파일의 ‘early-init, init, early-boot, boot’ 섹션에 포함된 명령어들을 순서로 실행한다.
action_for_each_trigger() 함수는 첫 번째 인자에 해당하는 섹션의 명령어들을 실행 큐인 action_add_queue_tail에 저장한다. drain_action_queue() 함수를 통해 실행 큐에 저장된 명령어들을 하나씩 실행한다.

(7) 정적 디바이스 노드 파일 생성

device_fd = device_init();  

정적 디바이스 노드 파일을 생성한다.

(8) 프로퍼티 서비스 초기화

property_init();  

프로퍼티 서비스 초기화를 진행한다. 안드로이드는 프로퍼티라는 저장소를 제공하여 시스템의 모든 프로세스에서 시스템의 설정 값을 공유한다. 함수 호출 시 프로퍼티 영역은 공유 메모리 영역(Android Shared Memory, ASHMEM)에 생성되고 초기화된다.

(9) 부팅 로고 출력

#define INIT_IMAGE_FILE “/initlogo.rle”  
load_565rle_image(INIT_IMAGE_FILE);  

안드로이드 부팅 로고를 화면에 출력한다.

(10) 프로퍼티 영역 초기화

property_set(  , );  

앞에서 생성한 프로퍼티 영역에 시스템 운용에 필요한 몇몇 초기 값들을 설정한다.

(11) 프로퍼티 서비스 시작

property_set_fd = start_property_service();  

프로퍼티 서비스를 시작한다.
프로퍼티를 초기화한다.

(12)

socketpair(AF_UNIX, SOCK_STREAM, 0, s)  

init 프로세스는 SIGCHLD 시그널 핸들러를 소켓을 통해 연결한다. 이를 통해 SIGCHLD 시그널이 발생하자마자 해당 핸들러를 호출 할 수 있다.
socketpari() 함수는 서로 연결된 소켓 쌍을 생성한다.
이벤트 처리 핸들러에서 signal_recv_fd의 값을 감시하고, 1로 설정되면서 자식 프로세스 종료 처리 핸들러를 호출하게 된다.

(13)

    ufds[0].fd = device_fd; // 디바이스 노드 생성 이벤트 처리를 위한 파일 디스크립터  
    ufds[0].events = POLLIN;  
    ufds[1].fd = property_set_fd; // 프로퍼티 서비스 요청 시 발생하는 이벤트 처리를 위한 디스크립터  
    ufds[1].events = POLLIN;  
    ufds[2].fd = signal_recv_fd; // SIGCHLD 시그널 처리를 위한 파일 디스크립터  
    ufds[2].events = POLLIN;  

이벤트 처리 루프에서 감시할 이벤트를 설정한다. init 프로세스가 감시할 파일 디스크립터를 등록한다. POLL에 등록된 파일 디스크립터는 poll() 함수에서 이벤트를 기다린다. 이벤트가 발생하면 poll() 함수를 빠져나와 이벤트를 처리한다.
ufds 배열은 pollfd 구조체 변수이고 파일 디스크립터를 등록한다.
파일 디스크립터는 poll() 함수 인자로 넘겨져 해당 파일 디스크립터의 이벤트를 감시한다.
pollfd 구조체의 fd는 모니터링할 파일 디스크립터 번호이고, events는 모니터링해야 할 이벤트다. POLL에 파일 디스크립터를 등록한 후에는 아래 이벤트 처리 루프에서 poll() 함수를 통해 이벤트를 감시한다.

(14)

for(;;) {  
    drain_action_queue(); // 액션 리스트에 들어 있는 명령 중 실행되지 않은 명령들을 실행한다.  
    restart_processes(); // 자식 프로세스가 종료됐을 때 자식 프로세스를 재시작하거나 종료한다.  
    nr = poll(ufds, fd_count, timeout); // poll() 함수를 통해 이벤트를 기다린다. 이벤트가 발생하면 pollfd 구조체 변수인 udfs.revents에 이벤트 정보가 담긴다.  
    if (ufds[2].revents == POLLIN) { // SIGCHLD가 발생하면 POLLIN 이벤트를 등록한다.  
        read(signal_recv_fd, tmp, sizeof(tmp));  
        while(!wait_for_one_process(0));  
            continue;  
    }  

    if(ufds[0].revents == POLLIN)  
        handle_device_fd(device_fd); // 핫플러그 장치가 삽입됐을 때 디바이스 노프 파일을 생성한다.  
    if(ufds[1].revents == POLLIN)  
        handle_property_set_fd(property_set_fd); // 프로퍼티 변경 요청을 처리한다.  
}  

이벤트 처리 루프이다.