스터디 2주차 / 3.3.4 ~ 3단원 끝

5 minute read

3. init 프로세스


3.4 디바이스 노드 파일 생성

  • 안드로이드에서 애플리케이션은 디바이스 드라이버를 통해 하드웨어에 접근.
  • 이를 위해 디바이스 드라이버의 논리적 파일인 디바이스 노드 파일 사용.
  • 안드로이드에서는 보안 문제로 인해 디바이스 생성 유틸리티를 제공하지 X. 그러므로 안드로이드에서 제공하는 디바이스 노드 파일 생성 방법을 따라야 함.

3.4.1 정적 디바이스 노드 생성

  • 일반적으로 리눅스에서는 동작 중에 필요한 디바이스 노드 파일을 /dev 디렉터리에 미리 정의. 애플리케이션은 이를 통해 디바이스 드라이버에 접근.
  • but 안드로이드 루트 파일 시스템 이미지에는 /dev 디렉터리 존재 X. 디바이스 노드 파일 생성은 init 프로세스가 담당.
    1. 미리 정의된 디바이스 정보를 바탕으로 init 프로세스가 실행될 때 일괄적 으로 디바이스 노드 파일 생성. 콜드 플러그(Cold Plug) 방법.
    2. 시스템 동작 중 USB 포트에 장치가 삽입될 때 이에 대한 이벤트 처리로 init 프로세스가 해당 장치의 디바이스 노드 파일을 동적 으로 생성. Hot Plug 방법.
  • 리눅스 커널의 udev(usersapce device) 유틸리티
    • 데몬 프로세스로 동작하면서 디바이스 드라이버가 로딩될 때 메이저 번호와 마이너 번호, 디바이스 타입을 하악해서 /dev 디렉터리에 자동으로 디바이스 노드 파일을 생성.
    • 데몬 프로세스? 사용자가 직접적으로 제어하지 않고, 백그라운드에서 돌면서 여러 작업을 하는 프로그램. 20190328_144358

      1. 시스템 동작 중에 장치가 삽입되면 커널은 해당 장치에 대한 드라아비 로딩. 드라이버는 드라이버의 시작함수인 probe() 함수 호출. /sys 파일 시스템에 드라이버의 메이저 번호, 마이너 번호, 디바이스 타입 저장.
      2. udev 프로세스에 uevent 발생시킴.
        • uevent란 사용자 공간의 프로세스로 메시지를 전달하기 위한 신호체계.
        • 디바이스 이름, 디바이스 타입, 메이저 번호, 마이너 번호, 디바이스 노드 파일이 생성될 경로 정보를 담고 있음. 이를 udev 데몬에 전달. udev 는 uevent 감지 및 받은 메시지를 파악하여 디바이스 노드 파일 생성.
      3. udev 데몬이 uevent 메시지 분석하여 /sys 디렉터리에 등록된 정보를 비교.
      4. 비교하여 /dev 디렉터리의 적절한 위치에 디바이스 노드 파일을 생성.
  • 콜드 플러그(Cold Plug)

    • udev 데몬은 커널의 부팅 과정 이후 동작하는 프로세스. 따라서 커널 부팅 과정에서 발생하는 디바이스 드라이버의 uevent 처리 X. 즉 디바이스 노드 파일 생성 X.
    • 리눅스에서 udev 데몬 실행 이전에 로딩된 리바이스 드라이버에 대해 콜드 플러그를 제공하여 디바이스 노드 파일이 생성되지 않는 문제를 해결,
    • 이미 삽입된 장치에 대한 처리를 담당하는 메커니즘.
    • 커널 부팅 후에 udev 데몬이 실행되면서 /sys 디렉터리에서 미리 등록된 디바이스 정보를 읽어들인 후 각 디바이스에 대해 uevent를 다시 발생 시켜 디바이스 노드 파일을 생성하는 방식.
    • 안드로이드도 동일한 방식으로 디바이스 노드 파일 생성. init 프로세스가 담당.
    • 안드로이드 코드의 /sysyrm/core/init/devices.c 파일에는 init 프로세스가 생성하는 노드 파일 목록이 있음. init 프로세스는 콜드 플러그 처리시에 devperms 구조체를 참고하여 /dev 디텍터리에 디바이스 노드 파일들을 생성.
    • devperms 구조체: 디바이스 노드 파일 이름. 접근 권한, 사용자 ID, 그룹 ID.
    • init 프로세스의 콜드 플러그 처리 절차. init 프로세스는 device_init() 함수를 호출.

    • 20190328_150401

    • 20190328_151029

    • 20190328_175046

    • 20190328_151930

    • 20190328_152304

3.4.2 동적 디바이스 감지

  • 핫 플러그(Hot Plug)
    • 시스템 동작 중에 추가되는 장치의 디바이스 노드 파일 생성을 위해 핫플러그 처리를 지원.
    • init 프로세스의 이벤트 처리 루프에서 처리. 20190328_152849

3.5 프로세스 종료와 재시작

  • init 프로세스는 init.rc 파일로부터 파싱한 서비스 리스트를 통해 자식 프로세스를 순차적으로 실행.
  • init이 실행하는 주요 프로세스
    • sh: 안드로이드가 탑재된 기기에 대해 터미널, 시리얼 혹은 adbd 접속 시 콘솔 입출력을 제공하는 셸 프로그램.
    • adbd: Android Debug Bridge 데몬. QEMU 애뮬레이터나 실제 기기의 상태를 관리하는데에 쓰이는 툴. 클라이언트-서버 프로그램의 일종으로 애뮬레이터나 기기 상에서 동작하는 서버.
    • servicemanager: 안드로이드 시스템 서비스의 목록을 관리.
    • vold: Volume 데몬. USB 스토리지나 SD 카드 장치를 마운트하고 관리.
    • playmp3: 안드로이드 부팅 시 부팅 사운드를 출력,
  • init 프로세스가 실행하는 프로세스는 일부를 제외하고 대부분 종료되더라도 init 프로세스에 의해 재시작 됨.

3.5.1 프로세스 재시작 코드 분석

  • sigchld_handler() 함수: 이벤트 처리 루프에서 대기하다가 자식 프로세스가 종료되면 SIGCHLD 시그널에 대한 핸들러 수행.
  • wait_for_one_process() 함수: 자식 프로세스 종료 이벤트 처리 함수.

  • 20190328_155612
    1. poll() 함수는 SIGCHLD 시그널 발생 시 이벤트 감시 상태에서 빠져나와 이후의 코드를 실행.
    2. wait_for_one_process() 함수는 SIGCHLD 시그널을 발생시킨 프로세스가 가진 서비스 리스트에서 옵션을 체크. 옵션이 oneshot 일 경우 재시작 X.
  • 20190328_175901 20190328_175927
    1. waitpid() 함수: 시그널을 발생시킨 자식 프로세스가 종료되면 해당 프로세스에 할당된 자원을 회수. pid 값을 반환하며, 반환된 값은 SIGCHLD 시그널이 발생한 프로세스 pid.
      • 인자 -1: 특정 pid가 아닌 모든 자식프로세스에 대해 SIGCHLD가 발생했는지.
      • 인자 status: 자식 프로세스의 상태를 반환.
      • 인자 삼항연산자: waitpid() 함수를 block으로 처리할 것인지 결정.
    2. service_find_by_pid() 함수: 서비스 리스트에서 종료된 프로세스에 해당하는 서비스 목록을 가져옴.
    3. 가져온 서비스 항목의 옵션에 SVC_ONESHOT 설정되어있는지 체크. 이 옵션이 설정된 프로세스들은 한번 실행후 재시작 X. 그대로 종료.
    4. 프로세스가 가진 소켓 스트립터(시그널 번호 기록되어있는 곳) 모두 삭제.
    5. 서비스 항목이 가진 pid 값과 상태 플래그에서 프로세스가 구종둥임을 나타내는 SVC_RUNNIG 제거.
    6. SVC_ONESHOT 옵션이 설정된 프로세스의 플래그에 SVC_DISABLED 설정. wait_for_one_process() 함수 빠져나가면서 프로세스 재시작 X.
    7. 재시작할 프로세스가 init.rc 파일에서 onerestart 옵션을 가지고 있는지 체크.
    8. 서비스 항목의 플래그에 restart_process() 함수에서 재시작될 프로세스를 결정할 플래그인 SVC_RESTART 추가.
      • restart_process() 함수: 서비스 리스트에서 SVC_RESTART 플래그를 가진 프로세스 실행. 자식 프로세스가 종료되어 SIGCHLD가 발생히더라도 이 함수를 통해 재시작됨.

3.6 프로퍼티 서비스

  • init 프로세스의 이벤트 처리 루프에서 처리하는 또 한가지 이벤트는 프로퍼티의 변경 요청.

  • 20190328_163059

  • 프로퍼티

    • 시스템이 동작하는 데 필요한 각종 설정 값을 동작 중인 모든 프로세스에서 공유하기 위해 안드로이드 플랫폼에서 사용하는 저장 공간.
    • 키(key)와 값(value)로 구성. 키=값 형태.
    • 안드로이드 플랫폼에서 모든 동작중인 프로세스에서는 프로퍼티의 값을 조회 가능. but 프로퍼티의 값을 변경하는 것은 오직 init 프로세스만이 가능.
    • init 이외의 프로세스는 init 프로세스에 변경 요청을 한 뒤 프로퍼티 값을 변경할 수 있음.
      • init 프로세스는 각 프로퍼티에 대한 접근 권한을 검사한 뒤에 프로퍼티의 값 변경.
    • 트리거(trigger): 프로퍼티의 값이 변경되면 init.rc에 정의된 특정 조건을 만족하는 경우 조건에 해당하는 동작을 실행하는 것. “on property” 키워드에 기술된 명령이 실행.

3.6.1 프로퍼티 초기화

  1. property_init() 함수
    • init 프로세스의 main() 함수 초기에 프로퍼티 영역을 초기화하기 위해 호출.
    • 프로퍼티의 값을 저장하기 위한 공유 메모리를 생성. ashmem(Android Shared Memory). 외부 프로세스들이 이 공유 메모리 영역에 접근하여 프로퍼티 값 조회 가능.
    • 20190328_171214

    • 생성된 영역에 값을 저장하거나 조회할 떄 property_set(), property_get() 함수 이용.
  2. start_property_service() 함수
    • 프로퍼티 서비스를 시작하는 데 필요한 유닉스 도메인 소켓 생성 및 생성된 소켓의 디스크립터 저장.
      1. 소켓 생성 전에 각 파일에 저장되어 있는 프로퍼티의 기본값을 읽어 프로퍼티 값을 설정. load_properties_from_file() 함수 사용.
      2. 기본적인 시스템 초기값 설정되면 load_persistent_properties() 함수를 사용하여 /data/property 디렉터리에 저장되어 있는 프로퍼티 값 읽어들임.
      • /data/property 디렉터리에는 시스템이 동작 중에 다른 프로세스에 의해 새로 생성된 프로퍼티 값이나, 동작중에 변경된 프로퍼티의 값들이 저장됨. 프로퍼티의 key가 파일이름, value가 파일 내용으로 저장됨. 3. 프로퍼티의 초기값 설정 완료되면 create_socket() 함수로 도메인 소켓 생성.

3.6.2 프로퍼티 변경 요청 처리

  • 앞서 생성한 소켓으로 프로퍼티 값의 변경 요청 메시지가 수신되면, 권한 검사 및 메시지 처리 함수인 handle_property_set_fd() 함수 호출.
    1. 메시지를 전송한 프로세스의 접근 권한을 검사하기 위해 소켓으로부터 SO_PEERCRED 값 얻어옴.
    2. 프로세스의 시작과 종료를 요청하는 ctl로 시작하는 메시지는 check_control_perms() 함수를 호출하여 접근 권한을 검사.
    3. 그 외의 메시지는 check_perms() 함수를 호출하여 검사.
      • 각 프로퍼티의 접근 권한은 리눅스의 uid를 사용하여 구분.
  • init.rc 파일에는 프로퍼티가 변경됐을 때 필요한 동작이 기술되어 있음. 이러한 동작이 실행되는 조건은 “on property:=" 형식으로 기술. 조건에 해당하는 키의 값이 설정되면 조건에 따른 명령(trigger) 실행.