[Android] “Running Android with low RAM” 의 Kernel Configuration

Except as noted, this content is licensed under Creative Commons Attribution 2.5. For details and restrictions, see the Content License.
 

원문: http://source.android.com/devices/low-ram.html
번역: 양정석<dasomoli@gmailREMOVETHIS.com>(근데 원래 혼자 보려던 거라 이상한 거 많음;;)

커널 설정


직접 회수(Direct reclaim)를 줄이기 위한 커널/ActivityManager 튜닝

직접 회수는 하나의 프로세스 또는 커널이 메모리의 한 페이지를 (직접 혹은 새로운 페이지 안의 폴트로 인해서) 할당하고자 하며, 커널이 모든 사용 가능한 자유 메모리를 사용하고 있을 때 일어납니다. 이것은 커널에게 하나의 페이지를 해제하는 동안 할당을 막도록 요구합니다. 결국 이것은 더티한 File-backed 페이지를 플러싱하기 위한 디스크 I/O나 lowmemorykiller가 프로세스 하나를 죽이기를 기다리는 것을 종종 요구합니다. 이것은 UI 스레드를 포함하는 어떤 스레드에 추가 I/O를 일으킬 수 있습니다.
직접 회수를 피하기 위해서 커널은 kswapd나 백그라운드 회수를 촉발시키는 워터마크를 가집니다. 이것은 페이지들을 해제하도록 시도해서 다음에 그를 할당하는 리얼 타임 스레드가 빨리 수행할 수 있도록 하는 하나의 스레드입니다.
백그라운드 회수를 촉발시키는 디폴트 임계값은 2GB 디바이스에서 2MB, 512MB 디바이스에서 636KB로, 상당히 낮습니다. 그리고 커널은 그저 수 MB의 메모리를 백그라운드 회수 시에 회수할 뿐입니다. 이것은 수 MB 이상을 빨리 할당하는 어떤 프로세스는 빨리 직접 회수를 치게 될 것이라는 것을 의미합니다.
새로운 커널에 튜닝 가능한 지원이 android-3.4 커널 브랜치에 패치 92189d47f66c67e5fd92eafaa287e153197a454f(“add extra free kbytes tunable”)로 추가됩니다. 이 패치를 디바이스의 커널로 체리픽하는 것은 ActivityManager가 커널에게 3개의 풀스크린 32 bpp 버퍼의 메모리를 자유롭게 유지하도록 할 것임을 이야기하는 것을 허용할 것입니다.
이들 임계값은 프레임워크 config.xml을 통해 설정될 수 있습니다.
<!– 커널 내에 (존재한다면) 튜닝 가능한 /proc/sys/vm/extra_free_kbytes를 셋팅하는 디바이스 설정. 높은 값은 커널이 free를 유지하는 메모리의 양을 증가시키고, 할당 시간을 줄이고, lowmemerykiller가 더 빨리 죽이도록 할 것입니다. 낮은 값은 프로세스들에 의해 더 많은 메모리를 사용하도록 하지만 디스크 I/O나 lowmemorykiller 상에서의 대기를 막는 더 많은 할당이 일어날 것입니다. 화면 크기에 기초한 ActivityManager에 의해 선택된 기본 값은 덮어씌움. 0은 커널의 기본 값을 넘는 추가 메모리를 유지하는 것을 막습니다. -1은 기본값을 유지합니다 –>
<integer name=”config_extraFreeKbytesAbsolute”>-1</integer>
<!– 커널 내에 (존재한다면) 튜닝 가능한 /proc/sys/vm/extra_free_kbytes를 조정하는 디바이스 설정. 0은 ActivityManager가 고르는 기본 값을 사용합니다. 양수 값은 커널이 해제를 유지하는 메모리의 양을 증가시키고, 할당 시간을 줄이고, lowmemorykiller 가 더 빨리 죽이도록 할 것입니다. 음수 값은 프로세스들에 의해 사용되는 메모리를 더 많이 허용하지만, 디스크 I/O나 lowmemorykiller 상의 대기를 막는 더 많은 할당이 일어날 것입니다. 화면 크기에 기초한 ActivityManager에 의해 선택된 기본 값은 직접 더해짐. –>
<integer name=”config_extraFreeKbytesAdjust”>0</integer>
LowMemoryKiller 튜닝
ActivityManager는 각 우선 순위 단계 버킷 안에서 프로세스를 실행하는데 필요한 file-backed 페이지(캐시된(cached) 페이지)의 워킹 셋의 그 예상치를 맞추기 위해서 LowMemoryKiller의 임계값을 설정합니다. 디바이스가 워킹 셋을 위한 높은 요구 사항을 갖는다면, 예를 들면, 벤더 UI가 더 많은 메모리를 요구하거나 더 많은 서비스가 추가되었다면, 임계값은 증가될 수 있습니다.
작아지고 있는 캐시 때문에 디스크 스레싱(thrashing)이 일어나기 전에 백그라운드 프로세스들이 죽여지고 있어서, 너무 많은 메모리가 file backed 페이지를 위해 예약되어 지고 있다면 임계값을 줄일 수 있습니다. 
<!– 커널 내의 lowmemorykiller 내의 튜닝 가능한 minfree를 셋팅하는 디바이스 설정. 높은 값은 lowmemorykiller가 더 일찍 켜지고, 파일 캐시 안에 더 많은 메모리를 유지하고, I/O 스레싱을 막도록 할 것이지만, 메모리 내에 더 적은 프로세스가 머물도록 할 것입니다. 낮은 값은 더 많은 프로세스가 메모리 상에 유지되지만, 너무 낮으면 스레싱을 일으킬 것입니다. 화면 크기와 가장 큰 lowmemorykiller 버킷을 위한 전체 메모리에 기초해서 ActivityManager에 의해 선택된 기본값을 덮어쓰고, 더 작은 버킷에 비례해서 크기가 조정됨. -1은 기본값을 유지 —>
<integer name=”config_lowMemoryKillerMinFreeKbytesAbsolute”>-1</integer>
<!– 커널 내의 lowmemorykiller 내의 튜닝 가능한 minfree를 조정하는 디바이스 설정. 높은 값은 lowmemorykiller가 더 일찍 켜지고, 파일 캐시 안에 더 많은 메모리를 유지하고, I/O 스레싱을 막도록 할 것이지만, 메모리 내에 더 적은 프로세스가 머물도록 할 것입니다. 낮은 값은 더 많은 프로세스가 메모리 상에 유지되지만, 너무 낮으면 스레싱을 일으킬 것입니다. 화면 크기와 가장 큰 lowmemorykiller 버킷을 위한 전체 메모리에 기초해서 ActivityManager에 의해 선택된 기본값에 직접 더해지고, 더 작은 버킷에 비례해서 크기가 조정됨. 0은 기본값을 유지 —>
<integer name=”config_lowMemoryKillerMinFreeKbytesAdjust”>0</integer>

Framework의 설정 참고 소스 코드: frameworks/base/services/java/com/android/server/am/ProcessList.java

[Linux] 새로 깔면 하는 환경 변수 셋팅

새로 깔면 하는 환경 변수 셋팅(~/.bashrc)

export ANDROID_JAVA_HOME=”/usr/lib/jvm/jdk1.6.0_38/”
export JAVA_HOME=”/usr/lib/jvm/jdk1.6.0_38/”
export PATH=”~/bin:$PATH:/usr/lib/jvm/jdk1.6.0_38/bin”

alias setcross=’export CROSS_COMPILE=”/opt/toolchains/arm-eabi-4.6/bin/arm-eabi-“‘
alias b=”. ~/bin/backdir.sh”

export USE_CCACHE=1

backdir.sh 는 [Bash] cd ../../../../.. 을 치기 귀찮을 때 유용한 스크립트에 있다

[Android] ${ro.hardware}를 사용한 하드웨어 별 init.rc 처리

안 적어 놓으니 역시나 까먹는다. 아래는 ARM 32비트에 국한한다.

커널 부팅의 마지막에 init 프로세스가 시작되면, 램디스크 안에 든 init.rc 파일을 읽어 처리하게 된다. 별 다른 처리를 하지 않았다면, 기본적으로 system/core/rootdir/init.rc 파일이 램디스크로 복사되므로 이 파일을 읽어 처리한다.
이 init.rc 파일에서는 ro.hardware 프로퍼티 값에 따라 init.${ro.hardware}.rc 파일을 읽어 처리하게 되는데 이 파일로 각 보드마다 다른 처리를 가능하게 한다.
여기서 ro.hardware 프로퍼티 값은 init(system/core/init) 안에서 다음과 같은 순서로 셋팅한다.

1. /proc/cpuinfo 를 읽어서 이 안의 “Hardware:” 뒤의 값을 소문자로 모두 바꿔 사용(system/core/init/util.c 의 get_hardware_name())
2. 부트로더에서 cmdline 으로 androidboot.hardware 값이 넘어왔다면, ro.boot.hardware 프로퍼티로 셋팅한다(system/core/init/init.c 의 import_kernel_nv()).[ro.boot.hardware 값은 꼭 부트로더에서 넘어오지 않을 수도 있다]
3. ro.boot.hardware 프로퍼티 값이 있다면, 앞에서 /proc/cpuinfo 에서 읽은 값을 오버라이드한다.

위의 순서를 거쳐 셋팅된 ro.hardware 프로퍼티를 사용해서 init.${ro.hardware}.rc 파일을 읽게 된다.

1. 의 경우를 사용할 때는 /proc/cpuinfo 값을 사용하므로, 리눅스 커널의 사용되는 struct machine_desc 안의 .name 을 사용하게 된다.
/proc/cpuinfo 에서 “Hardware: ” 를 출력하는 부분은 리눅스 커널 소스의 arch/arm/kernel/setup.c 안의 c_show() 함수를 보면 되는데, 다음과 같다.

static int c_show(struct seq_file *m, void *v)
{
        …

        seq_printf(m, “Hardware\t: %s\n”, machine_name);

        …

machine_name 은 커널 부팅 중 (arch/arm/kernel/setup.c 의) setup_arch() 에서 다음처럼 셋팅한다.

void __init setup_arch(char **cmdline_p)
{

        …
        machine_name = mdesc->name;

        …

struct machine_desc 안의 .name 은 대부분 머신 파일 안에서 MACHINE_START(_type, _name) 매크로를 통해 선언한다. _name이 .name이 된다. 그러므로 결국 _name 에 들어가는 값이 읽혀 소문자로 바뀌어 사용된다. 예를 들면, Nexus 10 디바이스의 경우 다음과 같이 arch/arm/mach-exynos/board-manta.c 안에 다음과 같이 정의되어 있다. 여기서는 “Manta” 가 소문자로 모두 바뀌어 manta 로 사용된다.

MACHINE_START(MANTA, “Manta”)
        … 
MACHINE_END


참고로, MACHINE_START 매크로는 arch/arm/include/asm/mach/arch.h 에 있는데 다음과 같다.

#define MACHINE_START(_type,_name)                      \
static const struct machine_desc __mach_desc_##_type    \
 __used                                                 \
 __attribute__((__section__(“.arch.info.init”))) = {    \
        .nr             = MACH_TYPE_##_type,            \
        .name           = _name,
#define MACHINE_END                             \
};

2. 의 경우는 cmdline 을 통해 넘어오므로, 부트로더에서 ATAG를 통해 cmdline 에 셋팅하여 넘기는 것이 가장 정상적인 방법이고, 편법으로는 cmdline 을 바꿀 수 있는 여러가지 방법(예를 들면, 커널 컴파일 타임에 셋팅한다든지, boot.img 를 만들 때 cmdline 을 설정해 준다든지 등)을 통해 셋팅할 수 있다.(아니면 아예 ro.boot.hardware 값을 직접 셋팅해도 아마?)
androidboot.hardware 값이 ro.boot.hardware 프로퍼티 값이 되는 과정은 system/core/init/init.c 의 import_kernel_nv() 함수 내에서 처리된다.

static void import_kernel_nv(char *name, int for_emulator)
{                          
    …
    if (!strcmp(name,”qemu”)) {
        strlcpy(qemu, value, sizeof(qemu));
    } else if (!strncmp(name, “androidboot.”, 12) && name_len > 12) {
        const char *boot_prop_name = name + 12;
        char prop[PROP_NAME_MAX];
        int cnt;
        
        cnt = snprintf(prop, sizeof(prop), “ro.boot.%s”, boot_prop_name);
        if (cnt < PROP_NAME_MAX)
            property_set(prop, value);
    }
}

3. 의 ro.boot.hardware 프로퍼티 값이 ro.hardware 프로퍼티 값으로 셋팅되는 과정은 system/core/init/init.c 의 export_kernel_boot_props() 에서 수행된다. 이 과정 중에 ro.boot.hardware 프로퍼티 값이 있다면, 1. 의 /proc/cpuinfo 을 통해 얻은 값을 오버라이드하게 된다.

static void export_kernel_boot_props(void)
{
    … 
    /* if this was given on kernel command line, override what we read
     * before (e.g. from /proc/cpuinfo), if anything */
    ret = property_get(“ro.boot.hardware”, tmp);
    if (ret)
        strlcpy(hardware, tmp, sizeof(hardware));

    property_set(“ro.hardware”, hardware);  

[Ubuntu 10.04] sun-java6-jdk 설치

partner repo 에서 제공하던 것이 안된다.

PPA repo 를 이용한다.

sudo apt-get install python-software-properties

sudo add-apt-repository ppa:sun-java-community-team/sun-java6

sudo apt-get update

sudo apt-get install sun-java6-jdk

sudo update-java-alternatives -s java-6-sun

Proxy 환경에서 add-apt-repository ppa:sun-java-community-team/sun-java6 에서 “couldn’t connect to host” 에러가 나는 경우 환경 변수 http_proxy, https_proxy, ftp_proxy 가 sudo 에서도 먹도록 다음을 /etc/sudoers 에 추가한다.

Defaults env_keep = “http_proxy https_proxy ftp_proxy”

참고 : http://www.bogdanovic.se/installing-oracle-sun-jdk-6-on-ubuntu

         https://help.ubuntu.com/community/AptGet/Howto#Setting_up_apt-get_to_use_a_http-proxy

[Linux Kernel] 부트로더 cmdline 확장 – CONFIG_CMDLINE_EXTEND

CONFIG_CMDLINE_EXTEND 옵션을 켜면 CONFIG_CMDLINE 의 cmdline 뒤에 부트로더에서 넘기는 cmdline 을 덧붙인다.

ARM 의 경우 arch/arm/kernel/setup.c 에 보면 다음과 같은 것들을 볼 수 있다.


static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;

static int __init parse_tag_cmdline(const struct tag *tag)
{
#if defined(CONFIG_CMDLINE_EXTEND)
        strlcat(default_command_line, ” “, COMMAND_LINE_SIZE);
        strlcat(default_command_line, tag->u.cmdline.cmdline,
                COMMAND_LINE_SIZE);
#elif defined(CONFIG_CMDLINE_FORCE)
        pr_warning(“Ignoring tag cmdline (using the default kernel command line)\n”);
#else
        strlcpy(default_command_line, tag->u.cmdline.cmdline,
                COMMAND_LINE_SIZE);
#endif
        return 0;
}


참고로 안드로이드의 경우, boot.img 에 쓰이는 cmdline 에 아무것도 없을 것이라고 기대된다. boot.img 안에 있는 cmdline 의 경우 fastboot -c 옵션을 통해 디버깅 목적으로 쓸 때 사용되곤 한다.

이 문서의 저작권은 GPL을 따른다(This document is released under the GPL license).

Linux kernel, ramdisk 사용 및 Android 빌드 시스템에서의 설정

Linux 커널의 경우 램디스크를 여러 종류를 사용하는데 이 중 initramfs/initrd 를 사용하는 경우
CONFIG_BLK_DEV_INITRD 를 설정하고, CONFIG_INITRAMFS_SOURCE 에 root 파일템이 만들어져 있는 디렉토리 경로를 써준다.
Android 빌드 시스템에서는 커널 및 램디스크를 합쳐서 boot.img 를 만들어 사용하는데,
이 때, 사용될 Kernel Command line 을 지정할 수 있다. 이 때 TARGET_NO_KERNEL은 false 여야 한다.
이는 build/core/Makefile 에서 확인할 수 있다. 

BOARD_KERNEL_CMDLINE := $(strip $(BOARD_KERNEL_CMDLINE))
ifdef BOARD_KERNEL_CMDLINE
  INTERNAL_BOOTIMAGE_ARGS += –cmdline “$(BOARD_KERNEL_CMDLINE)”
endif

이 외에도 Kernel Base address, Page Size 등도 지정할 수 있다.

BOARD_KERNEL_BASE := $(strip $(BOARD_KERNEL_BASE))
ifdef BOARD_KERNEL_BASE
  INTERNAL_BOOTIMAGE_ARGS += –base $(BOARD_KERNEL_BASE)
endif
BOARD_KERNEL_PAGESIZE := $(strip $(BOARD_KERNEL_PAGESIZE))
ifdef BOARD_KERNEL_PAGESIZE
  INTERNAL_BOOTIMAGE_ARGS += –pagesize $(BOARD_KERNEL_PAGESIZE)
endif

TARGET_BOOTIMAGE_USE_EXT2 를 true로 하면 ext2 파일시스템의 boot image를 만드는 것 같다.

ifeq ($(TARGET_BOOTIMAGE_USE_EXT2),true)
tmp_dir_for_image := $(call intermediates-dir-for,EXECUTABLES,boot_img)/bootimg
INTERNAL_BOOTIMAGE_ARGS += –tmpdir $(tmp_dir_for_image)
INTERNAL_BOOTIMAGE_ARGS += –genext2fs $(MKEXT2IMG)
$(INSTALLED_BOOTIMAGE_TARGET): $(MKEXT2IMG) $(INTERNAL_BOOTIMAGE_FILES)
    $(call pretty,”Target boot image: $@”)
    $(hide) $(MKEXT2BOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) –output $@
else # TARGET_BOOTIMAGE_USE_EXT2 != true
$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES)
    $(call pretty,”Target boot image: $@”)
    $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) –output $@
    $(hide) $(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE),raw)
endif # TARGET_BOOTIMAGE_USE_EXT2

예제)

TARGET_NO_KERNEL := false
BOARD_KERNEL_BASE := 0x30000000
BOARD_KERNEL_PAGESIZE := 4096
BOARD_KERNEL_CMDLINE := console=ttyFIQ0 no_console_suspend

[Android] lunch, mm, mmm 등 사용

빌드 시에 lunch 를 사용하래서 몬가 하고 찾아보니 여기 나온다.
http://devtcg.blogspot.com/2009/03/building-running-and-debugging-android.html
구글 애들이 mm, mmm 이런 걸 쓰던데, build/envsetup.sh 를 열어보니 거기 있었다. 오!
바빠서 파악은 못하고 일단 그런게 있다는 것만 알아둔다.

누군가 envsetup.sh에 대해서 정리해 놓은 글 : http://www.kaisyu.com/notes/google-android/android-envsetupsh
난 그냥 열어서 봤는데………… 정리해 놓은 글 보는게 더 편하구나……………….
system image를 만드는 make snod 가 가장 편리한 듯.