출처 : http://puresay.springnote.com/pages/5470395.xhtml
안드로이드의 초기화 과정 중 Init 프로세스를 생성하고 init 프로세스가 동작하는 과정에 대하여 정리한다.
Init 프로세스는 부팅 과정에서 커널이 생성하는 첫번째 프로세스로 PID는 1이다.
리눅스 커널의 부팅 과정을 보면 알겠지만 다음 과정에서 생성된다.
/kernel/init/main.c 파일을 참조하면 다음과 같은 초기화 과정을 거친다.
start_kernel()
rest_init()
kernel_init()
init_post()
init_post()에서 init 프로세스를 생성하는데, 이를 위해서 ramdisk_execute_command를 kernel_init()에서 "/init" 으로 설정하고 access 여부를 확인한다. run_init_process()에서는 입력된 파일명을 실행하는 역활만을 한다.
static int noinline init_post(void)
{
free_initmem();
unlock_kernel();
mark_rodata_ro();
system_state = SYSTEM_RUNNING;
numa_default_policy();
if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
printk(KERN_WARNING "Warning: unable to open an initial console.\n");
(void) sys_dup(0);
(void) sys_dup(0);
current->signal->flags |= SIGNAL_UNKILLABLE;
if (ramdisk_execute_command) {
run_init_process(ramdisk_execute_command);
printk(KERN_WARNING "Failed to execute %s\n",
ramdisk_execute_command);
}
/*
* We try each of these until one succeeds.
*
* The Bourne shell can be used instead of init if we are
* trying to recover a really broken machine.
*/
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.");
}
이후에 /sbin/init, /etc/init, /bin/init, /bin/sh 를 실행하는 코드가 있는데 안드로이드의 경우 실제 이와 관련된 파일이 없기 때문에 위의 과정으로만 처리가 된다.
안드로이드에서 init 프로세스와 관련된 코드는 /system/core/init/init.c 에 있다. init 프로세스가 하는 중요한 작업 중 하나는 init.rc 파일을 처리하는 것이다.
안드로이드에서 기본적으로 사용하는 init.rc 파일은 /system/core/rootdir/init.rc 파일이다. /system/core/rootdir/Android.mk 파일을 보면 init.rc 파일을 target 디렉토리로 복사하도록 하고 있다.
init 프로세스의 기본 기능을 다시 한번 정리하면 다음과 같다.
1) SIGCHLD signal 처리
2) rc 파일 처리 (init.rc 와 init.xxx.rc)
3) 디바이스 초기화와 관리
4) 기본 property 설정
이중 rc 파일 처리를 보면 다음과 같다.
INFO("reading config file\n");
parse_config_file("/init.rc");
/* pull the kernel commandline and ramdisk properties file in */
qemu_init();
import!_kernel_cmdline(0);
get_hardware_name();
snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
parse_config_file(tmp);
처음 init.rc 파일을 분석해서 서비스(Service) 리스트와 액션(Action) 리스트를 만든다.
다음으로 import!_kernel_cmdline()을 호출해서 필요한 내용을 전역 변수에 저장한다. 이를 보면 /proc/cmdline을 읽어와서 이를 분석하여 저장하는데 /proc/cmdline에 저장된 내용은 kernel 컴파일시 CONFIG_CMDLINE에 정의한 문자열 (/kernel/.config 파일 참조)이다.
static void import!_kernel_cmdline(int in_qemu)
{
char cmdline[1024];
char *ptr;
int fd;
fd = open("/proc/cmdline", O_RDONLY);
if (fd >= 0) {
int n = read(fd, cmdline, 1023);
if (n < 0) n = 0;
/* get rid of trailing newline, it happens */
if (n > 0 && cmdline[n-1] == '\n') n--;
cmdline[n] = 0;
close(fd);
} else {
cmdline[0] = 0;
}
ptr = cmdline;
while (ptr && *ptr) {
char *x = strchr(ptr, ' ');
if (x != 0) *x++ = 0;
import!_kernel_nv(ptr, in_qemu);
ptr = x;
}
/* don't expose the raw commandline to nonpriv processes */
chmod("/proc/cmdline", 0440);
}
arm의 경우는 CONFIG_CMDLINE으로 정의된 문자열을 /kernel/arch/arm/kernel/setup.c 에서 처리한다.
하여간에 import!_kernel_cmdline()을 보면 /proc/cmdline의 내용을 읽어와서 import!_kernel_nv()를 이용해서 전역 변수(console, bootmode, serialno, baseband, carrier, bootloader, hardware)에 저장한다.
내 안드로이드 환경에서는 CONFIG_CMDLINE의 문자열은 다음과 같다. androidboot.hardware만 정의하기 때문에 전역 변수 hardware 값만 설정된다.
마지막으로 get_hardware_name()을 호출해서 hardware 이름(xxx라고 가정)을 얻어낸 후, init.xxx.rc 파일을 분석해서 역시 서비스 리스트와 액션 리스트를 만든다. init.rc가 안드로이드에서 기본적으로 수행해야할 서비스들을 정리한다고 하면 init.xxx.rc에는 하드웨어 의존적인 초기화 과정을 만들어 두어야 한다.
get_hardware_name()을 보면 /proc/cpuinfo의 내용을 읽어서 전역 변수 hardware와 revision을 다시 설정하는데 왜 다시 처리하는지는 모르겠다.
일단 init,rc와 init.xxx.rc (내 보드에서는 init.tcc92xx.rc)을 분석해서 서비스 리스트와 액션 리스트를 만드는 것은 parse_config_file()이 한다. 이 함수는 /system/core/init/parser.c 에 있다.
init.rc 파일은 on init 에서 수행해야 하는 액션 리스트와 on boot에서 수행해야 하는 액션 리스트가 있다.
on init
sysclktz 0
loglevel 3
# setup the global environment
export PATH /sbin:/system/sbin:/system/bin:/system/xbin
export LD_LIBRARY_PATH /system/lib
export ANDROID_BOOTLOGO 1
export ANDROID_ROOT /system
export ANDROID_ASSETS /system/app
export ANDROID_DATA /data
export EXTERNAL_STORAGE /sdcard
# Backward compatibility
symlink /system/etc /etc
# create mountpoints and mount tmpfs on sqlite_stmt_journals
mkdir /sdcard 0000 system system
mkdir /system
mkdir /data 0771 system system
mkdir /cache 0770 system cache
mkdir /sqlite_stmt_journals 01777 root root
mount tmpfs tmpfs /sqlite_stmt_journals size=4m
mount rootfs rootfs / ro remount
write /proc/sys/kernel/panic_on_oops 1
write /proc/sys/kernel/hung_task_timeout_secs 0
write /proc/cpu/alignment 4
write /proc/sys/kernel/sched_latency_ns 10000000
write /proc/sys/kernel/sched_wakeup_granularity_ns 2000000
write /proc/sys/kernel/sched_compat_yield 1
# Create cgroup mount points for process groups
mkdir /dev/cpuctl
mount cgroup none /dev/cpuctl cpu
chown system system /dev/cpuctl
chown system system /dev/cpuctl/tasks
chmod 0777 /dev/cpuctl/tasks
......
# mount mtd partitions
# Mount /system rw first to give the filesystem a chance to save a checkpoint
mount yaffs2 mtd@system /system
mount yaffs2 mtd@system /system ro remount
......
# create basic filesystem structure
mkdir /data/misc 01771 system misc
mkdir /data/misc/hcid 0770 bluetooth bluetooth
mkdir /data/misc/keystore 0770 keystore keystore
on boot
# basic network init
ifup lo
hostname localhost
domainname localdomain
# set RLIMIT_NICE to allow priorities from 19 to -20
setrlimit 13 40 40
# Define the oom_adj values for the classes of processes that can be
# killed by the kernel. These are used in ActivityManagerService.
setprop ro.FOREGROUND_APP_ADJ 0
setprop ro.VISIBLE_APP_ADJ 1
setprop ro.SECONDARY_SERVER_ADJ 2
setprop ro.BACKUP_APP_ADJ 2
setprop ro.HOME_APP_ADJ 4
setprop ro.HIDDEN_APP_MIN_ADJ 7
setprop ro.CONTENT_PROVIDER_ADJ 14
setprop ro.EMPTY_APP_ADJ 15
# Define the memory thresholds at which the above process classes will
# be killed. These numbers are in pages (4k).
setprop ro.FOREGROUND_APP_MEM 1536
setprop ro.VISIBLE_APP_MEM 2048
setprop ro.SECONDARY_SERVER_MEM 4096
setprop ro.BACKUP_APP_MEM 4096
setprop ro.HOME_APP_MEM 4096
setprop ro.HIDDEN_APP_MEM 5120
setprop ro.CONTENT_PROVIDER_MEM 5632
setprop ro.EMPTY_APP_MEM 6144
# Write value must be consistent with the above properties.
# Note that the driver only supports 6 slots, so we have HOME_APP at the
# same memory level as services.
write /sys/module/lowmemorykiller/parameters/adj 0,1,2,7,14,15
write /proc/sys/vm/overcommit_memory 1
write /proc/sys/vm/min_free_order_shift 4
write /sys/module/lowmemorykiller/parameters/minfree 1536,2048,4096,5120,5632,6144
# Set init its forked children's oom_adj.
write /proc/1/oom_adj -16
# Permissions for System Server and daemons.
chown radio system /sys/android_power/state
chown radio system /sys/android_power/request_state
chown radio system /sys/android_power/acquire_full_wake_lock
chown radio system /sys/android_power/acquire_partial_wake_lock
chown radio system /sys/android_power/release_wake_lock
chown radio system /sys/power/state
......
# Define TCP buffer sizes for various networks
# ReadMin, ReadInitial, ReadMax, WriteMin, WriteInitial, WriteMax,
setprop net.tcp.buffersize.default 4096,87380,110208,4096,16384,110208
setprop net.tcp.buffersize.wifi 4095,87380,110208,4096,16384,110208
setprop net.tcp.buffersize.umts 4094,87380,110208,4096,16384,110208
setprop net.tcp.buffersize.edge 4093,26280,35040,4096,16384,35040
setprop net.tcp.buffersize.gprs 4092,8760,11680,4096,8760,11680
분석되어 리스트로 만들어진 액션들은 다음 코드들에 의하여 실행된다. (/system/core/init/main.c)
int main(int argc, char **argv)
{
action_for_each_trigger("early-init", action_add_queue_tail);
drain_action_queue();
......
/* execute all the boot actions to get us started */
action_for_each_trigger("init", action_add_queue_tail);
drain_action_queue();
......
/* 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();
}
코드를 보면 early-init, init, early-boot, boot 에 따른 액션 리스트를 실행하는데, 안드로이드에서 사용하는 init.rc에 early-init과 early-boot가 없기 때문에 init과 boot에 해당하는 액션만 실행된다.
init은 주로 전역 변수 초기화, 마운트 포인트(mount point) 생성, MTD 파티션 마운트, 기본적인 파일 시스템 구조를 생성하며,
boot는 네트웍을 초기화 하며, 접근 권한 등을 설정하고, APP group의 메모리 사용량을 설정한다.
서비스의 경우는 class_start default 로부터 정의된다.
class_start default
## Daemon processes to be run by init.
##
service console /system/bin/sh
console
# adbd is controlled by the persist.service.adb.enable system property
service adbd /sbin/adbd
disabled
# adbd on at boot in emulator
on property:ro.kernel.qemu=1
start adbd
......
service servicemanager /system/bin/servicemanager
user system
critical
onrestart restart zygote
onrestart restart media
서비스들을 정의하는 방법은 다음과 같다.
service <name> <pathname> [<argument>]
<option>
.....
<option>
즉 위의 예를 보면 servicemanager는 /system/bin/servicemanager를 실행해야 하며 user system, critical과 onstart 옵션을 가진다는 것을 알 수 있다. critical은 안드로이드의 운영 정책으로 4번 오류 발생시 reboot한다는 의미라고 한다. onrestart는 해당 서비스가 재시작되면 다음의 서비스를 다시 시작하라는 말이다. 즉 servicemanager가 재시작되면 zygote와 media도 다시 시작해야 한다.
(/system/core/init/readme.txt 참조)
서비스 리스트들은 어떻게 시작될까?
이 부분에 대해서는 다음 포스팅을 일단 참조한다. (http://andstudy.springnote.com/pages/3872497)
일단 init 프로세스가 동작하면 루프를 돌면서 restart_process(0를 호출하는데 여기에서 처리되는 것 같다. 좀 더 정리하면 좋겠지만 다른 일로 나중에 정리한다.
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;
}
#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
nr = poll(ufds, fd_count, timeout);
if (nr <= 0)
continue;
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);
}
init.rc에 의해서 수행되는 서비스는 다음과 같다.
console, abdb, servicemanager, vold, nexus(?), debuggerd, ril-daemon, zygote, media, bootsound, bootanim, dbus, hcid, hfag, hsag, installd, flash_recovery, racoon, httpd, keystore
'개발 개발 > Android' 카테고리의 다른 글
Debugging Deadlocks on Android (0) | 2012.05.24 |
---|---|
ICS에서 MTP대신 Mass Storage 추가하기 (3) | 2012.05.16 |
Android system property 개수 (0) | 2012.04.24 |
Git 사용자 설명서 (0) | 2012.03.27 |
adb를 wifi로 쓰기 (0) | 2012.03.27 |