SlideShare una empresa de Scribd logo
1 de 45
Descargar para leer sin conexión
Init process
in Android

       Created by Andstudy

               Andstudy Seminar
                     2009 08 22
                         김 연찬
Android Study
                                                                  2009 08 15

                           Android Study members
    송형주                   전형민                     박지훈             황세희

    이 백                   전유진                     김연찬             전승원

    강명훈                   임기영                     박은병             이덕용

    박은병                   박주애                     이덕용             구자관

    윤동렬                   김신수                     김태연



                    Spring note : http://andstudy.springnote.com/
Study web page
                    Android Pub: http://www.androidpub.com/devstudy_groupb



이 문서는 다음의 CCL (creative commons license) 을 따름니다.

           http://creativecommons.org/licenses/by-nc-sa/2.0/kr/
           저작자표시-비영리-동일조건변경허락 2.0 대한민국
Change LOG
 Change history            changes         editor
2009년 08월 22일     최초작성                   김 연찬
2009년 09월 05일     1차 교정 (PDF 배포용으로 수정)   김 연찬
About Init.

Init Process 는 PID 가 1인 프로세스이고 부팅과정에서 커널이 생성하
는 첫번째 프로세스이다.

그럼 Init Process는 언제 실행되는가?

  start_kernel() -> rest_init() -> kernel_thread() -> kernel_init() -> in
  it_post() 에서 "/init"을 수행

안드로이드 소스트리에서 Init process 코드는 어디에?

  /system/core/init/init.c
  이 문서에서 다루는 부분의 코드는 /system/core/init/ 에서 찾아 볼
  수 있다.
  Init.rc 의 경우는 /system/core/rootdir/init.rc 에 존재한다.
Android Init의 기능

SIGCHLD           Device 초
          RC 파일              Property
 signal            기화 &
          내용처리                설정
 처리                 관리
Init Process (1)
                                  Kernal
                                              Init post()


                                   Init
- 자식프로세스 처리를 위한 S                              main
IGCHLD SIGNAL handler등록
                                act.sa_handler = sigchld_handler;
                                sigaction(SIGCHLD, &act, 0);                       Sigchild handler()                        Zombi process

                                mkdir("/dev", 0755);
                                mkdir("/proc", 0755);                                                          /
Make Special purpose            mkdir("/sys", 0755);
Device node files                                                                       tmpfs
                                mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755")
                                                                                                /dev         /proc   /sys
                                mkdir("/dev/pts", 0755);
                                mkdir("/dev/socket", 0755);
                                                            xxxxxx
                                mount("devpts", "/dev/pts", "devpts", 0, NULL);           Socket       pts
                                mount("proc", "/proc", "proc", 0, NULL);
                                mount("sysfs", "/sys", "sysfs", 0, NULL);


Make Special purpose            open_devnull_stdio();
Device node files                                                                       Fd[0]              stdin             Fd[0]    stdin

                                                                                        Fd[1]                                Fd[1]
                                                                                                           stdout                    stdout
                                                                                        Fd[2]                                Fd[2]
                                                                                                           stderr                    stderr
                                                                                        Fd[3]                                Fd[3]
                                                                                                        __null__                     __null__
                                                                                       Fd[…]                                 Fd[…]




                                                                                                   parent            Fd[0]             stdin
                                log_init();                                                                          Fd[1]            stdout
Make __kmsg__ device file log                                                     Ex) FD_CLOEXEC                     Fd[2]            stderr
massage will be written to        mknode(“/dev/__kmsg__”);                                                           Fd[3]             a.txt
“/dev/__kmsg__”                   Log_fd = open(“/dev/__kmsg__”);                                  Child             Fd[0]
&set it as “FD_CLOEXEC”                                                                                              Fd[1]
                                  Fcntl(log_fd, F_SETFD, FD_CLOEXEC);                                                Fd[2]
                                                                                                                     Fd[3]
Init Process (2)
                                                         Service_list       struct service             struct service      struct service
Init.rc 파일을 Parsing 하여                                                      Listnode slist             Slist               Slist
Service_list 와 Action_list를 구                                               Char *name                 “console “          “Servicemanager”
                                parse_config_file("/init.rc");              Char *classname            “default”           “default”
성한다.                                                                        Int nargs                  0                   0
                                                                            Struct Action onrestart    “onrestart”         “onrestart”


                                                          Action_list       Action_list                 Action_list         Action_list
                                                                            Listnode alist              listnode            listnode
                                                                            Char *name                  “init”              “boot”
                                                                            Struct listnode command     Listnode command    Listnode command
                                                                            Struct command *curtent


                                                                                        QEMU란?
Qemu 에서 사용하는 메모리                qemu_init();
영역 초기화



커널 커맨드 라인(/proc/cmdli           import_kernel_cmdline(0);                                  Argument is ‘0’ : physical H/W
ne )을 읽어서 필요한 내용을                                                                          Argument is ‘1’ : QEMU emulator
전역 변수에 저장한다



커널로 부터 H/W 정보를 얻어               get_hardware_name();
와서 “init.H/W_name.rc 파일                                                                   default h/w configuration is goldfish.
                                snprintf(tmp, sizeof(tmp),"/init.%s.rc",hardware)
을 Parsing 하여 Service_list       parse_config_file(tmp);
                                                                                          ->/system/core/rootdir/etc/init.goldfish.rc
와 Action_list 에 추가한다.



Action list 에서 “early-init”이라   action_for_each_trigger("early-init“,action_a             Init.rc 와 init.goldfish.rc 에 “early-init” 의
는 name의 노드를                     dd_queue_tail);                                           name의 항목이 없다. 따라서 여기서 하는
Action_queue에 삽입하고,             drain_action_queue();                                     일은 없다.
Action_queue에 있는 노드를 실
행 시킨다.
Init Process (3)
/dev 이하에 장치 파일을              device_fd = device_init();                        /dev 이하에 장치 파일을 uevent 파일을
uevent 파일을 이용하여 생성                                                             이용하여 생성하고 접근권한을 설정한다.
하고 접근권한을 설정한다.


                                                                            Property_area memory map


ashmem_create_region(/dev/   property_init();
ashmem) 을 사용하여 공유
메모리 공간을 생성한다.




                              debuggable = property_get("ro.debuggable");
keychords 확인
                              if (debuggable && !strcmp(debuggable, "1"))
Consols 확인
                                     keychord_fd = open_keychord();
                              fd = open(console_name, O_RDWR);
                              if (fd >= 0)
                                      have_console = 1;
                              close(fd);



                             if( load_565rle_image(INIT_IMAGE_FILE) ) {
부팅 이미지 출력
                                  fd = open("/dev/tty0", O_WRONLY);          로고 파일이 있으면 로고를 출력하고,
                                  if (fd >= 0) {                             로고 파일이 없거나 로딩에 실패하면 tty0
                                     const char *msg; msg = "n ANDROID“;    에 “ANDROID” 문자열을 출력한다.
                                     write(fd, msg, strlen(msg));
                                     close(fd); }


Qemu 설정일 때, property에
                              if (qemu[0])
'ro.kernel'이라는 접두어를 붙
여 property를 set 한다.                import_kernel_cmdline(1);
Init Process (4)
                                    if (!strcmp(bootmode,"factory"))                                   Struct Property info
                                       property_set("ro.factorytest", "1");                                                          Ro.bootmode
 추가 Property 설정                     else if (!strcmp(bootmode,"factory2"))                                    name
                                       property_set("ro.factorytest", "2");
                                    else property_set("ro.factorytest", "0");                                     serial
                                                                                                                                     Bootmode[0]
                                    property_set("ro.serialno", serialno[0] ? serialno : "");
                                                                                                              value
                                    property_set("ro.bootmode", bootmode[0] ? bootmode :
                                    "unknown");
                                                                                                                                      Ro.hardware
                                    property_set("ro.baseband", baseband[0] ? baseband : "u
                                    nknown");
                                    property_set("ro.carrier", carrier[0] ? carrier : "unknown");                                       goldfish
                                    property_set("ro.bootloader", bootloader[0] ? bootloader
                                    : "unknown");                                                                                     Ro.revision
                                    property_set("ro.hardware", hardware);
                                    snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
                                    property_set("ro.revision", tmp);                                                                   xxxxxxx

Action list 에서 “init”이라는            action_for_each_trigger("init“,action_add_q
name의 노드를 Action_queue에             ueue_tail);                                                                              /
삽입하고, Action_queue에 있는              drain_action_queue();
노드를 실행 시킨다.
create basic FileSystem structure                                                 /dev         /proc        /sys           /sdcard   /system      /data   /cache


기타 property 파일 및                    property_set_fd = start_property_service();                         default 로 생성한 이미지의 /data/property 에
/data/property 경로에 저장된                                                                                 는 4개의 로케일 셋팅 정보 파일이 있다.
persistent property들을 시스템                                                                              persist.sys.country   persist.sys.language
property 영역에 로드 한다                                                                                     persist.sys.localevar persist.sys.timezone

                                    if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0)
 시그널 처리를 위한 소켓 생성                   signal_fd = s[0];      signal_recv_fd = s[1];
                                                                                                       Init이 관리
                                                                                                        하는 FD
                                    fcntl(s[0], F_SETFD, FD_CLOEXEC);
                                    fcntl(s[0], F_SETFL, O_NONBLOCK);
                                    fcntl(s[1], F_SETFD, FD_CLOEXEC);
                                    fcntl(s[1], F_SETFL, O_NONBLOCK); }

                                    if ((device_fd < 0) || (property_set_fd < 0) |
 부팅에 필요한 FD 확인                      | (signal_recv_fd < 0)) {
                                    ERROR("init startup failuren"); return 1; }
Init Process (5)
Action list 에서 “early-boot”와   action_for_each_trigger("early-boot",   - network init
“boot”이라는 name의 노드를                         action_add_queue_tail);    - System Server and daemons 의 Permissions 설정
Action_queue에 삽입하고,            action_for_each_trigger("boot",         - 각 APP group 메모리 사용 설정
Action_queue에 있는 노드를                        action_add_queue_tail);
실행 시킨다.
                               drain_action_queue();
action_list 에 있는 노드중에                                                  Action_list        Action_list                Action_list
node.name 이 "property" 인 노      queue_all_property_triggers();                                                       listnode
                                                                                          Listnode alist
드들을 acttion queue 에 추가한         drain_action_queue();                                     Char *name                 “property”
                                                                                          Struct listnode            Listnode
다.                                                                                                                         command
                                                                                                      command
Action_queue에 있는 노드를 실                                                                    Struct command
행 시킨다.                                                                                                 *curtent


                               ufds[0].fd = device_fd;
init 프로세스가 poll함수로 감시          ufds[0].events = POLLIN;                       Ufds[ ]
                                                                                        [0] fd=device_fd event = POLLIN
할 파일디스크립터 설정                    ufds[1].fd = property_set_fd;
                               ufds[1].events = POLLIN;                                 [1] fd=property_set_fd event = POLLIN
                                                                       Init
                               ufds[2].fd = signal_recv_fd;
                                                                                        [2] fd=signal_recv_fd event = POLLIN
                               ufds[2].events = POLLIN;
                               ufds[3].fd = keychord_fd;
                                                                                        [3] fd=keychord_fd event = POLLIN
                               ufds[3].events = POLLIN;

무한 루프 문에서 action               For(;;;)
queue에 실행할 action이 있
으면 실행하고 재시작이 필요                    drain_action_queue();
한 프로세스가 있으면 재시작                    restart_processes();
해준다. ufds 를 감시하고
POLLIN 이 발생하면 해당 핸
들러를 통해 처리 한다.
                               while (!wait_for_one_process(0))
                               handle_device_fd(device_fd);
해당 fd의 POLLIN 에 따라 4가          handle_property_set_fd(property_set_fd);
지 핸들러를 호출한다.                   handle_keychord(keychord_fd);
Init.c 분석 (1)
                                 • File descriptor, Signal 구조체, Polling 을 위한 구조체 등
Init이 사용할 자료구조 선언
                                 • int device_fd = -1; struct sigaction act; struct pollfd ufds[4];


                                 • 자식프로세스 처리를 위한 SIGCHLD SIGNAL handler등록
   SIGCHLD Handler등록


                                 • /dev, /proc, /sys 디렉토리를 각각 생성
 디렉토리를 생성 및 마운트
                                 • tmpfs, devpts, proc, sysfs 마운트


                                 • stdin, stdout, stderr File descriptor 를 문자 디바이스 파일
    open_devnull_stdio();         /dev/__null__ 의 FD 로 연결한다.


                                 • /dev/__kmsg__ 디바이스 파일을 생성하고, 파일 디스크립터
          log_init();             를 log_fd에 저장


                                 • /dev/__kmsg__에 log 내용을 기록
INFO("reading config filen");
SIGCHILD SIGNAL
SIGCHILD Signal
- 자식 프로세스가 멈추거나 종료하거나 추적당하는 경우 부모프로세스가 받
는 시그널
Init process 는 왜 SIGCHILD 시그널을 사용할까?
- 리눅스에서 자식프로세스 보다 부모 프로세스가 먼저 죽는다면, 자식프로세
스의 부모를 init process로 만들어준다. 그래서 고아 프로세스가 죽을 때 처리
해야 하는 일을 init process가 대신하게된다. 따라서 init process는SIGCHILD
Signal 를 처리 해야만 한다.
 - init process 의 자식프로세스 중에 프로세스가 종료하고 재 시작 해야 하는
프로세스가 있다면, 재 시작 관련 설정을 해줘야 한다. (ex. Restart 옵션으로 시
작된 Service)
SA_NOCLDSTOP flag: 만약 SIGCHLD의 시그널 핸들러일 경우 자식 프
로세스의 상태가 stop일 경우는 SIGCHLD 시그널이 발생 안됨.
디렉토리를 생성 및 마운트
                                                /




                           /dev               /proc    /SYS

                          [tmpfs]             [proc]   [sysfs]



                 /pts               /socket

               [devpts]


File System     설명
Tmpfs          tmpfs는 램파일시스템의 일종(주로 성능 향상 목적)
Devpts         devpts는 가상 터미널을 위한 파일시스템
Proc           Proc fs는 커널메모리에서 돌아가는 일종의 가상 파일시
               스템
Sysfs          sysfs 파일 시스템은 proc, devfs, devpts 파일 시스템을
               하나로 통합한 파일 시스템 (Linux Kernel 2.6 에서 도입)
open_devnull_stdio()
static const char *name = "/dev/__null__";
if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {   주번호 1 부번호 3
      fd = open(name, O_RDWR);
         unlink(name);
         if (fd >= 0) {
          dup2(fd, 0); dup2(fd, 1); dup2(fd, 2);
          if (fd > 2) {
               close(fd); }
이후로는 stdio 가 null dev로 설정되어 init process가 어떠한 메시지
를 stdio로 보내더라도 null dev로 전달되어 stdio 불가능해진다.

Fd[0]              stdin                  Fd[0]           stdin
Fd[1]             stdout                  Fd[1]          stdout
Fd[2]                                     Fd[2]
                  stderr                                  stderr
Fd[3]                                     Fd[3]
                  __null__                               __null__
Fd[…]                                     Fd[…]
log_init()
void log_init(void)
   static const char *name = "/dev/__kmsg__";
           if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) { 주번호 1 부번호 11
             log_fd = open(name, O_WRONLY);
           fcntl(log_fd, F_SETFD, FD_CLOEXEC);
           unlink(name);
/dev/__kmsg__ 디바이스 파일을 생성하고, 파일 디스크립터를 log_fd에 저장

 FD_CLOEXEC            fd의 close-on-exec 플래그를 arg의 FD_CLOEXEC
                       비트에 의해 지정된 값으로 설정한다.
 close-on-exec         보통 프로세스에서 exec를 시켜서 새로운 프로세스를 실행시키면
                       새로운 프로세스는 기존의 프로세스의 이미지를 덮어쓰게 된다.
                       그러면서 기존 프로세스가 열었던 파일 지정자를 그대로 넘겨주게
                       된다. 그러나 기존 프로세스가 열었던 파일 디스크립터의 close-
                       on-exec가 set됐을 경우 해당 파일은 새로운 프로세스로는 상속이
                       되지 않는다.
 Init 에서 close-on-     /dev/__kmsg__는 init이 unlink를 했기 때문에 파일의 데
 exec 의 의미             이터는 남아있지만 접근할 이름이 없어졌다. 그리고
                       close-on-exec를 set 함으로써 fork를 통해서도 파일 디스
                       크립터는 상속이 되지 않는다. 따라서 /dev/__kmsg__는
                       Init process 만 접근 가능하다.
close-on-exec flag

close-on-exec = unSET                 close-on-exec = SET

Parent 가 “a.txt”를 open() 후에 Fork()를
이용하여 child를 생성할 때 fd 값의 차이.

                                                   Fd[0]    stdin
             Fd[0]          stdin       parent
  parent
                                                   Fd[1]    stdout
             Fd[1]         stdout
             Fd[2]         stderr                  Fd[2]    stderr

             Fd[3]          a.txt                  Fd[3]     a.txt

            Fd[0]                                  Fd[0]
  Child                                 Child
            Fd[1]                                  Fd[1]
            Fd[2]
                                                   Fd[2]
            Fd[3]
                                                    Fd[3]
Init.c 분석 (2)
                                 • init.rc 파일을 파싱해서 각 action, service 섹션별로 연
parse_config_file("/init.rc");    결 리스트를 생성한다



                                 • 안드로이드의 에뮬레이터로 사용되는 QEMU와 관련된
        qemu_init();              접근 권한 관련 변수 초기화



                                 • 커널 커맨드 라인을 읽어서 필요한 내용을 전역 변수에
import_kernel_cmdline(0);         저장한다.



                                 • 커널로부터 CPU정보를 읽어와 hardware와 revision 정
  get_hardware_name();            보를저장



                                 • hardware에 해당하는 init.rc 파일을 추가적으로 파싱
  parse_config_file(tmp);        • snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
parse_config_file("/init.rc“)
                            INIT.RC 내용 (/system/core/rootdir/init.rc)
Init.rc 파일을 Parsing 하여 Service_list 와 Action_list를 구성한다.
RC file ? [runtime configuration files] runtime시에 환경설정을 할 수 있도록 설정내용을 정의한 파일

on init                           - 글로벌 변수 초기화
[환경 설정]                           - mount point 생성 /sdcard, /system, /data, /cache
                                  -MTD 파티션 마운드 (기본적으로 yaffs2 사용)
                                  - create basic filesystem structure
on boot                           - network init
[boot action 정의]                  - System Server and daemons 의 Permissions 설정
                                  - 각 APP group 메모리 사용 설정
class_start default               - service 정의 형식
[서비스 시작]                           - service <name> <pathname> [ <argument> ]*
                                     <option>
                                     <option>
             Service_list      struct service            struct service        struct service
                               Listnode slist            Slist                 Slist
                               Char *name                “console “            “Servicemanager”
                               Char *classname           “default”             “default”
                               Int nargs                 0                     0
                               Stuct Action onrestart    “onrestart”           “onrestart”



              Action_list      Action_list                Action_list          Action_list
                               Listnode alist             listnode             listnode
                               Char *name                 “init”               “boot”
                               Struct listnode command    Listnode command     Listnode command       CMD   CMD   CMD
                               Struct command *curtent
                                                                             CMD    CMD         CMD
APP group & 메모리 설정
APP group                ADJ Define the mem
                        value ory thresholds
                              [4k pages ]
FOREGROUND_APP             0    1536 [6M]          전면에 있는 프로그램
VISIBLE_APP                1    2048 [8M]          화면에 보이지만 실행되지 않는
                                                   APP
SECONDARY_SERVER           2    4096 [16M]         Service Demon
HOME_APP                   4    4096 [16M]         시작화면에 등록되는 APP
HIDDEN_APP_MIN             7    5120 [20M]         최소화 시킨 APP
CONTENT_PROVIDER          14    5632 [22M]         CONTENT_PROVIDER
EMPTY_APP                 15    6144 [24M]

About ADJ Value ?
 - Define the oom_adj values for the classes of processes that can be killed
by the kernel
 - OOM Killer 가 동작해야 할때 계산되는 score 값에 영향을 미치는 값
 - 숫자가 높을 수록 OOM killer에 의해 죽을 가능성이 높다.
 - OOM_killer에 절대 죽지 않는 ADJ value 는 -17 이다. (ex init process)
Service Start
[service 정의 형식]
 service <name> <pathname> [ <argument> ]*
  <option>
  <option>

[code]
service servicemanager /system/bin/servicemanager
   user system
   critical
   onrestart restart zygote
   onrestart restart media
- servicemanager 는 /system/bin/servicemanager 경로에 존재
- critcal 옵션 [ 4분 안에 4번의 오류가 발생 한다면 reboot 하겠다는 안드로이드의 시
스템 운영정책]
- onrestart 옵션 -> servicemanager 가 재시작되면 zygoto & media 도 재시작해라 라
는 의미
-ini.rc 에서 시작하는 서비스 리스트
Console adbd vold servicemanager ril-daemon zygote media
 bootsound dbus hcid hfag hsag installd flash_recovery

 ※ Init.rc 에 대한 보다 많은 정보는 /system/core/init/readme.txt 문서 참조
qemu_init()
Func 기능 : qemu_perms 영역을 memset() 을 이용하여 초기화 한다.

What is QEMU & Goldfish?

   Google은 app 개발자를 위해 SDK 배포시 emulator 를 포함시켰고, 이
   emulator 에서 동작하도록 하기위한 가상의 device configuration을
   goldfish 라 합니다. 그리고 이 emulator 를 QEMU라 합니다.



                                           Applications
                                           APP Framework
                                           Android Run- Time
                                           Libraries
               QEMU                        Linux Kernel

   ARM core                Goldfish        Emulator or Hardware
   Simulator          Hardware Simulator   Network
import_kernel_cmdline(0)
커널 커맨드 라인(/proc/cmdline )을 읽어서 필요한 내용을 전역 변수에 저장한다

  “0”을 인자로 넘길 경우는 실제 타겟을 위한 몇몇 커맨드 라인의 내용이 init
  프로세스의 전역변수에 저장

  “1”을 인자로 넘길 경우는 QEMU 에뮬레이터를 위해 모든 커맨드 라인의 내
  용을 "ro.kernel" 접두어를 붙여서 property 값을 set한다.(unix domain
  socket 이용)
Init.H/W_name.rc 파싱
커널(proc 파일시스템)로부터 CPU정보를 읽어와 hardware와 revision 정보를 전역변수
에 저장하고 이를 이용하여 hardware 관련 rc 파일을 추가적으로 파싱한다. 현재는
default로 hardware가 goldfish로 되어 있기 때문에 init.goldfish.rc 파일이 파싱 된다.
   " /system/core/rootdir/etc/init.goldfish.rc”
[code]
get_hardware_name();
snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
parse_config_file(tmp);
Init.rc 파일을 파싱해서 만들어놓은 Service_list 와 Action_list 에 해당 항목이 있다면 추
가한다.

   Service_list   struct service            struct service         struct service
                  Listnode slist            Slist                  Slist
                  Char *name                “console “             “Servicemanager”
                  Char *classname           “default”              “default”
                  Int nargs                 0                      0
                  Steuct Action onrestart   “onrestart”            “onrestart”



    Action_list   Action_list                Action_list           Action_list              Action_list
                  Listnode alist             listnode              listnode                 listnode
                  Char *name                 “init”                “boot”                   “xxx”
                  Struct listnode command    Listnode command      Listnode command         Listnode command    CMD   CMD
                  Struct command *curtent
                                                                               CMD     CMD       CMD      CMD

                                                             CMD   CMD       CMD      CMD
Init 분석 (2,3)
action_for_each_trigger("early-in   • action리스트에서 name이 “early-init”인 노드를 ACTION queue에 추
                                    가한다.
  it“,action_add_queue_tail);



                                    • ACTION queue에 저장된 커맨드를 순차적으로 실행한다.

     drain_action_queue();



                                    •uevent 파일들에 'add' 명령을 write해서 디바이스 추가 이벤트를 발생
                                    시키고 이렇게 발생된 이벤트는 uevent 소켓을 통해 수신해서 파싱
   device_fd = device_init();
                                    • uevent메시지를 수신하기 위해 사용한 소켓을 리턴함.




                                    •system property 영역을 생성하고, default property들을 저장함

         property_init();
Action_queue에 실행할 노드 추가 & 실행
action_for_each_trigger("early-init“,action_add_queue_tail)
action리스트에서 name이 “early-init”인 노드를 ACTION queue에 추가한다.
Action-list에 name=“early-init”인 노드가 없기 때문에 아무 일도 하지 않는다.
(init.rc 와 init.goldfish.rc 에 “early-init” 항목이 없다)
init 프로세스에서는 다음과 같은 4개의 boot action을 정의 할수 있다.
early-init       정의된 action 없음
init             create basic filesystem structure
early-boot,      정의된 action 없음
Boot             System Server and daemons 의 Permissions 설정
early-init, early-boot, 추후 시스템의 확장을 고려해서 설계한 것이라 추측된다.


drain_action_queue()
action queue에 있는 내용(커맨드)를 실행한다.
“early-init”에서 추가한 내용이 없을것 이므로 여기서 실행되는 내용도 없을 것
이다.
device_init()
uevent 메시지용 소켓 오픈 하고 3번의 coldboot() 함수를 호출한다.
[code]
Fd = open_uevent_socket();
coldboot(fd, "/sys/class");
coldboot(fd, "/sys/block");
coldboot(fd, "/sys/devices");
cold boot는 무슨일을 하나?
/dev/ 이하 파일들을 init 프로세스에서 생성하는데, 어떠한 디바이스들이 있는지를
확인하기 위해서 netlink socket (device_fd) 를 생성하고, 디바이스 드라이버와
uevent 메세지를 주고 받는다. 과정은 디렉토리 "/sys/class" , "/sys/block",
"/sys/devices" 를 각각 검색하여, uevent 파일을 열어서 "add" 를 write 한 후, 응답
하는 uevent 메세지를 수신하고, 해당 디바이스의 노드 생성 및 접근 권한을 설정한
다.
수신된 uevent구조체의 subsysem과               event가 firmware subsystem 관련이고,
path 필드의 정보를 참조하여 /dev 디렉              uevent->acttion 이 'ADD'이면 fork()를
토리 이하의 장치 관련 서브 디렉토리 및                 통해 새로운 프로세스를 만들고 irmware
장치 파일을 생성 혹은 삭제하고 접근 권                 관련 uevent는 새로운 자식 프로세스에
한을 설정한다.                               서 처리한다.
                                       (process_firmware_event(uevent);)
uevent
- LDM(Linux Device Model)에서는 커널 이벤트를 사용자공간으로
전달하기 위한 인터페이스를 제공하고 있다. 이것이 바로 uevent다.
- uevent는 커널에서 유저 프로세스에게 디바이스 관련 메시지를 전
달하는 netlink socket의 한 종류이다.

netlink socket ?
      netlink socket은 커널과 유저 영역 사이의 통신(IPC) 방법이다

<커널> <-------( netlink socket ) ------> <유저프로세스>

netlink socket 장점

-netlink의 경우는 커널 모듈로 추가 가능.
- 다른 IPC에 반해, netlink는 여러 프로세스 그룹으로 멀티캐스트 전송이 가능
- 시스템콜과 ioctl의 경우, 유저 애플리케이션에 의해 시작 가능 , 이에 반해
netlink는 커널에 의해서 시작 가능
Unix domain socket
Unix domain socket                             socket
Unix Domain 소켓은 같이 동일 PC내의                     네트워크 프로그래밍에서 네트워크로 연
프로세스끼리 통신을 하기 위해서 사용                           결된 서로 다른 PC간의 통신을 위해 사용
파일명을 가지고 바인딩                                   ip주소와 포트로 바인딩
sock = socket( PF_FILE, SOCK_DGRAM, 0);        sock = socket( PF_INET, SOCK_DGRAM, 0);
Unix domain 사용 예
struct sockaddr_un server_addr;
memset( &server_addr, 0, sizeof( server_addr));
server_addr.sun_family    = AF_UNIX;
strcpy( server_addr.sun_path, "/tmp/test_server.dat");
property_init()
• ashmem_create_region(/dev/ashmem) 을 사용하여 공유 메모리 공
  간을 생성한다. (anonymous shared memory)
Init 분석 (3,4)
                      •Keychord open 조건을 확인하고 참인 조건이면 open

    keychords 확인      •keychord_fd = open_keychord();




                      •console을 open해서 동작 유무를 체크한다.

     Consols 확인       •정상동작을 한다면 have_console = 1 로 셋팅




                      •565rle image(로고) 파일을 프레임 버퍼에 로딩한다.

   부팅 이미지 출력          • image 파일이 없으면 텍스트 모드로 프레임 버퍼에 “ANDORID” 출력




                      • if (qemu[0])   import_kernel_cmdline(1);
QEMU설정에 따라 CMDLINE
                      •커널 커맨드 라인의 옵션들을 QEMU에서 참조하게끔 'ro.kernel'이라는 접두어
        변경            를 붙여 property를 생성한다.



                      •각 커널 커맨드에 대한 중요 옵션들을 property로 만든다.

   추가 Property 설정     •Factory mode, ro.bootmode, ro.baseband등등
open_keychord(), Consols 확인
Keychord 란?
Keychord는 핸드폰에 있는 단축키와 조합키 와 같은 특수키와 조합 키를 지
원하기 위한 구조이다.
[code]
debuggable = property_get("ro.debuggable");
if (debuggable && !strcmp(debuggable, "1"))
      keychord_fd = open_keychord();


debuggable 셋팅값에 따라 keychord를 오픈한다.

Consols 동작 확인
[code]
fd = open(console_name, O_RDWR);
if (fd >= 0)
        have_console = 1;
close(fd);
Consols 동작 확인을 하고 have_console 변수를 셋 한다.
부팅 이미지 출력
565rle image(로고) 파일을 프레임 버퍼에 로딩한다.
[code]
if( load_565rle_image(INIT_IMAGE_FILE) ) {
      fd = open("/dev/tty0", O_WRONLY);
if (fd >= 0) {
        const char *msg;
        msg = "n"
               "n“
               “            A N D R O I D ";
 write(fd, msg, strlen(msg));
 close(fd); }


-/initlogo.rle 파일이 있다면 이미지를 로딩하여 LCD에 출력한다.
-/initlogo.rle 파일이 없거나, image file 로딩이 실패하면 -1을 리턴하고 tty0 에
텍스트(“A N D R O I D”)를 출력한다.
- 로고이미지는 565rle format 이다.
QEMU설정에 따라 CMDLINE 변경

Qemu 환경이라면 이에 맞는 셋팅을 해준다.
[code]
if (qemu[0])
     import_kernel_cmdline(1);
import_kernel_cmdline(1) 인자가 1이면 qemu 관련 환경이고, 내부에서
property_set() 을 하게된다. Property 는 'ro.kernel'이라는 접두어를 붙여
property를 생성한다




Property 관련 함수
property_init()                    Property_area 로 사용할 공유메모리 공간을 생성한다

property_get() /property_set()
start_property_service()           property_service() 를 시작해야 하는 서비스 내용을 소
                                   켓을 이용하여 시스템에 알린다.
property service(handle_property   위에서 써진 소켓 내용을 통해 서비스를 시작한다.
_set_fd())
추가 Property 설정
                                                                         Struct Property info
각 커널 커맨드에 대한 중요 옵션들을 property로 만든다.
                                                                                name
[code]                                                                           serial

if (!strcmp(bootmode,"factory"))                                                value
   property_set("ro.factorytest", "1");                                     ro.factorytest
else if (!strcmp(bootmode,"factory2"))
   property_set("ro.factorytest", "2");                                            1

else property_set("ro.factorytest", "0");                                   Ro.hardware
property_set("ro.serialno", serialno[0] ? serialno : "");
property_set("ro.bootmode", bootmode[0] ? bootmode : "unknown");              goldfish

property_set("ro.baseband", baseband[0] ? baseband : "unknown");             Ro.revision
property_set("ro.carrier", carrier[0] ? carrier : "unknown");
                                                                               xxxxxxx
property_set("ro.bootloader", bootloader[0] ? bootloader : "unknown");
property_set("ro.hardware", hardware);
snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
property_set("ro.revision", tmp);
Property_area에 property_info 구조체의 형태로
Property_area를 채운다.
Init 분석 ( 4 )
                                •전체 action list에서 'init'에 해당하는 action에 관계되는 커맨드 내용을 뽑아내
action_for_each_trigger("init   ACTION queue에 저장

 “,action_add_queue_tail);

                                •ACTION queue에 저장된 커맨드를 순차적으로 실행한다.

   drain_action_queue();


                                •기타 property 파일 및 /data/property 디렉토리에 저장된 persistent property
property_set_fd = start_pro     들을 시스템 property 영역에 로드한다. 그리고 property service를 위한 서버용

      perty_service();          unix domain socket을 생성하고, 리턴한다.




                                •sockpair 시스템 콜을 이용해 서로 연결된 unix domain socket 쌍을 생성한다
시그널 처리를 위한 소켓 생
             성

                                •device_fd , property_set_fd, signal_recv_fd 값이 모두 0 보다 커야 한다.

  부팅에 필요한 FD 확인                 •그 외의 경우는 ERROR()호출 후 return 1
“Init” action 실행
-Action리스트에서 name이 “init”인 노드를 ACTION queue에 추가한다.
-추가한 action queue에 있는 노드를 실행시킨다.
[code]
action_for_each_trigger("init", action_add_queue_tail);
drain_action_queue();
Init.rc. “on init “ 섹션에서 정의한 Action이 시작되는 시점이고, 다음과 같은 내
용의 명령어를 수행한다.
-   글로벌 변수 초기화
-   mount point 생성 /sdcard, /system, /data, /cashe
-   MTD 파티션 마운드 (기본적으로 yaffs2 사용)
-   create basic filesystem structure



    basic filesystem structure            /



                /dev    /proc    /sys   /sdcard   /system   /data   /cashe
start_property_service()
property_set_fd = start_property_service();
- 기타 property 파일 및 /data/property 디렉토리에 저장된 persistent property들
을 시스템 property 영역에 로드한다.
- property service를 위한 서버용 unix domain socket을 생성하고, 리턴한다.
- 이후에 property 를 변경할 경우 여기서 생성한 unix domain socket을 이용한다.
/data/property/file 은 안드로이드의 로케일 설정 파일이 있다.
persist.sys.country    persist.sys.language
persist.sys.localevar  persist.sys.timezone
start_property_service() 함수에서 참조하는 property fils list
/system/build.prop
/system/default.prop
/data/local.prop
시그널 처리를 위한 소켓 생성
서로 연결된 unix domain socket 쌍을 생성한다. S[0], S[1]
[code]
if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) {
signal_fd = s[0];
signal_recv_fd = s[1];
fcntl(s[0], F_SETFD, FD_CLOEXEC);
fcntl(s[0], F_SETFL, O_NONBLOCK);
fcntl(s[1], F_SETFD, FD_CLOEXEC);
fcntl(s[1], F_SETFL, O_NONBLOCK);
}
- sockpair 시스템 콜은 서로 연결된 unix domain socket 쌍을 생성한다. 생성된
소켓 쌍은 서로 직접 연결되어 있어 바인딩이 필요 없으며. 4번째 인자에 저장된다.
S[] 는 init이 SIGCHLD시그널 핸들러에서 발생한 시그널번호를 수신하는 소켓이다.
부팅에 필요한 FD 확인
device_fd, property_set_fd, signal_recv_fd가 정상적이어야만 부팅이 진행된다.

if ((device_fd < 0) || (property_set_fd < 0) || (signal_recv_fd < 0)) {
 ERROR("init startup failuren");
return 1; }
ERROR() 함수는 __kmsg__ 에 Level =3 으로 로그를 남기는 함수이다.

                                               stdin
Init이 관리
 하는 FD
                           Fd[0]              stdout
                           Fd[1]
                                              stderr
                           Fd[2]
                                             __null__
                           Fd[x]
                                              __kmsg__
       Init                Fd[x]
                           Fd[x]                 Uevent Socket             device_fd
                           Fd[x]              Unix domain Socket Property_set_fd
                           Fd[x]              Unix domain Socket Signal_recv_fd
                           Fd[…]
                                                 /dev/keychord            keychord_fd
Init 분석 ( 5 )

                               • action_for_each_trigger("early-boot", action_add_queue_tail);
‘early-boot'와 'boot' action에   • action_for_each_trigger("boot", action_add_queue_tail);
   관계되는 커맨드 실행                 • drain_action_queue();




queue_all_property_triggers(); • 아직까지 셋팅 되지 않은 property 를
                                 queue에 추가하고 실행한다.
   drain_action_queue();

                               • ufds[0] : uevent 메시지 체크
init 프로세스가 poll함수로 감           • ufds[1] : property set관련 Unix Domain 소켓 메시지 체크

 시할 파일디스크립터 설정                 • ufds[2] : SIGCHLD 시그널 발생 체크
                               • ufds[3] : keychord 발생 체크



                               • ufds에서 정의한 파일 디스크립터들의 입력을 감시한다
       무한 루프 문                 • 4개의 FD에서 POLLIN이 뜨면 해당 이벤트 처리를 한다.
action_for_each_trigger(“boot“,action_add_queue_tail)

Action list에서 name이 'early-boot'와 'boot‘ 인 커맨드 내용을 뽑아내
ACTION queue에 추가한 후, 각 커맨드를 실행한다
[code]
action_for_each_trigger("early-boot", action_add_queue_tail);
action_for_each_trigger("boot", action_add_queue_tail);
drain_action_queue();

queue_all_property_triggers();
drain_action_queue();
Init.rc. 내용에서 보았듯이 early-boot의 내용은 없었기 때문에 이부분은 실행
되는 것이 없을 것이다. Boot 섹션은 다음과 같은 일을 한다.
- network init
- System Server and daemons 의 Permissions 설정
- 각 APP group 메모리 사용 설정
queue_all_property_triggers() 함수에서 아직까지 셋팅 되지 않은 property 를
queue에 추가하고 실행한다.
Ufds[ ] 파일디스크립터 설정
init 프로세스가 poll함수(I/O multiplexing)로 감시할 파일 디스크립터 설정
[code]
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;        ufds[2].events = POLLIN;
fd_count = 3;
ufds[3].fd = keychord_fd;           ufds[3].events = POLLIN;
fd_count++;
ufds[0]   :   uevent 메시지 체크
ufds[1]   :   property set관련 Unix Domain 소켓 메시지 체크
ufds[2]   :   SIGCHLD 시그널 발생 체크
ufds[3]   :   keychord 발생 체크


                                    [0] fd=device_fd event = POLLIN
                          Ufds[ ]
                                    [1] fd=property_set_fd event = POLLIN
               Init
                                    [2] fd=signal_recv_fd event = POLLIN
                                    [3] fd=keychord_fd event = POLLIN
무한 루프 문
Loop를 돌면서 action queue 에 처리해야 하는 action이 있다면 실행하고,
restart 해야 하는 prosess가 있으면 재시작 해준다.
Poll() 함수를 이용하여 ufds에서 정의한 파일 디스크립터들의 입력을 감시한다
[code]
for (i = 0; i < fd_count; i++)
       ufds[i].revents = 0;
drain_action_queue(); restart_processes();
nr = poll(ufds, fd_count, timeout);
if (nr <= 0)
      continue;
if (ufds[2].revents == 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);
if (ufds[3].revents == POLLIN) handle_keychord(keychord_fd);
마름모 안에 텍스트는
drain_action_queue();                        “ufds[x].revents == POLLIN ?”
 restart_processes();
                                             대신 “dfds[x]”로 표현

                    Nr > 0    no
      nr = poll(
        ufds);

               yes
                             yes
      ufds[0]                      wait_for_one_process(0)

                   no
                             yes   handle_device_fd(device_fd)
      ufds[1]


                   no
                             yes
      ufds[2]                      handle_property_set_fd(property_set_fd)

                   no
                             yes   handle_keychord(keychord_fd);
      ufds[3]
Ufds[ ] handle action
wait_for_one_process(0)
SIGCHID 시그널에 발생 했다는 것은 child process가 종료 했다는 것을 의미한
다. 따라서 이 함수에서는 process 종료 시에 parent 가 처리해야 하는 내용이 있
다. Wait() 하고, 종료된 process 속성 값에 따라 재 시작 해줘야 하는지 확인하고
옵션에 따라 기능을 수행한다.
handle_device_fd(device_fd)
Device_fd에 Uevent가 발생한것은 디바이스 드라이버에서 핫 플러그 등의 디바
이스 관련한 이벤트가 발생할 경우이다. 따라서 init procrss는 이런 디바이스 메
시지를 처리해야한다. 이 루틴은 앞서서 devicd_init()에서 이미 호출된 적이 있다.
- uevent는 커널에서 유저 프로세스에게 디바이스 관련 메시지를 전달하는
netlink socket의 한 종류이다.
handle_property_set_fd(property_set_fd);
Property가 변경되어야 할때, 소켓을 통해 전달된 정보를 바탕으로 권한 체크를
하고, 문제가 없다면 property_set()을 한다.
handle_keychord(keychord_fd);
Keychord 정보를 읽고 나서, 읽어온 keychord 에 매칭되는 서비스가 있으면 실
행한다.

Más contenido relacionado

Destacado

Econ635 uk pres
Econ635 uk presEcon635 uk pres
Econ635 uk presliangtfm
 
Anixter Overview
Anixter OverviewAnixter Overview
Anixter Overviewrrakib
 
CinestudiO Cine Prêmio 2010 - Parte 1/4
CinestudiO Cine Prêmio 2010 - Parte 1/4CinestudiO Cine Prêmio 2010 - Parte 1/4
CinestudiO Cine Prêmio 2010 - Parte 1/4blog Cinestudio
 
Unit 2. extra practice
Unit 2. extra practiceUnit 2. extra practice
Unit 2. extra practiceSonia
 
Com Ensenyar Llengua A Xinesos Xiv Tallers
Com Ensenyar Llengua A Xinesos Xiv TallersCom Ensenyar Llengua A Xinesos Xiv Tallers
Com Ensenyar Llengua A Xinesos Xiv TallersArnau Cerdà
 
Econ775PP1
Econ775PP1Econ775PP1
Econ775PP1liangtfm
 
Jenkins実践入門のnext step
Jenkins実践入門のnext stepJenkins実践入門のnext step
Jenkins実践入門のnext stepikikko
 
Men of galilee why do you stand looking into heaven
Men of galilee why do you stand looking into heavenMen of galilee why do you stand looking into heaven
Men of galilee why do you stand looking into heavenPedro Morales
 
Tuotan-Olen olemassa. Sisältöä sosiaaliseen mediaan
Tuotan-Olen olemassa. Sisältöä sosiaaliseen mediaanTuotan-Olen olemassa. Sisältöä sosiaaliseen mediaan
Tuotan-Olen olemassa. Sisältöä sosiaaliseen mediaanPasi Siltakorpi
 
Future Uses Of Computer
Future Uses Of ComputerFuture Uses Of Computer
Future Uses Of Computermsanchezg001
 

Destacado (17)

Communication Protocols (UART, SPI,I2C)
Communication Protocols (UART, SPI,I2C)Communication Protocols (UART, SPI,I2C)
Communication Protocols (UART, SPI,I2C)
 
Econ635 uk pres
Econ635 uk presEcon635 uk pres
Econ635 uk pres
 
Anixter Overview
Anixter OverviewAnixter Overview
Anixter Overview
 
Leathersich2010
Leathersich2010Leathersich2010
Leathersich2010
 
CinestudiO Cine Prêmio 2010 - Parte 1/4
CinestudiO Cine Prêmio 2010 - Parte 1/4CinestudiO Cine Prêmio 2010 - Parte 1/4
CinestudiO Cine Prêmio 2010 - Parte 1/4
 
Tto tbc nejm
Tto tbc nejmTto tbc nejm
Tto tbc nejm
 
Google Plus Guida Completa
Google Plus Guida CompletaGoogle Plus Guida Completa
Google Plus Guida Completa
 
Unit 2. extra practice
Unit 2. extra practiceUnit 2. extra practice
Unit 2. extra practice
 
Com Ensenyar Llengua A Xinesos Xiv Tallers
Com Ensenyar Llengua A Xinesos Xiv TallersCom Ensenyar Llengua A Xinesos Xiv Tallers
Com Ensenyar Llengua A Xinesos Xiv Tallers
 
Econ775PP1
Econ775PP1Econ775PP1
Econ775PP1
 
Jenkins実践入門のnext step
Jenkins実践入門のnext stepJenkins実践入門のnext step
Jenkins実践入門のnext step
 
Men of galilee why do you stand looking into heaven
Men of galilee why do you stand looking into heavenMen of galilee why do you stand looking into heaven
Men of galilee why do you stand looking into heaven
 
Tuotan-Olen olemassa. Sisältöä sosiaaliseen mediaan
Tuotan-Olen olemassa. Sisältöä sosiaaliseen mediaanTuotan-Olen olemassa. Sisältöä sosiaaliseen mediaan
Tuotan-Olen olemassa. Sisältöä sosiaaliseen mediaan
 
Future Uses Of Computer
Future Uses Of ComputerFuture Uses Of Computer
Future Uses Of Computer
 
Fazail-e-misvak
Fazail-e-misvakFazail-e-misvak
Fazail-e-misvak
 
Hydrogen Progress, Priorities and Next Steps (Sept. 15)
Hydrogen Progress, Priorities and Next Steps (Sept. 15)Hydrogen Progress, Priorities and Next Steps (Sept. 15)
Hydrogen Progress, Priorities and Next Steps (Sept. 15)
 
Ruins of Detroit
Ruins of DetroitRuins of Detroit
Ruins of Detroit
 

Similar a About Init In Android By Andstudy

리눅스 드라이버 실습 #1
리눅스 드라이버 실습 #1리눅스 드라이버 실습 #1
리눅스 드라이버 실습 #1Sangho Park
 
이산수학 C1 프로젝트 7
이산수학 C1 프로젝트 7이산수학 C1 프로젝트 7
이산수학 C1 프로젝트 7pkok15
 
Linux Kernel Boot Process , SOSCON 2015, By Mario Cho
Linux Kernel Boot Process , SOSCON 2015, By Mario ChoLinux Kernel Boot Process , SOSCON 2015, By Mario Cho
Linux Kernel Boot Process , SOSCON 2015, By Mario ChoMario Cho
 
mago3d workshop in FOSS4G Korea 2018
mago3d workshop in FOSS4G Korea 2018mago3d workshop in FOSS4G Korea 2018
mago3d workshop in FOSS4G Korea 2018Gaia3D,Inc.
 
Linux 강의자료 ed10
Linux 강의자료 ed10Linux 강의자료 ed10
Linux 강의자료 ed10hungrok
 
Linux ut-broker-install
Linux ut-broker-installLinux ut-broker-install
Linux ut-broker-installJUNHEEKIM27
 
리눅스 커널 기초 태스크관리
리눅스 커널 기초 태스크관리리눅스 커널 기초 태스크관리
리눅스 커널 기초 태스크관리Seungyong Lee
 

Similar a About Init In Android By Andstudy (10)

리눅스 드라이버 실습 #1
리눅스 드라이버 실습 #1리눅스 드라이버 실습 #1
리눅스 드라이버 실습 #1
 
이산수학07
이산수학07이산수학07
이산수학07
 
이산수학 C1 프로젝트 7
이산수학 C1 프로젝트 7이산수학 C1 프로젝트 7
이산수학 C1 프로젝트 7
 
Linux Kernel Boot Process , SOSCON 2015, By Mario Cho
Linux Kernel Boot Process , SOSCON 2015, By Mario ChoLinux Kernel Boot Process , SOSCON 2015, By Mario Cho
Linux Kernel Boot Process , SOSCON 2015, By Mario Cho
 
mago3d workshop in FOSS4G Korea 2018
mago3d workshop in FOSS4G Korea 2018mago3d workshop in FOSS4G Korea 2018
mago3d workshop in FOSS4G Korea 2018
 
Linux 강의자료 ed10
Linux 강의자료 ed10Linux 강의자료 ed10
Linux 강의자료 ed10
 
Device driver
Device driverDevice driver
Device driver
 
Linux ut-broker-install
Linux ut-broker-installLinux ut-broker-install
Linux ut-broker-install
 
리눅스 커널 기초 태스크관리
리눅스 커널 기초 태스크관리리눅스 커널 기초 태스크관리
리눅스 커널 기초 태스크관리
 
Init to systemd
Init to systemdInit to systemd
Init to systemd
 

About Init In Android By Andstudy

  • 1. Init process in Android Created by Andstudy Andstudy Seminar 2009 08 22 김 연찬
  • 2. Android Study 2009 08 15 Android Study members 송형주 전형민 박지훈 황세희 이 백 전유진 김연찬 전승원 강명훈 임기영 박은병 이덕용 박은병 박주애 이덕용 구자관 윤동렬 김신수 김태연 Spring note : http://andstudy.springnote.com/ Study web page Android Pub: http://www.androidpub.com/devstudy_groupb 이 문서는 다음의 CCL (creative commons license) 을 따름니다. http://creativecommons.org/licenses/by-nc-sa/2.0/kr/ 저작자표시-비영리-동일조건변경허락 2.0 대한민국
  • 3. Change LOG Change history changes editor 2009년 08월 22일 최초작성 김 연찬 2009년 09월 05일 1차 교정 (PDF 배포용으로 수정) 김 연찬
  • 4. About Init. Init Process 는 PID 가 1인 프로세스이고 부팅과정에서 커널이 생성하 는 첫번째 프로세스이다. 그럼 Init Process는 언제 실행되는가? start_kernel() -> rest_init() -> kernel_thread() -> kernel_init() -> in it_post() 에서 "/init"을 수행 안드로이드 소스트리에서 Init process 코드는 어디에? /system/core/init/init.c 이 문서에서 다루는 부분의 코드는 /system/core/init/ 에서 찾아 볼 수 있다. Init.rc 의 경우는 /system/core/rootdir/init.rc 에 존재한다.
  • 5. Android Init의 기능 SIGCHLD Device 초 RC 파일 Property signal 기화 & 내용처리 설정 처리 관리
  • 6. Init Process (1) Kernal Init post() Init - 자식프로세스 처리를 위한 S main IGCHLD SIGNAL handler등록 act.sa_handler = sigchld_handler; sigaction(SIGCHLD, &act, 0); Sigchild handler() Zombi process mkdir("/dev", 0755); mkdir("/proc", 0755); / Make Special purpose mkdir("/sys", 0755); Device node files tmpfs mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755") /dev /proc /sys mkdir("/dev/pts", 0755); mkdir("/dev/socket", 0755); xxxxxx mount("devpts", "/dev/pts", "devpts", 0, NULL); Socket pts mount("proc", "/proc", "proc", 0, NULL); mount("sysfs", "/sys", "sysfs", 0, NULL); Make Special purpose open_devnull_stdio(); Device node files Fd[0] stdin Fd[0] stdin Fd[1] Fd[1] stdout stdout Fd[2] Fd[2] stderr stderr Fd[3] Fd[3] __null__ __null__ Fd[…] Fd[…] parent Fd[0] stdin log_init(); Fd[1] stdout Make __kmsg__ device file log Ex) FD_CLOEXEC Fd[2] stderr massage will be written to mknode(“/dev/__kmsg__”); Fd[3] a.txt “/dev/__kmsg__” Log_fd = open(“/dev/__kmsg__”); Child Fd[0] &set it as “FD_CLOEXEC” Fd[1] Fcntl(log_fd, F_SETFD, FD_CLOEXEC); Fd[2] Fd[3]
  • 7. Init Process (2) Service_list struct service struct service struct service Init.rc 파일을 Parsing 하여 Listnode slist Slist Slist Service_list 와 Action_list를 구 Char *name “console “ “Servicemanager” parse_config_file("/init.rc"); Char *classname “default” “default” 성한다. Int nargs 0 0 Struct Action onrestart “onrestart” “onrestart” Action_list Action_list Action_list Action_list Listnode alist listnode listnode Char *name “init” “boot” Struct listnode command Listnode command Listnode command Struct command *curtent QEMU란? Qemu 에서 사용하는 메모리 qemu_init(); 영역 초기화 커널 커맨드 라인(/proc/cmdli import_kernel_cmdline(0); Argument is ‘0’ : physical H/W ne )을 읽어서 필요한 내용을 Argument is ‘1’ : QEMU emulator 전역 변수에 저장한다 커널로 부터 H/W 정보를 얻어 get_hardware_name(); 와서 “init.H/W_name.rc 파일 default h/w configuration is goldfish. snprintf(tmp, sizeof(tmp),"/init.%s.rc",hardware) 을 Parsing 하여 Service_list parse_config_file(tmp); ->/system/core/rootdir/etc/init.goldfish.rc 와 Action_list 에 추가한다. Action list 에서 “early-init”이라 action_for_each_trigger("early-init“,action_a Init.rc 와 init.goldfish.rc 에 “early-init” 의 는 name의 노드를 dd_queue_tail); name의 항목이 없다. 따라서 여기서 하는 Action_queue에 삽입하고, drain_action_queue(); 일은 없다. Action_queue에 있는 노드를 실 행 시킨다.
  • 8. Init Process (3) /dev 이하에 장치 파일을 device_fd = device_init(); /dev 이하에 장치 파일을 uevent 파일을 uevent 파일을 이용하여 생성 이용하여 생성하고 접근권한을 설정한다. 하고 접근권한을 설정한다. Property_area memory map ashmem_create_region(/dev/ property_init(); ashmem) 을 사용하여 공유 메모리 공간을 생성한다. debuggable = property_get("ro.debuggable"); keychords 확인 if (debuggable && !strcmp(debuggable, "1")) Consols 확인 keychord_fd = open_keychord(); fd = open(console_name, O_RDWR); if (fd >= 0) have_console = 1; close(fd); if( load_565rle_image(INIT_IMAGE_FILE) ) { 부팅 이미지 출력 fd = open("/dev/tty0", O_WRONLY); 로고 파일이 있으면 로고를 출력하고, if (fd >= 0) { 로고 파일이 없거나 로딩에 실패하면 tty0 const char *msg; msg = "n ANDROID“; 에 “ANDROID” 문자열을 출력한다. write(fd, msg, strlen(msg)); close(fd); } Qemu 설정일 때, property에 if (qemu[0]) 'ro.kernel'이라는 접두어를 붙 여 property를 set 한다. import_kernel_cmdline(1);
  • 9. Init Process (4) if (!strcmp(bootmode,"factory")) Struct Property info property_set("ro.factorytest", "1"); Ro.bootmode 추가 Property 설정 else if (!strcmp(bootmode,"factory2")) name property_set("ro.factorytest", "2"); else property_set("ro.factorytest", "0"); serial Bootmode[0] property_set("ro.serialno", serialno[0] ? serialno : ""); value property_set("ro.bootmode", bootmode[0] ? bootmode : "unknown"); Ro.hardware property_set("ro.baseband", baseband[0] ? baseband : "u nknown"); property_set("ro.carrier", carrier[0] ? carrier : "unknown"); goldfish property_set("ro.bootloader", bootloader[0] ? bootloader : "unknown"); Ro.revision property_set("ro.hardware", hardware); snprintf(tmp, PROP_VALUE_MAX, "%d", revision); property_set("ro.revision", tmp); xxxxxxx Action list 에서 “init”이라는 action_for_each_trigger("init“,action_add_q name의 노드를 Action_queue에 ueue_tail); / 삽입하고, Action_queue에 있는 drain_action_queue(); 노드를 실행 시킨다. create basic FileSystem structure /dev /proc /sys /sdcard /system /data /cache 기타 property 파일 및 property_set_fd = start_property_service(); default 로 생성한 이미지의 /data/property 에 /data/property 경로에 저장된 는 4개의 로케일 셋팅 정보 파일이 있다. persistent property들을 시스템 persist.sys.country persist.sys.language property 영역에 로드 한다 persist.sys.localevar persist.sys.timezone if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) 시그널 처리를 위한 소켓 생성 signal_fd = s[0]; signal_recv_fd = s[1]; Init이 관리 하는 FD fcntl(s[0], F_SETFD, FD_CLOEXEC); fcntl(s[0], F_SETFL, O_NONBLOCK); fcntl(s[1], F_SETFD, FD_CLOEXEC); fcntl(s[1], F_SETFL, O_NONBLOCK); } if ((device_fd < 0) || (property_set_fd < 0) | 부팅에 필요한 FD 확인 | (signal_recv_fd < 0)) { ERROR("init startup failuren"); return 1; }
  • 10. Init Process (5) Action list 에서 “early-boot”와 action_for_each_trigger("early-boot", - network init “boot”이라는 name의 노드를 action_add_queue_tail); - System Server and daemons 의 Permissions 설정 Action_queue에 삽입하고, action_for_each_trigger("boot", - 각 APP group 메모리 사용 설정 Action_queue에 있는 노드를 action_add_queue_tail); 실행 시킨다. drain_action_queue(); action_list 에 있는 노드중에 Action_list Action_list Action_list node.name 이 "property" 인 노 queue_all_property_triggers(); listnode Listnode alist 드들을 acttion queue 에 추가한 drain_action_queue(); Char *name “property” Struct listnode Listnode 다. command command Action_queue에 있는 노드를 실 Struct command 행 시킨다. *curtent ufds[0].fd = device_fd; init 프로세스가 poll함수로 감시 ufds[0].events = POLLIN; Ufds[ ] [0] fd=device_fd event = POLLIN 할 파일디스크립터 설정 ufds[1].fd = property_set_fd; ufds[1].events = POLLIN; [1] fd=property_set_fd event = POLLIN Init ufds[2].fd = signal_recv_fd; [2] fd=signal_recv_fd event = POLLIN ufds[2].events = POLLIN; ufds[3].fd = keychord_fd; [3] fd=keychord_fd event = POLLIN ufds[3].events = POLLIN; 무한 루프 문에서 action For(;;;) queue에 실행할 action이 있 으면 실행하고 재시작이 필요 drain_action_queue(); 한 프로세스가 있으면 재시작 restart_processes(); 해준다. ufds 를 감시하고 POLLIN 이 발생하면 해당 핸 들러를 통해 처리 한다. while (!wait_for_one_process(0)) handle_device_fd(device_fd); 해당 fd의 POLLIN 에 따라 4가 handle_property_set_fd(property_set_fd); 지 핸들러를 호출한다. handle_keychord(keychord_fd);
  • 11. Init.c 분석 (1) • File descriptor, Signal 구조체, Polling 을 위한 구조체 등 Init이 사용할 자료구조 선언 • int device_fd = -1; struct sigaction act; struct pollfd ufds[4]; • 자식프로세스 처리를 위한 SIGCHLD SIGNAL handler등록 SIGCHLD Handler등록 • /dev, /proc, /sys 디렉토리를 각각 생성 디렉토리를 생성 및 마운트 • tmpfs, devpts, proc, sysfs 마운트 • stdin, stdout, stderr File descriptor 를 문자 디바이스 파일 open_devnull_stdio(); /dev/__null__ 의 FD 로 연결한다. • /dev/__kmsg__ 디바이스 파일을 생성하고, 파일 디스크립터 log_init(); 를 log_fd에 저장 • /dev/__kmsg__에 log 내용을 기록 INFO("reading config filen");
  • 12. SIGCHILD SIGNAL SIGCHILD Signal - 자식 프로세스가 멈추거나 종료하거나 추적당하는 경우 부모프로세스가 받 는 시그널 Init process 는 왜 SIGCHILD 시그널을 사용할까? - 리눅스에서 자식프로세스 보다 부모 프로세스가 먼저 죽는다면, 자식프로세 스의 부모를 init process로 만들어준다. 그래서 고아 프로세스가 죽을 때 처리 해야 하는 일을 init process가 대신하게된다. 따라서 init process는SIGCHILD Signal 를 처리 해야만 한다. - init process 의 자식프로세스 중에 프로세스가 종료하고 재 시작 해야 하는 프로세스가 있다면, 재 시작 관련 설정을 해줘야 한다. (ex. Restart 옵션으로 시 작된 Service) SA_NOCLDSTOP flag: 만약 SIGCHLD의 시그널 핸들러일 경우 자식 프 로세스의 상태가 stop일 경우는 SIGCHLD 시그널이 발생 안됨.
  • 13. 디렉토리를 생성 및 마운트 / /dev /proc /SYS [tmpfs] [proc] [sysfs] /pts /socket [devpts] File System 설명 Tmpfs tmpfs는 램파일시스템의 일종(주로 성능 향상 목적) Devpts devpts는 가상 터미널을 위한 파일시스템 Proc Proc fs는 커널메모리에서 돌아가는 일종의 가상 파일시 스템 Sysfs sysfs 파일 시스템은 proc, devfs, devpts 파일 시스템을 하나로 통합한 파일 시스템 (Linux Kernel 2.6 에서 도입)
  • 14. open_devnull_stdio() static const char *name = "/dev/__null__"; if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) { 주번호 1 부번호 3 fd = open(name, O_RDWR); unlink(name); if (fd >= 0) { dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); if (fd > 2) { close(fd); } 이후로는 stdio 가 null dev로 설정되어 init process가 어떠한 메시지 를 stdio로 보내더라도 null dev로 전달되어 stdio 불가능해진다. Fd[0] stdin Fd[0] stdin Fd[1] stdout Fd[1] stdout Fd[2] Fd[2] stderr stderr Fd[3] Fd[3] __null__ __null__ Fd[…] Fd[…]
  • 15. log_init() void log_init(void) static const char *name = "/dev/__kmsg__"; if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) { 주번호 1 부번호 11 log_fd = open(name, O_WRONLY); fcntl(log_fd, F_SETFD, FD_CLOEXEC); unlink(name); /dev/__kmsg__ 디바이스 파일을 생성하고, 파일 디스크립터를 log_fd에 저장 FD_CLOEXEC fd의 close-on-exec 플래그를 arg의 FD_CLOEXEC 비트에 의해 지정된 값으로 설정한다. close-on-exec 보통 프로세스에서 exec를 시켜서 새로운 프로세스를 실행시키면 새로운 프로세스는 기존의 프로세스의 이미지를 덮어쓰게 된다. 그러면서 기존 프로세스가 열었던 파일 지정자를 그대로 넘겨주게 된다. 그러나 기존 프로세스가 열었던 파일 디스크립터의 close- on-exec가 set됐을 경우 해당 파일은 새로운 프로세스로는 상속이 되지 않는다. Init 에서 close-on- /dev/__kmsg__는 init이 unlink를 했기 때문에 파일의 데 exec 의 의미 이터는 남아있지만 접근할 이름이 없어졌다. 그리고 close-on-exec를 set 함으로써 fork를 통해서도 파일 디스 크립터는 상속이 되지 않는다. 따라서 /dev/__kmsg__는 Init process 만 접근 가능하다.
  • 16. close-on-exec flag close-on-exec = unSET close-on-exec = SET Parent 가 “a.txt”를 open() 후에 Fork()를 이용하여 child를 생성할 때 fd 값의 차이. Fd[0] stdin Fd[0] stdin parent parent Fd[1] stdout Fd[1] stdout Fd[2] stderr Fd[2] stderr Fd[3] a.txt Fd[3] a.txt Fd[0] Fd[0] Child Child Fd[1] Fd[1] Fd[2] Fd[2] Fd[3] Fd[3]
  • 17. Init.c 분석 (2) • init.rc 파일을 파싱해서 각 action, service 섹션별로 연 parse_config_file("/init.rc"); 결 리스트를 생성한다 • 안드로이드의 에뮬레이터로 사용되는 QEMU와 관련된 qemu_init(); 접근 권한 관련 변수 초기화 • 커널 커맨드 라인을 읽어서 필요한 내용을 전역 변수에 import_kernel_cmdline(0); 저장한다. • 커널로부터 CPU정보를 읽어와 hardware와 revision 정 get_hardware_name(); 보를저장 • hardware에 해당하는 init.rc 파일을 추가적으로 파싱 parse_config_file(tmp); • snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
  • 18. parse_config_file("/init.rc“) INIT.RC 내용 (/system/core/rootdir/init.rc) Init.rc 파일을 Parsing 하여 Service_list 와 Action_list를 구성한다. RC file ? [runtime configuration files] runtime시에 환경설정을 할 수 있도록 설정내용을 정의한 파일 on init - 글로벌 변수 초기화 [환경 설정] - mount point 생성 /sdcard, /system, /data, /cache -MTD 파티션 마운드 (기본적으로 yaffs2 사용) - create basic filesystem structure on boot - network init [boot action 정의] - System Server and daemons 의 Permissions 설정 - 각 APP group 메모리 사용 설정 class_start default - service 정의 형식 [서비스 시작] - service <name> <pathname> [ <argument> ]* <option> <option> Service_list struct service struct service struct service Listnode slist Slist Slist Char *name “console “ “Servicemanager” Char *classname “default” “default” Int nargs 0 0 Stuct Action onrestart “onrestart” “onrestart” Action_list Action_list Action_list Action_list Listnode alist listnode listnode Char *name “init” “boot” Struct listnode command Listnode command Listnode command CMD CMD CMD Struct command *curtent CMD CMD CMD
  • 19. APP group & 메모리 설정 APP group ADJ Define the mem value ory thresholds [4k pages ] FOREGROUND_APP 0 1536 [6M] 전면에 있는 프로그램 VISIBLE_APP 1 2048 [8M] 화면에 보이지만 실행되지 않는 APP SECONDARY_SERVER 2 4096 [16M] Service Demon HOME_APP 4 4096 [16M] 시작화면에 등록되는 APP HIDDEN_APP_MIN 7 5120 [20M] 최소화 시킨 APP CONTENT_PROVIDER 14 5632 [22M] CONTENT_PROVIDER EMPTY_APP 15 6144 [24M] About ADJ Value ? - Define the oom_adj values for the classes of processes that can be killed by the kernel - OOM Killer 가 동작해야 할때 계산되는 score 값에 영향을 미치는 값 - 숫자가 높을 수록 OOM killer에 의해 죽을 가능성이 높다. - OOM_killer에 절대 죽지 않는 ADJ value 는 -17 이다. (ex init process)
  • 20. Service Start [service 정의 형식] service <name> <pathname> [ <argument> ]* <option> <option> [code] service servicemanager /system/bin/servicemanager user system critical onrestart restart zygote onrestart restart media - servicemanager 는 /system/bin/servicemanager 경로에 존재 - critcal 옵션 [ 4분 안에 4번의 오류가 발생 한다면 reboot 하겠다는 안드로이드의 시 스템 운영정책] - onrestart 옵션 -> servicemanager 가 재시작되면 zygoto & media 도 재시작해라 라 는 의미 -ini.rc 에서 시작하는 서비스 리스트 Console adbd vold servicemanager ril-daemon zygote media bootsound dbus hcid hfag hsag installd flash_recovery ※ Init.rc 에 대한 보다 많은 정보는 /system/core/init/readme.txt 문서 참조
  • 21. qemu_init() Func 기능 : qemu_perms 영역을 memset() 을 이용하여 초기화 한다. What is QEMU & Goldfish? Google은 app 개발자를 위해 SDK 배포시 emulator 를 포함시켰고, 이 emulator 에서 동작하도록 하기위한 가상의 device configuration을 goldfish 라 합니다. 그리고 이 emulator 를 QEMU라 합니다. Applications APP Framework Android Run- Time Libraries QEMU Linux Kernel ARM core Goldfish Emulator or Hardware Simulator Hardware Simulator Network
  • 22. import_kernel_cmdline(0) 커널 커맨드 라인(/proc/cmdline )을 읽어서 필요한 내용을 전역 변수에 저장한다 “0”을 인자로 넘길 경우는 실제 타겟을 위한 몇몇 커맨드 라인의 내용이 init 프로세스의 전역변수에 저장 “1”을 인자로 넘길 경우는 QEMU 에뮬레이터를 위해 모든 커맨드 라인의 내 용을 "ro.kernel" 접두어를 붙여서 property 값을 set한다.(unix domain socket 이용)
  • 23. Init.H/W_name.rc 파싱 커널(proc 파일시스템)로부터 CPU정보를 읽어와 hardware와 revision 정보를 전역변수 에 저장하고 이를 이용하여 hardware 관련 rc 파일을 추가적으로 파싱한다. 현재는 default로 hardware가 goldfish로 되어 있기 때문에 init.goldfish.rc 파일이 파싱 된다. " /system/core/rootdir/etc/init.goldfish.rc” [code] get_hardware_name(); snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware); parse_config_file(tmp); Init.rc 파일을 파싱해서 만들어놓은 Service_list 와 Action_list 에 해당 항목이 있다면 추 가한다. Service_list struct service struct service struct service Listnode slist Slist Slist Char *name “console “ “Servicemanager” Char *classname “default” “default” Int nargs 0 0 Steuct Action onrestart “onrestart” “onrestart” Action_list Action_list Action_list Action_list Action_list Listnode alist listnode listnode listnode Char *name “init” “boot” “xxx” Struct listnode command Listnode command Listnode command Listnode command CMD CMD Struct command *curtent CMD CMD CMD CMD CMD CMD CMD CMD
  • 24. Init 분석 (2,3) action_for_each_trigger("early-in • action리스트에서 name이 “early-init”인 노드를 ACTION queue에 추 가한다. it“,action_add_queue_tail); • ACTION queue에 저장된 커맨드를 순차적으로 실행한다. drain_action_queue(); •uevent 파일들에 'add' 명령을 write해서 디바이스 추가 이벤트를 발생 시키고 이렇게 발생된 이벤트는 uevent 소켓을 통해 수신해서 파싱 device_fd = device_init(); • uevent메시지를 수신하기 위해 사용한 소켓을 리턴함. •system property 영역을 생성하고, default property들을 저장함 property_init();
  • 25. Action_queue에 실행할 노드 추가 & 실행 action_for_each_trigger("early-init“,action_add_queue_tail) action리스트에서 name이 “early-init”인 노드를 ACTION queue에 추가한다. Action-list에 name=“early-init”인 노드가 없기 때문에 아무 일도 하지 않는다. (init.rc 와 init.goldfish.rc 에 “early-init” 항목이 없다) init 프로세스에서는 다음과 같은 4개의 boot action을 정의 할수 있다. early-init 정의된 action 없음 init create basic filesystem structure early-boot, 정의된 action 없음 Boot System Server and daemons 의 Permissions 설정 early-init, early-boot, 추후 시스템의 확장을 고려해서 설계한 것이라 추측된다. drain_action_queue() action queue에 있는 내용(커맨드)를 실행한다. “early-init”에서 추가한 내용이 없을것 이므로 여기서 실행되는 내용도 없을 것 이다.
  • 26. device_init() uevent 메시지용 소켓 오픈 하고 3번의 coldboot() 함수를 호출한다. [code] Fd = open_uevent_socket(); coldboot(fd, "/sys/class"); coldboot(fd, "/sys/block"); coldboot(fd, "/sys/devices"); cold boot는 무슨일을 하나? /dev/ 이하 파일들을 init 프로세스에서 생성하는데, 어떠한 디바이스들이 있는지를 확인하기 위해서 netlink socket (device_fd) 를 생성하고, 디바이스 드라이버와 uevent 메세지를 주고 받는다. 과정은 디렉토리 "/sys/class" , "/sys/block", "/sys/devices" 를 각각 검색하여, uevent 파일을 열어서 "add" 를 write 한 후, 응답 하는 uevent 메세지를 수신하고, 해당 디바이스의 노드 생성 및 접근 권한을 설정한 다. 수신된 uevent구조체의 subsysem과 event가 firmware subsystem 관련이고, path 필드의 정보를 참조하여 /dev 디렉 uevent->acttion 이 'ADD'이면 fork()를 토리 이하의 장치 관련 서브 디렉토리 및 통해 새로운 프로세스를 만들고 irmware 장치 파일을 생성 혹은 삭제하고 접근 권 관련 uevent는 새로운 자식 프로세스에 한을 설정한다. 서 처리한다. (process_firmware_event(uevent);)
  • 27. uevent - LDM(Linux Device Model)에서는 커널 이벤트를 사용자공간으로 전달하기 위한 인터페이스를 제공하고 있다. 이것이 바로 uevent다. - uevent는 커널에서 유저 프로세스에게 디바이스 관련 메시지를 전 달하는 netlink socket의 한 종류이다. netlink socket ? netlink socket은 커널과 유저 영역 사이의 통신(IPC) 방법이다 <커널> <-------( netlink socket ) ------> <유저프로세스> netlink socket 장점 -netlink의 경우는 커널 모듈로 추가 가능. - 다른 IPC에 반해, netlink는 여러 프로세스 그룹으로 멀티캐스트 전송이 가능 - 시스템콜과 ioctl의 경우, 유저 애플리케이션에 의해 시작 가능 , 이에 반해 netlink는 커널에 의해서 시작 가능
  • 28. Unix domain socket Unix domain socket socket Unix Domain 소켓은 같이 동일 PC내의 네트워크 프로그래밍에서 네트워크로 연 프로세스끼리 통신을 하기 위해서 사용 결된 서로 다른 PC간의 통신을 위해 사용 파일명을 가지고 바인딩 ip주소와 포트로 바인딩 sock = socket( PF_FILE, SOCK_DGRAM, 0); sock = socket( PF_INET, SOCK_DGRAM, 0); Unix domain 사용 예 struct sockaddr_un server_addr; memset( &server_addr, 0, sizeof( server_addr)); server_addr.sun_family = AF_UNIX; strcpy( server_addr.sun_path, "/tmp/test_server.dat");
  • 29. property_init() • ashmem_create_region(/dev/ashmem) 을 사용하여 공유 메모리 공 간을 생성한다. (anonymous shared memory)
  • 30. Init 분석 (3,4) •Keychord open 조건을 확인하고 참인 조건이면 open keychords 확인 •keychord_fd = open_keychord(); •console을 open해서 동작 유무를 체크한다. Consols 확인 •정상동작을 한다면 have_console = 1 로 셋팅 •565rle image(로고) 파일을 프레임 버퍼에 로딩한다. 부팅 이미지 출력 • image 파일이 없으면 텍스트 모드로 프레임 버퍼에 “ANDORID” 출력 • if (qemu[0]) import_kernel_cmdline(1); QEMU설정에 따라 CMDLINE •커널 커맨드 라인의 옵션들을 QEMU에서 참조하게끔 'ro.kernel'이라는 접두어 변경 를 붙여 property를 생성한다. •각 커널 커맨드에 대한 중요 옵션들을 property로 만든다. 추가 Property 설정 •Factory mode, ro.bootmode, ro.baseband등등
  • 31. open_keychord(), Consols 확인 Keychord 란? Keychord는 핸드폰에 있는 단축키와 조합키 와 같은 특수키와 조합 키를 지 원하기 위한 구조이다. [code] debuggable = property_get("ro.debuggable"); if (debuggable && !strcmp(debuggable, "1")) keychord_fd = open_keychord(); debuggable 셋팅값에 따라 keychord를 오픈한다. Consols 동작 확인 [code] fd = open(console_name, O_RDWR); if (fd >= 0) have_console = 1; close(fd); Consols 동작 확인을 하고 have_console 변수를 셋 한다.
  • 32. 부팅 이미지 출력 565rle image(로고) 파일을 프레임 버퍼에 로딩한다. [code] if( load_565rle_image(INIT_IMAGE_FILE) ) { fd = open("/dev/tty0", O_WRONLY); if (fd >= 0) { const char *msg; msg = "n" "n“ “ A N D R O I D "; write(fd, msg, strlen(msg)); close(fd); } -/initlogo.rle 파일이 있다면 이미지를 로딩하여 LCD에 출력한다. -/initlogo.rle 파일이 없거나, image file 로딩이 실패하면 -1을 리턴하고 tty0 에 텍스트(“A N D R O I D”)를 출력한다. - 로고이미지는 565rle format 이다.
  • 33. QEMU설정에 따라 CMDLINE 변경 Qemu 환경이라면 이에 맞는 셋팅을 해준다. [code] if (qemu[0]) import_kernel_cmdline(1); import_kernel_cmdline(1) 인자가 1이면 qemu 관련 환경이고, 내부에서 property_set() 을 하게된다. Property 는 'ro.kernel'이라는 접두어를 붙여 property를 생성한다 Property 관련 함수 property_init() Property_area 로 사용할 공유메모리 공간을 생성한다 property_get() /property_set() start_property_service() property_service() 를 시작해야 하는 서비스 내용을 소 켓을 이용하여 시스템에 알린다. property service(handle_property 위에서 써진 소켓 내용을 통해 서비스를 시작한다. _set_fd())
  • 34. 추가 Property 설정 Struct Property info 각 커널 커맨드에 대한 중요 옵션들을 property로 만든다. name [code] serial if (!strcmp(bootmode,"factory")) value property_set("ro.factorytest", "1"); ro.factorytest else if (!strcmp(bootmode,"factory2")) property_set("ro.factorytest", "2"); 1 else property_set("ro.factorytest", "0"); Ro.hardware property_set("ro.serialno", serialno[0] ? serialno : ""); property_set("ro.bootmode", bootmode[0] ? bootmode : "unknown"); goldfish property_set("ro.baseband", baseband[0] ? baseband : "unknown"); Ro.revision property_set("ro.carrier", carrier[0] ? carrier : "unknown"); xxxxxxx property_set("ro.bootloader", bootloader[0] ? bootloader : "unknown"); property_set("ro.hardware", hardware); snprintf(tmp, PROP_VALUE_MAX, "%d", revision); property_set("ro.revision", tmp); Property_area에 property_info 구조체의 형태로 Property_area를 채운다.
  • 35. Init 분석 ( 4 ) •전체 action list에서 'init'에 해당하는 action에 관계되는 커맨드 내용을 뽑아내 action_for_each_trigger("init ACTION queue에 저장 “,action_add_queue_tail); •ACTION queue에 저장된 커맨드를 순차적으로 실행한다. drain_action_queue(); •기타 property 파일 및 /data/property 디렉토리에 저장된 persistent property property_set_fd = start_pro 들을 시스템 property 영역에 로드한다. 그리고 property service를 위한 서버용 perty_service(); unix domain socket을 생성하고, 리턴한다. •sockpair 시스템 콜을 이용해 서로 연결된 unix domain socket 쌍을 생성한다 시그널 처리를 위한 소켓 생 성 •device_fd , property_set_fd, signal_recv_fd 값이 모두 0 보다 커야 한다. 부팅에 필요한 FD 확인 •그 외의 경우는 ERROR()호출 후 return 1
  • 36. “Init” action 실행 -Action리스트에서 name이 “init”인 노드를 ACTION queue에 추가한다. -추가한 action queue에 있는 노드를 실행시킨다. [code] action_for_each_trigger("init", action_add_queue_tail); drain_action_queue(); Init.rc. “on init “ 섹션에서 정의한 Action이 시작되는 시점이고, 다음과 같은 내 용의 명령어를 수행한다. - 글로벌 변수 초기화 - mount point 생성 /sdcard, /system, /data, /cashe - MTD 파티션 마운드 (기본적으로 yaffs2 사용) - create basic filesystem structure basic filesystem structure / /dev /proc /sys /sdcard /system /data /cashe
  • 37. start_property_service() property_set_fd = start_property_service(); - 기타 property 파일 및 /data/property 디렉토리에 저장된 persistent property들 을 시스템 property 영역에 로드한다. - property service를 위한 서버용 unix domain socket을 생성하고, 리턴한다. - 이후에 property 를 변경할 경우 여기서 생성한 unix domain socket을 이용한다. /data/property/file 은 안드로이드의 로케일 설정 파일이 있다. persist.sys.country persist.sys.language persist.sys.localevar persist.sys.timezone start_property_service() 함수에서 참조하는 property fils list /system/build.prop /system/default.prop /data/local.prop
  • 38. 시그널 처리를 위한 소켓 생성 서로 연결된 unix domain socket 쌍을 생성한다. S[0], S[1] [code] if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) { signal_fd = s[0]; signal_recv_fd = s[1]; fcntl(s[0], F_SETFD, FD_CLOEXEC); fcntl(s[0], F_SETFL, O_NONBLOCK); fcntl(s[1], F_SETFD, FD_CLOEXEC); fcntl(s[1], F_SETFL, O_NONBLOCK); } - sockpair 시스템 콜은 서로 연결된 unix domain socket 쌍을 생성한다. 생성된 소켓 쌍은 서로 직접 연결되어 있어 바인딩이 필요 없으며. 4번째 인자에 저장된다. S[] 는 init이 SIGCHLD시그널 핸들러에서 발생한 시그널번호를 수신하는 소켓이다.
  • 39. 부팅에 필요한 FD 확인 device_fd, property_set_fd, signal_recv_fd가 정상적이어야만 부팅이 진행된다. if ((device_fd < 0) || (property_set_fd < 0) || (signal_recv_fd < 0)) { ERROR("init startup failuren"); return 1; } ERROR() 함수는 __kmsg__ 에 Level =3 으로 로그를 남기는 함수이다. stdin Init이 관리 하는 FD Fd[0] stdout Fd[1] stderr Fd[2] __null__ Fd[x] __kmsg__ Init Fd[x] Fd[x] Uevent Socket device_fd Fd[x] Unix domain Socket Property_set_fd Fd[x] Unix domain Socket Signal_recv_fd Fd[…] /dev/keychord keychord_fd
  • 40. Init 분석 ( 5 ) • action_for_each_trigger("early-boot", action_add_queue_tail); ‘early-boot'와 'boot' action에 • action_for_each_trigger("boot", action_add_queue_tail); 관계되는 커맨드 실행 • drain_action_queue(); queue_all_property_triggers(); • 아직까지 셋팅 되지 않은 property 를 queue에 추가하고 실행한다. drain_action_queue(); • ufds[0] : uevent 메시지 체크 init 프로세스가 poll함수로 감 • ufds[1] : property set관련 Unix Domain 소켓 메시지 체크 시할 파일디스크립터 설정 • ufds[2] : SIGCHLD 시그널 발생 체크 • ufds[3] : keychord 발생 체크 • ufds에서 정의한 파일 디스크립터들의 입력을 감시한다 무한 루프 문 • 4개의 FD에서 POLLIN이 뜨면 해당 이벤트 처리를 한다.
  • 41. action_for_each_trigger(“boot“,action_add_queue_tail) Action list에서 name이 'early-boot'와 'boot‘ 인 커맨드 내용을 뽑아내 ACTION queue에 추가한 후, 각 커맨드를 실행한다 [code] action_for_each_trigger("early-boot", action_add_queue_tail); action_for_each_trigger("boot", action_add_queue_tail); drain_action_queue(); queue_all_property_triggers(); drain_action_queue(); Init.rc. 내용에서 보았듯이 early-boot의 내용은 없었기 때문에 이부분은 실행 되는 것이 없을 것이다. Boot 섹션은 다음과 같은 일을 한다. - network init - System Server and daemons 의 Permissions 설정 - 각 APP group 메모리 사용 설정 queue_all_property_triggers() 함수에서 아직까지 셋팅 되지 않은 property 를 queue에 추가하고 실행한다.
  • 42. Ufds[ ] 파일디스크립터 설정 init 프로세스가 poll함수(I/O multiplexing)로 감시할 파일 디스크립터 설정 [code] 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; ufds[2].events = POLLIN; fd_count = 3; ufds[3].fd = keychord_fd; ufds[3].events = POLLIN; fd_count++; ufds[0] : uevent 메시지 체크 ufds[1] : property set관련 Unix Domain 소켓 메시지 체크 ufds[2] : SIGCHLD 시그널 발생 체크 ufds[3] : keychord 발생 체크 [0] fd=device_fd event = POLLIN Ufds[ ] [1] fd=property_set_fd event = POLLIN Init [2] fd=signal_recv_fd event = POLLIN [3] fd=keychord_fd event = POLLIN
  • 43. 무한 루프 문 Loop를 돌면서 action queue 에 처리해야 하는 action이 있다면 실행하고, restart 해야 하는 prosess가 있으면 재시작 해준다. Poll() 함수를 이용하여 ufds에서 정의한 파일 디스크립터들의 입력을 감시한다 [code] for (i = 0; i < fd_count; i++) ufds[i].revents = 0; drain_action_queue(); restart_processes(); nr = poll(ufds, fd_count, timeout); if (nr <= 0) continue; if (ufds[2].revents == 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); if (ufds[3].revents == POLLIN) handle_keychord(keychord_fd);
  • 44. 마름모 안에 텍스트는 drain_action_queue(); “ufds[x].revents == POLLIN ?” restart_processes(); 대신 “dfds[x]”로 표현 Nr > 0 no nr = poll( ufds); yes yes ufds[0] wait_for_one_process(0) no yes handle_device_fd(device_fd) ufds[1] no yes ufds[2] handle_property_set_fd(property_set_fd) no yes handle_keychord(keychord_fd); ufds[3]
  • 45. Ufds[ ] handle action wait_for_one_process(0) SIGCHID 시그널에 발생 했다는 것은 child process가 종료 했다는 것을 의미한 다. 따라서 이 함수에서는 process 종료 시에 parent 가 처리해야 하는 내용이 있 다. Wait() 하고, 종료된 process 속성 값에 따라 재 시작 해줘야 하는지 확인하고 옵션에 따라 기능을 수행한다. handle_device_fd(device_fd) Device_fd에 Uevent가 발생한것은 디바이스 드라이버에서 핫 플러그 등의 디바 이스 관련한 이벤트가 발생할 경우이다. 따라서 init procrss는 이런 디바이스 메 시지를 처리해야한다. 이 루틴은 앞서서 devicd_init()에서 이미 호출된 적이 있다. - uevent는 커널에서 유저 프로세스에게 디바이스 관련 메시지를 전달하는 netlink socket의 한 종류이다. handle_property_set_fd(property_set_fd); Property가 변경되어야 할때, 소켓을 통해 전달된 정보를 바탕으로 권한 체크를 하고, 문제가 없다면 property_set()을 한다. handle_keychord(keychord_fd); Keychord 정보를 읽고 나서, 읽어온 keychord 에 매칭되는 서비스가 있으면 실 행한다.