• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Android+init+process
 

Android+init+process

on

  • 7,477 views

 

Statistics

Views

Total Views
7,477
Views on SlideShare
7,477
Embed Views
0

Actions

Likes
1
Downloads
201
Comments
1

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel

11 of 1 previous next

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • androidinitprocess
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Android+init+process Android+init+process Presentation Transcript

    • Android Init
      ENM System 권홍재
    • Kernel에서 Android init 시작
      • Android 커널 부팅이 완료된 후 루트파일 시스템의 “/init”을 찾아서 수행한다.
      • 안드로이드init은 루트에 위치하기때문에 반드시 커널 부팅옵션에서 “init=/init”옵션이 주어져야 한다.
      < kernel/init/main.c >
      static intnoinlineinit_post(void)
      {
      ...
      if (execute_command) {
      run_init_process(execute_command);
      printk(KERN_WARNING "Failed to execute %s. Attempting "
      "defaults...n", execute_command);
      }
      run_init_process("/sbin/init");
      run_init_process("/etc/init");
      run_init_process("/bin/init");
      run_init_process("/bin/sh");
      panic("No init found. Try passing init= option to kernel.");
      }
    • Init 프로세스의 시작과 Signal 처리
      • Android init process가 실행한 자식프로세스들이 종료되는 경우의 처리를 위해 SIGCHLD신호를 초기화한다.
      < android/system/core/init/init.c >
      static void sigchld_handler(int s)
      {
      write(signal_fd, &s, 1);
      }
      ...
      int main(intargc, char **argv)
      {
      ...
      structsigaction act;
      act.sa_handler = sigchld_handler;
      act.sa_flags = SA_NOCLDSTOP;
      act.sa_mask = 0;
      act.sa_restorer = NULL;
      sigaction(SIGCHLD, &act, 0);
      ...
      }
    • 디렉토리 생성과 mount
      • 루트파일시스템에 디렉토리를 생성하고 리눅스 가상파일 시스템을 마운트한다.
      < android/system/core/init/init.c >
      mkdir("/dev", 0755);
      mkdir("/proc", 0755);
      mkdir("/sys", 0755);
      mount("tmpfs", "/dev", "tmpfs", 0, "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);
    • Standard I/O 설정
      • 안드로이드log메시지를 한곳에서 보기위해(logcat)Standard I/O를 모두 막는다.
      < android/system/core/init/init.c >
      int main(intargc, char **argv)
      {
      ...
      open_devnull_stdio();
      ...
      }
      void open_devnull_stdio(void)
      {
      intfd;
      static const char *name = "/dev/__null__";
      if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
      fd = open(name, O_RDWR);
      unlink(name);
      if (fd >= 0) {
      dup2(fd, 0); dup2(fd, 1); dup2(fd, 2);
      if (fd > 2) {
      close(fd);
      }
      return;
      }
      }
      exit(1);
      }
      fd[0]
      stdin
      fd[0]
      stdin
      fd[1]
      stdout
      fd[1]
      stdout
      fd[2]
      stderr
      fd[2]
      stderr
      fd[3]
      _null_
      fd[3]
      _null_
      fd[…]
      fd[…]
    • Init 프로세스의 로그메시지 설정
      • Init process의 로그메시지는 kmsg를 통해 출력한다.
      (dmesg유틸을 사용하여 확인가능)
      < android/system/core/init/init.c >
      int main(intargc, char **argv)
      {
      ...
      log_init();

      }
      < android/system/core/init/util.c >
      #define ERROR(x...) log_write(3, "<3>init: " x)
      #define NOTICE(x...) log_write(5, "<5>init: " x)
      #define INFO(x...) log_write(6, "<6>init: " x)
      void log_init(void)
      {
      static const char *name = "/dev/__kmsg__";
      if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {
      log_fd = open(name, O_WRONLY);

      }
      }
      void log_write(int level, const char *fmt, ...)
      {

      write(log_fd, buf, strlen(buf));
      }
    • init.rc파싱
      • Standard I/O설정을 변경하였으므로 그 후부터의 메시지를 출력하기위해kmsg를 이용한다. (dmsg유틸을 사용하여 확인가능)
      < android/system/core/init/init.c >
      int main(intargc, char **argv)
      {
      ...
      parse_config_file(“init.rc”);
      ….
      }
      • Init.rc구조
      < android/system/core/rootdir/init.rc >
      on init

      on boot

      on property:ro.kernel.qemu=1

      serviceservicemanager /system/bin/servicemanager
      servicevold /system/bin/vold
      servicenetd /system/bin/netd

      action_list
      service_list
    • Emulator초기화
      • 에뮬레이터 환경을 위한 QEMU장치 초기화
      < android/system/core/init/init.c >
      int main(intargc, char **argv)
      {
      ...
      qemu_init();
      ….
      }
      • Android Emulator
      • QEMU기반의 어플리케이션이다.
      • 커널cmdline에 qumu=1의 값이 있다면 안드로이드 에뮬레이터라고 인식한다.
      • qemu mode에서는 property영역에 ro.kernel.<name>에 해당하는 값을 설정하고 이를 시스템에서 참조한다.
      • QEMU
      • PC를 위한 오픈 소스 에뮬레이터
      • 프로세서를 에뮬레이션하는 이외에 네트워크, 비디오 하드웨어와 같은 필요한 모든 하위 시스템을 흉내낸다.
      • 또한 (255개 CPU까지 지원하는) SMP와 같은 최신 개념, ARM이나 PowerPC와 같은 다른 프로세서 아키텍처도 에뮬레이션한다.
    • Kernel의 cmdline의 내용을 전역변수에 저장
      • 사용될 /proc/cmdline(커널 실행옵션)의 옵션들을 전역번수에 저장한다.
      < android/system/core/init/init.c >
      int main(intargc, char **argv)
      {
      ...
      import_kernel_cmdline(0);
      ….
      }
      • /proc/cmdline내용
      # cat /proc/cmdline
      init=/init console=ttySAC1,115200 root=/dev/mtdblock2 rwrootfstype=yaffs2 ip=192.168.0.101:192.168.0.198:192.168.0.1:255.255.255.0::eth0:off ethaddr=00:40:5c:26:0a:5b;
    • init.[H/W이름].rc파싱
      • 하드웨어 명칭을 얻어와서 init.[H/W이름].rc파일의 내용을 action_list와 service_list 구조체에 각각 담는다.
      < android/system/core/init/init.c >
      int main(intargc, char **argv)
      {
      ...
      get_hardware_name();
      snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
      parse_config_file(tmp);
      ….
      }
      • /proc/cpuinfo내용
      (Hardware명을소문자로 치환하여 rc파일을 찾는다.)
      # cat /proc/cpuinfo

      Hardware : ENMC100

    • .rc파일의 early-init섹션 수행
      • Linked list로 연결된 action_list구조체의 내용을 수행한다.
      < android/system/core/init/init.c >
      int main(intargc, char **argv)
      {
      ...
      action_for_each_trigger("early-init", action_add_queue_tail);
      drain_action_queue();
      ….
      }
      • action_for_each_trigger("early-init", action_add_queue_tail);
      • init.rc와 init.[H/W 이름].rc파일에서 early-early-init섹션에 해당하는 부분을 실행큐에 담는다.
      • Drain_action_queue();
      • 실행큐(action_queue)에 저장된 명령어를 하나씩 수행한다.
    • 정적 디바이스 노드파일 생성(Cold Plug)
      • uevent발생을 위한 Socket생성
      • 정적 디바이스 파일 생성 flow
      < init.c >
      device_fd = device_init();
      main()
      < devices.c >
      device_init()
      coldboot()
      do_coldboot()
      devperms구조체를 참조하여
      uevent강제발생
      Hot Plug
      handle_device_fd()
      handle_device_event()
      make_device()
    • property 공간 초기화
      • property정보를 위한 공유메모리 공간을 생성한다
      (anonymous shared memory)
      < android/system/core/init/init.c >
      int main(intargc, char **argv)
      {
      ...
      property_init();
      ….
      }
      < android/system/core/init/init.c >
      void property_init(void){
      init_property_area();
      load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);  /default.prop
      }
    • keychord open 및 serial 동작확인
      • keychord입력을 받기위해/dev/keychord를 open한다.
      • serial 연결을 검사한다.
      < android/system/core/init/init.c >
      int main(intargc, char **argv)
      {

      // only listen for keychords if ro.debuggable is true
      /* nothing to do if no services require keychords */
      keychord_fd = open_keychord();
      // check serial
      // e.g. console name  /dev/ttySAC1
      fd = open(console_name, O_RDWR);
      if (fd >= 0)
      have_console = 1;
      close(fd);

      }
      • Keychord - 핸드폰에 있는 단축키와 조합키 와 같은 특수키와 조합 키를 지원하기 위한 구조
    • LCD 부팅로그 출력
      • 안드로이드 부팅로그를 LCD화면에 출력한다.
      < android/system/core/init/init.c >
      int main(intargc, char **argv)
      {

      if( load_565rle_image(INIT_IMAGE_FILE) ) {
      fd = open("/dev/tty0", O_WRONLY);
      if (fd >= 0) {
      const char *msg;
      msg = "n“
      "n" // console is 40 cols x 30 lines
      “ A N D R O I D ";
      write(fd, msg, strlen(msg));
      close(fd);
      }

      }
    • property 설정
      • property영역에 시스템 운용에 필요한 초기값을 설정한다.
      • import_kernel_cmdline에서 읽어온값 설정
      < android/system/core/init/init.c >
      int main(intargc, char **argv)
      {

      if (qemu[0])
      import_kernel_cmdline(1);
      if (!strcmp(bootmode,"factory"))
      property_set("ro.factorytest", "1");
      else if (!strcmp(bootmode,"factory2"))
      property_set("ro.factorytest", "2");
      else
      property_set("ro.factorytest", "0");
      property_set("ro.serialno", serialno[0] ? serialno : "");
      property_set("ro.bootmode", bootmode[0] ? bootmode : "unknown");
      property_set("ro.baseband", baseband[0] ? baseband : "unknown");
      property_set("ro.carrier", carrier[0] ? carrier : "unknown");
      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 서버 시작
      • .property파일에서 내용을 로드하고 property 설정 관련서비스를 위한 socket을 생성한다.
      < android/system/core/init/init.c >
      property_set_fd = start_property_service();
      < android/system/core/init/property_service.c >
      intstart_property_service(void){
      intfd;
      load_properties_from_file(PROP_PATH_SYSTEM_BUILD);  /system/build.prop
      load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);  /system/default.prop
      load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE);  /data/local.prop
      load_persistent_properties();  /data/property/persist.xxxx파일의 내용을 로드한다.
      fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);
      if(fd < 0) return -1;
      fcntl(fd, F_SETFD, FD_CLOEXEC);
      fcntl(fd, F_SETFL, O_NONBLOCK);
      listen(fd, 8);
      return fd;
      }
      < android/system/core/init/util.c >
      intcreate_socket(const char *name, int type, mode_t perm, uid_tuid, gid_tgid){
      fd = socket(PF_UNIX, type, 0);
      memset(&addr, 0 , sizeof(addr));
      addr.sun_family = AF_UNIX;
      // addr.sun_path /dev/socket/property_service
      snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s“, name);
      bind(fd, (structsockaddr *) &addr, sizeof (addr));
      return fd;
      }
    • SIGCHLD Signal 통신Socket 생성
      • SIGCHLD Signal발생시 통지하기위한Socket을 생성한다.
      < android/system/core/init/init.c >
      static void sigchld_handler(int s)
      {
      write(signal_fd, &s, 1);
      }

      int main(intargc, char **argv)
      {

      /* create a signalling mechanism for the sigchld handler */
      if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) {
      signal_fd = s[0];  쓰기전용 file descriptor
      signal_recv_fd = s[1];  읽기전용 file descriptor
      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);
      }

      }
    • File Descriptor검사 및 boot섹션 수행
      • 앞서 얻어온 file descriptor들이 올바른지 검사한다.
      • early-boot, boot섹션을 실행큐에 담고 수행한다.
      < android/system/core/init/init.c >
      int main(intargc, char **argv)
      {

      /* make sure we actually have all the pieces we need */
      if ((device_fd < 0) ||
      (property_set_fd < 0) ||
      (signal_recv_fd < 0)) {
      ERROR("init startup failuren");
      return 1;
      }
      /* execute all the boot actions to get us started */
      action_for_each_trigger("early-boot", action_add_queue_tail);
      action_for_each_trigger("boot", action_add_queue_tail);
      drain_action_queue();

      }
    • on property에 지정된 service수행
      • property섹션을 실행큐에 담고 수행 해당 명령을 수행한다.
      • property를 설정했을때init.rc에 지정한 명령의수행을 위해 property_triggers를 enable시킨다.
      < android/system/core/init/init.c >
      int main(intargc, char **argv)
      {

      /* run all property triggers based on current state of the properties */
      queue_all_property_triggers();
      drain_action_queue();
      /* enable property triggers */
      property_triggers_enabled = 1;

      }
      • 현재 설정되어있는 property값이 init.rc의 값과 같다면 특정 명령을 수행시킨다.
      < android/system/core/rootdir/init.rc >

      on property:persist.service.adb.enable=1
      start adbd
      on property:persist.service.adb.enable=0
      stop adbd

    • 감시할 이벤트 설정
      • 앞서 얻어온 file descriptor들이 올바른지 검사한다.
      • early-boot, boot섹션을 실행큐에 담고 수행한다.
      < android/system/core/init/init.c >
      int main(intargc, char **argv)
      {

      structpollfdufds[4];

      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;
      if (keychord_fd > 0) {
      ufds[3].fd = keychord_fd;
      ufds[3].events = POLLIN;
      fd_count++;
      } else {
      ufds[3].events = 0;
      ufds[3].revents = 0;
      }

      }
    • bootchart생성기능수행
      • INIT_BOOTCHART=true 환경변수를 설정할경우 수행되는 로직.
      • 안드로이드boot process 분석을위해 관련 데이터들을 수집한다.
      < android/system/core/init/init.c >
      int main(intargc, char **argv)
      {

      i#f BOOTCHART
      bootchart_count = bootchart_init();
      if (bootchart_count < 0) {
      ERROR("bootcharting init failuren");
      } else if (bootchart_count > 0) {
      NOTICE("bootcharting started (period=%d ms)n", bootchart_count*BOOTCHART_POLLING_MS);
      } else {
      NOTICE("bootcharting ignoredn");
      }
      #endif

      for(;;) {
      #if BOOTCHART
      if (bootchart_count > 0) {
      if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)
      timeout = BOOTCHART_POLLING_MS;
      if (bootchart_step() < 0 || --bootchart_count == 0) {
      bootchart_finish();
      bootchart_count = 0;
      }
      }
      #endif

      }

      }
    • 이벤트 처리루프 수행
      • 무한루프를 돌면서 해당하는 이벤트가 발생시 각각 처리한다.
      int main(intargc, char **argv)
      {

      for(;;) {
      int nr, i, timeout = -1;
      for (i = 0; i < fd_count; i++)
      ufds[i].revents = 0;
      drain_action_queue();
      restart_processes();
      if (process_needs_restart) {
      timeout = (process_needs_restart - gettime()) * 1000;
      if (timeout < 0)
      timeout = 0;
      }
      nr = poll(ufds, fd_count, timeout);
      if (nr <= 0)
      continue;
      < android/system/core/init/init.c >
      if (ufds[2].revents == POLLIN) {
      /* we got a SIGCHLD - reap and restart as needed */
      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);
      }
      return 0;
      }
    • - 수고하셨습니다 -