스터디 2주차 / 3.3.4 ~ 3단원 끝
3. init 프로세스
3.4 디바이스 노드 파일 생성
- 안드로이드에서 애플리케이션은 디바이스 드라이버를 통해 하드웨어에 접근.
- 이를 위해 디바이스 드라이버의 논리적 파일인 디바이스 노드 파일 사용.
- 안드로이드에서는 보안 문제로 인해 디바이스 생성 유틸리티를 제공하지 X. 그러므로 안드로이드에서 제공하는 디바이스 노드 파일 생성 방법을 따라야 함.
3.4.1 정적 디바이스 노드 생성
- 일반적으로 리눅스에서는 동작 중에 필요한 디바이스 노드 파일을 /dev 디렉터리에 미리 정의. 애플리케이션은 이를 통해 디바이스 드라이버에 접근.
- but 안드로이드 루트 파일 시스템 이미지에는 /dev 디렉터리 존재 X. 디바이스 노드 파일 생성은 init 프로세스가 담당.
- 미리 정의된 디바이스 정보를 바탕으로 init 프로세스가 실행될 때 일괄적 으로 디바이스 노드 파일 생성. 콜드 플러그(Cold Plug) 방법.
- 시스템 동작 중 USB 포트에 장치가 삽입될 때 이에 대한 이벤트 처리로 init 프로세스가 해당 장치의 디바이스 노드 파일을 동적 으로 생성. Hot Plug 방법.
- 리눅스 커널의 udev(usersapce device) 유틸리티
- 데몬 프로세스로 동작하면서 디바이스 드라이버가 로딩될 때 메이저 번호와 마이너 번호, 디바이스 타입을 하악해서 /dev 디렉터리에 자동으로 디바이스 노드 파일을 생성.
-
데몬 프로세스? 사용자가 직접적으로 제어하지 않고, 백그라운드에서 돌면서 여러 작업을 하는 프로그램.
- 시스템 동작 중에 장치가 삽입되면 커널은 해당 장치에 대한 드라아비 로딩. 드라이버는 드라이버의 시작함수인 probe() 함수 호출. /sys 파일 시스템에 드라이버의 메이저 번호, 마이너 번호, 디바이스 타입 저장.
- udev 프로세스에 uevent 발생시킴.
- uevent란 사용자 공간의 프로세스로 메시지를 전달하기 위한 신호체계.
- 디바이스 이름, 디바이스 타입, 메이저 번호, 마이너 번호, 디바이스 노드 파일이 생성될 경로 정보를 담고 있음. 이를 udev 데몬에 전달. udev 는 uevent 감지 및 받은 메시지를 파악하여 디바이스 노드 파일 생성.
- udev 데몬이 uevent 메시지 분석하여 /sys 디렉터리에 등록된 정보를 비교.
- 비교하여 /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() 함수를 호출.
-
-
-
-
3.4.2 동적 디바이스 감지
- 핫 플러그(Hot Plug)
- 시스템 동작 중에 추가되는 장치의 디바이스 노드 파일 생성을 위해 핫플러그 처리를 지원.
- init 프로세스의 이벤트 처리 루프에서 처리.
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() 함수: 자식 프로세스 종료 이벤트 처리 함수.
- poll() 함수는 SIGCHLD 시그널 발생 시 이벤트 감시 상태에서 빠져나와 이후의 코드를 실행.
- wait_for_one_process() 함수는 SIGCHLD 시그널을 발생시킨 프로세스가 가진 서비스 리스트에서 옵션을 체크. 옵션이 oneshot 일 경우 재시작 X.
- waitpid() 함수: 시그널을 발생시킨 자식 프로세스가 종료되면 해당 프로세스에 할당된 자원을 회수. pid 값을 반환하며, 반환된 값은 SIGCHLD 시그널이 발생한 프로세스 pid.
- 인자 -1: 특정 pid가 아닌 모든 자식프로세스에 대해 SIGCHLD가 발생했는지.
- 인자 status: 자식 프로세스의 상태를 반환.
- 인자 삼항연산자: waitpid() 함수를 block으로 처리할 것인지 결정.
- service_find_by_pid() 함수: 서비스 리스트에서 종료된 프로세스에 해당하는 서비스 목록을 가져옴.
- 가져온 서비스 항목의 옵션에 SVC_ONESHOT 설정되어있는지 체크. 이 옵션이 설정된 프로세스들은 한번 실행후 재시작 X. 그대로 종료.
- 프로세스가 가진 소켓 스트립터(시그널 번호 기록되어있는 곳) 모두 삭제.
- 서비스 항목이 가진 pid 값과 상태 플래그에서 프로세스가 구종둥임을 나타내는 SVC_RUNNIG 제거.
- SVC_ONESHOT 옵션이 설정된 프로세스의 플래그에 SVC_DISABLED 설정. wait_for_one_process() 함수 빠져나가면서 프로세스 재시작 X.
- 재시작할 프로세스가 init.rc 파일에서 onerestart 옵션을 가지고 있는지 체크.
- 서비스 항목의 플래그에 restart_process() 함수에서 재시작될 프로세스를 결정할 플래그인 SVC_RESTART 추가.
- restart_process() 함수: 서비스 리스트에서 SVC_RESTART 플래그를 가진 프로세스 실행. 자식 프로세스가 종료되어 SIGCHLD가 발생히더라도 이 함수를 통해 재시작됨.
- waitpid() 함수: 시그널을 발생시킨 자식 프로세스가 종료되면 해당 프로세스에 할당된 자원을 회수. pid 값을 반환하며, 반환된 값은 SIGCHLD 시그널이 발생한 프로세스 pid.
3.6 프로퍼티 서비스
-
init 프로세스의 이벤트 처리 루프에서 처리하는 또 한가지 이벤트는 프로퍼티의 변경 요청.
-
-
프로퍼티
- 시스템이 동작하는 데 필요한 각종 설정 값을 동작 중인 모든 프로세스에서 공유하기 위해 안드로이드 플랫폼에서 사용하는 저장 공간.
- 키(key)와 값(value)로 구성. 키=값 형태.
- 안드로이드 플랫폼에서 모든 동작중인 프로세스에서는 프로퍼티의 값을 조회 가능. but 프로퍼티의 값을 변경하는 것은 오직 init 프로세스만이 가능.
- init 이외의 프로세스는 init 프로세스에 변경 요청을 한 뒤 프로퍼티 값을 변경할 수 있음.
- init 프로세스는 각 프로퍼티에 대한 접근 권한을 검사한 뒤에 프로퍼티의 값 변경.
- 트리거(trigger): 프로퍼티의 값이 변경되면 init.rc에 정의된 특정 조건을 만족하는 경우 조건에 해당하는 동작을 실행하는 것. “on property” 키워드에 기술된 명령이 실행.
3.6.1 프로퍼티 초기화
- property_init() 함수
- init 프로세스의 main() 함수 초기에 프로퍼티 영역을 초기화하기 위해 호출.
- 프로퍼티의 값을 저장하기 위한 공유 메모리를 생성. ashmem(Android Shared Memory). 외부 프로세스들이 이 공유 메모리 영역에 접근하여 프로퍼티 값 조회 가능.
-
- 생성된 영역에 값을 저장하거나 조회할 떄 property_set(), property_get() 함수 이용.
- start_property_service() 함수
- 프로퍼티 서비스를 시작하는 데 필요한 유닉스 도메인 소켓 생성 및 생성된 소켓의 디스크립터 저장.
- 소켓 생성 전에 각 파일에 저장되어 있는 프로퍼티의 기본값을 읽어 프로퍼티 값을 설정. load_properties_from_file() 함수 사용.
- 기본적인 시스템 초기값 설정되면 load_persistent_properties() 함수를 사용하여 /data/property 디렉터리에 저장되어 있는 프로퍼티 값 읽어들임.
- /data/property 디렉터리에는 시스템이 동작 중에 다른 프로세스에 의해 새로 생성된 프로퍼티 값이나, 동작중에 변경된 프로퍼티의 값들이 저장됨. 프로퍼티의 key가 파일이름, value가 파일 내용으로 저장됨. 3. 프로퍼티의 초기값 설정 완료되면 create_socket() 함수로 도메인 소켓 생성.
- 프로퍼티 서비스를 시작하는 데 필요한 유닉스 도메인 소켓 생성 및 생성된 소켓의 디스크립터 저장.
3.6.2 프로퍼티 변경 요청 처리
- 앞서 생성한 소켓으로 프로퍼티 값의 변경 요청 메시지가 수신되면, 권한 검사 및 메시지 처리 함수인 handle_property_set_fd() 함수 호출.
- 메시지를 전송한 프로세스의 접근 권한을 검사하기 위해 소켓으로부터 SO_PEERCRED 값 얻어옴.
- 프로세스의 시작과 종료를 요청하는 ctl로 시작하는 메시지는 check_control_perms() 함수를 호출하여 접근 권한을 검사.
- 그 외의 메시지는 check_perms() 함수를 호출하여 검사.
- 각 프로퍼티의 접근 권한은 리눅스의 uid를 사용하여 구분.
- init.rc 파일에는 프로퍼티가 변경됐을 때 필요한 동작이 기술되어 있음. 이러한 동작이 실행되는 조건은 “on property:
= " 형식으로 기술. 조건에 해당하는 키의 값이 설정되면 조건에 따른 명령(trigger) 실행.