'2017/02/18'에 해당되는 글 3건

  1. 2017.02.18 [android framework] init 프로세스 #1
반응형

 리눅스상에서 모든 프로세스는 init 프로세스에 의해서 생성되어 실행된다. init 프로세스는 첫번쨰 프로세스가 되며, 백그라운드에서 동작하면서 다른 프로세스를 감시하고, 프로세스가 종료되면 자원을 반환한다. 안드로이드에서는 일반적인 init 프로세스 이외에 추가적인 기능을 한다. 


 Android Kernel 소스를 다운로드 하거나 빌드하는 것은 다음의 사이트를 참조하면 된다.


http://source.android.com/source/building-kernels.html



 Nexus 나 Pixel 에 들어간 Qualcomm MSM Chipsets 에 대한 소스를 다운로드 해보자.

git clone https://android.googlesource.com/kernel/msm.git

cd msm/

git branch -a

git checkout -b android-4.4.y remotes/origin/android-4.4.y



1. init 프로세스 실행

 msm/init/main.c 에 start_kernel() 부분이 있다. 이 코드의 마지막에는 rest_init() 부분이 있는데, 여기에 kernel_init에 대한 kernel_thread 를 생성하는 부분이 있으며 kernel_init 함수는 다음과 같으며 옵션에 따른 프로세스를 실행하는 코드가 있다.

static int __ref kernel_init(void *unused)

{

[-------------------중략-------------------]

/*

* We try each of these until one succeeds.

*

* The Bourne shell can be used instead of init if we are

* trying to recover a really broken machine.

*/

if (execute_command) {

ret = run_init_process(execute_command);

if (!ret)

return 0;

panic("Requested init %s failed (error %d).",

     execute_command, ret);

}

if (!try_to_run_init_process("/sbin/init") ||

   !try_to_run_init_process("/etc/init") ||

   !try_to_run_init_process("/bin/init") ||

   !try_to_run_init_process("/bin/sh"))

return 0;


panic("No working init found.  Try passing init= option to kernel. "

     "See Linux Documentation/init.txt for guidance.");

}


 안드로이드에서 정상 부팅을 위해서 커널의 부팅 옵션에서 init=/init 과 같은 옵션이 주어져야 한다. 왜냐하면 안드로이드 빌드후에 생성되는 루트 파일 시스템에서 init 프로세스가 최상위 디렉토리에 존재하기 때문이다.


2. init프로세스 메인

 init 프로세스는 android aosp source를 다운로드 해서 봐야 한다. android aosp source는 다음의 사이트에서 다운로드 할 수 있다.


https://source.android.com/source/requirements.html


 소스 코드를 다운로드 하기 위해서는 repo 를 설치하고 PATH를 설정해야 한다.

$ curl https://storage.googleapis.com/git-repo-downloads/repo > [PATH경로]/repo

$ chmod a+x [PATH경로]/repo

$ git config --global user.name "Your Name"

$ git config --global user.email "you@example.com"

$ repo init -u https://android.googlesource.com/platform/manifest -b [AndroidVersionTag]

$ repo sync


 AndroidVersionTag는 다음에서 확인 가능하다.


https://source.android.com/source/build-numbers.html#source-code-tags-and-builds



다음 그림은 init process의 주요 역활과 기능에 대한 것이다.


 Init Process는 Android AOSP 의 system/core/init/init.c 에서 확인 가능하다. main함수는 다음과 같다.

int main(int argc, char **argv)

{

    if (!strcmp(basename(argv[0]), "ueventd"))

        return ueventd_main(argc, argv);

    //ueventd 를 호출하는 부분

    // ueventd에서 디바이스를 초기화 한다.

    if (!strcmp(basename(argv[0]), "watchdogd"))

        return watchdogd_main(argc, argv);


[-------------------중략-------------------]


    // 부팅에 필요한 디렉토리를 생성하고 마운트 한다.

    mkdir("/dev", 0755);

    mkdir("/proc", 0755);

    mkdir("/sys", 0755);

    mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");

    mkdir("/dev/pts", 0755);

    mkdir("/dev/socket", 0755);

    mount("devpts", "/dev/pts", "devpts", 0, NULL);

    mount("proc", "/proc", "proc", 0, NULL);

    mount("sysfs", "/sys", "sysfs", 0, NULL);


 [-------------------중략-------------------]



    open_devnull_stdio(); 

    klog_init();

    // 로그 출력장치 초기화, 

    //   - open_devnull_stdio() : 표준입출력을 /dev/__null__ 로 redirection

    //   - klog_init() : 로그 메시지 출력을 위한 새로운 출력 장치 초기화 (/dev/__kmsg__)

    


 [-------------------중략-------------------]


    property_init();

    // 모든 프로세스에서 시스템 설정 값을 공유하기 위한 프로퍼티 저장소 초기화

    // 공유 메모리 영역 생성 및 초기화 함

    // 값의 변경은 init 프로세스만 가능


 [-------------------중략-------------------]


    INFO("property init\n");

    if (!is_charger)

        property_load_boot_defaults();


    INFO("reading config file\n");


    init_parse_config_file("/init.rc");

    // init.rc에 정의된 실행 파일과 환경 변수를 파싱함

    // init_parser.c 에 정의됨


    action_for_each_trigger("early-init", action_add_queue_tail);

    // early-init 명령어 실행

    

    // 아래는 init.c에 정의된 함수를 action queue에 넣는 것임

    queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");

    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");

    queue_builtin_action(keychord_init_action, "keychord_init");

    queue_builtin_action(console_init_action, "console_init");

    //android Logo 출력 action 


    /* execute all the boot actions to get us started */

    action_for_each_trigger("init", action_add_queue_tail);

    // init 명령어 실행 


 [-------------------중략-------------------]

  

    /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random

     * wasn't ready immediately after wait_for_coldboot_done

     */

    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");

    queue_builtin_action(property_service_init_action, "property_service_init");

    // 프로퍼티 서비스 시작을 위한 Action 을 Queueing

    // property_service.c의 start_property_service를 실행함

    // 이 함수에서는 property_service를 위한 socket을 생성함


    queue_builtin_action(signal_init_action, "signal_init");

     //자식 프로세스가 종료 되었을떄 발생하는 Signal(SIGCHLD) 처리 Init

     // signal_handler.c 의 signal_init 함수 호출

     // 이 함수에서는 signal 처리를 위해서 socket을 생성하고 처리를 위한 핸들러를 등록한다


  [-------------------중략-------------------]


    for(;;) {

        int nr, i, timeout = -1;


        execute_one_command();

        //이벤트 확인 전에 액션 리스트의 명령어  실행

        restart_processes();

        // 자식 프로세스가 종료되었을 때 재시작하거나 종료하는 구문


        if (!property_set_fd_init && get_property_set_fd() > 0) {

            ufds[fd_count].fd = get_property_set_fd();

            ufds[fd_count].events = POLLIN;

            ufds[fd_count].revents = 0;

            fd_count++;

            property_set_fd_init = 1;

        }

        if (!signal_fd_init && get_signal_fd() > 0) {

            ufds[fd_count].fd = get_signal_fd();

            ufds[fd_count].events = POLLIN;

            ufds[fd_count].revents = 0;

            fd_count++;

            signal_fd_init = 1;

        }

        if (!keychord_fd_init && get_keychord_fd() > 0) {

            ufds[fd_count].fd = get_keychord_fd();

            ufds[fd_count].events = POLLIN;

            ufds[fd_count].revents = 0;

            fd_count++;

            keychord_fd_init = 1;

        }

        //POLL 이벤트 등록을 위한 디스크립터 처리


 [-------------------중략-------------------]


        nr = poll(ufds, fd_count, timeout);

        //등록한 파일 디스크립터에서 발생한 이벤트를 기다림.

        if (nr <= 0)

            continue;

        for (i = 0; i < fd_count; i++) {

            if (ufds[i].revents == POLLIN) {

                if (ufds[i].fd == get_property_set_fd())

                    handle_property_set_fd();

                else if (ufds[i].fd == get_keychord_fd())

                    handle_keychord();

                else if (ufds[i].fd == get_signal_fd())

                    handle_signal();

            }

        }

        //이벤트를 처리함

    }

    return 0;

}


3. init.rc 분석

init.rc는 on 키워드로 시작하는 액션 리스트와 service 키워드로 시작하는 서비스 리스트로 나뉜다. init.rc는 system/core/rootdir/init.rc 에서 찾을 수 있다. 

import /init.environ.rc

import /init.usb.rc

import /init.${ro.hardware}.rc

import /init.trace.rc


on early-init

    write /proc/1/oom_adj -16

    setcon u:r:init:s0

    start ueventd

    mkdir /mnt 0775 root system


on init

sysclktz 0

loglevel 3

# Backward compatibility

    symlink /system/etc /etc

    symlink /sys/kernel/debug /d


# Right now vendor lives on the same filesystem as system,

# but someday that may change.

    symlink /system/vendor /vendor


# Create cgroup mount point for cpu accounting

    mkdir /acct

    mount cgroup none /acct cpuacct

    mkdir /acct/uid


 [-------------------중략-------------------]


## Daemon processes to be run by init.

##

service ueventd /sbin/ueventd

    class core

    critical

    seclabel u:r:ueventd:s0


service healthd /sbin/healthd

    class core

    critical

    seclabel u:r:healthd:s0


service healthd-charger /sbin/healthd -n

    class charger

    critical

    seclabel u:r:healthd:s0


on property:selinux.reload_policy=1

    restart ueventd

    restart installd


service console /system/bin/sh

    class core

    console

    disabled

    user shell

    group log


 [-------------------생략-------------------]



 on init 섹션에서는 환경 변수를 등록하고, 필요한 디렉토리를 생성하고 설정, 마운트 한다. 다음은 안드로이드 파일 시스템 구조이다.android root filesystem에 대한 이미지 검색결과

  on boot 섹션에서는 기본 네트워크 설정, 디렉토리 및 파일 퍼미션 설정, 어플리케이션 구동 조건 설정등을 한다.


 service 섹션에는 부팅음을 출력하는 프로세서와 같은 일회성 프로그램이나 백그라운드로 구동되면서 어플리케이션이나 시스템 운영에 관여하는 데몬 프로세스들에 대해서 기술한다.


 init.c에서 init_parse_config_file("/init.rc");를 통해 파싱이 완료 되면, 액션 리스트와 서비스 리스트가 완성된다. 


 액션 리스트는 exeute_one_command 에서 실행된다. 

void execute_one_command(void)

{

    int ret;


    if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {

        cur_action = action_remove_queue_head();

        cur_command = NULL;

        if (!cur_action)

            return;

        INFO("processing action %p (%s)\n", cur_action, cur_action->name);

        cur_command = get_first_command(cur_action);

    } else {

        cur_command = get_next_command(cur_action, cur_command);

    }


    if (!cur_command)

        return;


    ret = cur_command->func(cur_command->nargs, cur_command->args);

    INFO("command '%s' r=%d\n", cur_command->args[0], ret);

}

 action_remove_queue_head 로 action 리시트 헤더를 가져오고, command 구조체를 가져온 다음에 해당 함수를 실행 시킨다.


 서비스의 경우 init.rc 의 on boot  섹션의 class_start 명령어로 service 섹션의 해당 클래스 프로세스를 실행하게 된다. 예를 들어 on boot 섹션의 class_start core 부분이 있는데, 서비스 정의 항목을 보면 관련 class를 지정하는 부분이 있다.

on boot

 ......

# Define default initial receive window size in segments.

    setprop net.tcp.default_init_rwnd 60


    class_start core

    class_start main

 .....
## Daemon processes to be run by init.
##
service ueventd /sbin/ueventd
    class core
    critical
    seclabel u:r:ueventd:s0

service healthd /sbin/healthd
    class core
    critical
    seclabel u:r:healthd:s0


 do_class_start에는 전역적으로 선언된 service_list 구조체로 해당 class의 서비스를 찾아서 service_Start 함수로 실행시킨다. init.rc 는 AIL(Android Init Language)라는 문법을 통해 정의된다.


[다음 포스팅에 계속]

반응형
Posted by alias
,