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 에 존재한다.
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");
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에 추가하고 실행한다.
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 에 매칭되는 서비스가 있으면 실
행한다.