OneDrive를 잘 사용하고 있었는데 어느 순간부터 윈도우 기본 폴더인 바탕화면, 문서, 사진과 자동 동기화가 되면서 문제가 발생하기 시작했습니다

몇몇 게임이 기본 문서 폴더에 파일을 저장하는데 OneDrive 자동동기화가 되면서 게임 실행시 에러가 발생하네요

 

기본 폴더 동기화가 default enable로 되어 있어 발생하는 문제인데, 동기화만 끄면 OneDrive도 깔끔하게 사용 할 수 있고 게임 실행시에도 문제가 발생하지 않습니다

 

우측 하단에 있는 작업표시줄 아이콘에서 OneDrive를 찾아 우클릭 하면 아래처럼 OneDrive가 실행됩니다

 

 

여기서 설정을 선택하면 OneDrive 설정 화면이 나오고 백업 탭으로 이동하여 백업 관리를 눌러줍니다 

 

백업 관리에서 바탕화면, 문서, 사진 폴더를 모두 백업 중지 버튼을 누르면 자동 동기화가 되지 않고 기존처럼 독립적으로 사용 가능합니다

기본 폴더 동기화가 필요없는 경우에는 백업 중지를 해서 사용하시면 됩니다

나중에 동기화가 필요해지면 백업 폴더 관리에서 다시 백업 시작 버튼만 눌러주면 되는데, 저는 그렇게 사용할 일은 없을 듯 하네요


Android 부팅중 init 프로세스의 동작에 대한 설명이다.

========================================================================================================

출처 : http://blog.daum.net/baramjin/16010995

안드로이드의 초기화 과정 중 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 값만 설정된다.

 

CONFIG_CMDLINE="root=/dev/ram rw initrd=0x40600000,0x400000 init=/init console=ttySAC0 mem=200M androidboot.hardware=tcc92xx"

 

마지막으로 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

 


http://blog.daum.net/baramjin/16010995


'Platform > Android' 카테고리의 다른 글

[Android] Android 부팅  (0) 2010.03.18
[Android] Android 의 개요  (0) 2010.03.18
[Android] Boot Animation  (0) 2010.03.18
[Android] SGL  (0) 2010.03.18
[Android] Surface Flinger의 쓰레드  (1) 2010.03.18

안드로이드의 부팅 순서

========================================================================================================

출처 : http://blog.daum.net/baramjin/16010975

안드로이드는 리눅스 커널을 이용한다. 따라서 리눅스의 시작 시퀀스 이후에 안드로이드 고유 시스템이 동작하게 된다.

기본적인 동작을 잘 설명한 곳( http://samse.tistory.com/112?srchid=BR1http%3A%2F%2Fsamse.tistory.com%2F112 )이 있어서 일부 발췌해 온다.

 

 


리눅스는 기본적으로 init이 가장먼저 실행된다. init.rc 라는 이름의 파일에는 init이 해야할 작업들이 기록되어 있다. 파일시스템 마운팅, 폴더 권한설정, 캐시폴더 삭제, 시작프로그램 동작 등이 기록되어 있다. 우선 데몬을 올린다. 데몬은 init에 의해 리눅스와 같이 시작되었다가 리눅스가 종료될 때 없어지는 프로그램으로서 데몬을 작성하는 규격에 따라 만들어져야 한다. Zygote가 Dalvik을 초기화 한다. C 밑에 있는 기본라이브러리들은 런타임을 통해 실행되고 상위 서비스들은 Dalvik을 통해 실행된다. 이러한 과정들을 위한 설정은 해당하는 config 파일을 수정하면 된다. 어떤 동작들을 바꾸고 싶으면 기본적으로 init.rc를 바꾸면 되고 Zygote를 바꾸고 싶으면 그 설정파일을 바꾸면 된다. 그리고 시스템서버, 서페이스매니저, 오디오매니저들이 올라간다. 그 다음에는 시스템 서비스들이 활성화 된다. 이들은 서비스이므로 서비스매니저에 등록된다. 

 

init은 /system/core/init 디렉토리에 관련 소스가 있다. init.rc 파일은 /system/core/rootdir/에 있다.

init이 하는 일과 init.rc 파일을 작성하는 방법에 대해서는 여기( http://www.aesop.or.kr/?document_srl=46566 )를 참조한다.

  

init이 하는 일을 정리하면 다음과 같다.

1. log system 초기화

2. /init.rc와 /init.%hardware$.rc 파일 파싱

3. 2번 스텝의 2개 파일에서 초기 선동작(early-init action) 수행

4. 디바이스 종속적인 초기화 작업 (예를 들어 /dev 에 모든 디바이스 노드 만듬)

5. property system 초기화 (property system은 공유 메모리로 동작하며 윈도우의 레지스트리와 같은 역활이다)

6. 2번 스텝의 2개 파일에서 초기 동작(init action) 수행

7. property service 시작

8. 2번 스텝의 2개 파일에서 선부팅(early boot), 부팅(boot) 동작 수행

9. 2번 스텝의 2개 파일에서 property 동작 수행

10.  device/property가 /child process를 셋할때까지 대기하면서 rc 파일에 정의된 명령어를 수행한다.

 

 

위의 설명처럼 /out/target/product/XXX/init.rc 를 먼저 살펴 보자.  위에서 설명한 것처럼 폴더 권한 설정 등의 초기화 작업을 하고 나면

Demon을 올리는 작업을 하도록 되어 있다. 이를 정리하면 다음과 같다.

 

1) console : shell 을 시작 (system/core/sh/)

## Daemon processes to be run by init.
##
service console /system/bin/sh
    console

 

2) abdb : abdb daemon을 시작 (system/core/adb/)

# adbd on at boot in emulator
on property:ro.kernel.qemu=1
    start adbd

 

on property:persist.service.adb.enable=1
    start adbd

 

on property:persist.service.adb.enable=0
    stop adbd

 

3) servicemanager : binder system을 시작 (framework/base/cmds/servicemanager/)

service servicemanager /system/bin/servicemanager
    user system
    critical
    onrestart restart zygote
    onrestart restart media

 

4) vold : volume daemon 시작 (system/core/vold/)

service vold /system/bin/vold
    socket vold stream 0660 root mount

 

#service mountd /system/bin/mountd
#    socket mountd stream 0660 root mount

 

5) debuggerd : debug system을 시작 (system/debuggerd/)

service debuggerd /system/bin/debuggerd

 

6) rild : radio interface layer daemon을 시작 (hardware/ril/rild/)

service ril-daemon /system/bin/rild
    socket rild stream 660 root radio
    socket rild-debug stream 660 radio system
    user root
    group radio cache inet misc

 

7) Zygote : (frameworks/base/cmds/app_process/)

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    socket zygote stream 666
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on

 

8) media : AudioFlinger, MediaPlayerService, CameraService를 시작 (frameworks/base/media/mediaserver/)

service media /system/bin/mediaserver
    user media
    group system audio camera graphics inet net_bt net_bt_admin

 

관련 코드를 보면 굉장히 간단하다. (framework/base/media/mediaserver/main_mediaserver.cpp)

int main(int argc, char** argv)
{
    sp<ProcessState> proc(ProcessState::self());
    sp<IServiceManager> sm = defaultServiceManager();
    LOGI("ServiceManager: %p", sm.get());
    AudioFlinger::instantiate();
    MediaPlayerService::instantiate();
    CameraService::instantiate();

    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
}

 

9) bootsound : boot sound (system/media/audio/ui/boot.mp3)를 재생, (그런데 이 바이너리는 실제 찾을 수 없네...?. 그리고 샘플 파일도 안 만들어 지네)

service bootsound /system/bin/playmp3
    user media
    group audio
    oneshot

 

10) dbus : dbus daemon 시작 (BlueZ 즉 Bluetooth 관련 서비스) (external/dbus/bus/)

service dbus /system/bin/dbus-daemon --system --nofork
    socket dbus stream 660 bluetooth bluetooth
    user bluetooth
    group bluetooth net_bt_admin

 

11) hcid : hcid의 stdout, stdin을 안드로이드 logging sytstem으로 redirect (external/bluez/hcid/)

service hcid /system/bin/hcid -s -n -f /etc/bluez/hcid.conf
    socket bluetooth stream 660 bluetooth bluetooth
    socket dbus_bluetooth stream 660 bluetooth bluetooth
    # init.rc does not yet support applying capabilities, so run as root and
    # let hcid drop uid to bluetooth with the right linux capabilities
    group bluetooth net_bt_admin misc
    disabled

 

12) hfag : Bluetooth handsfree audio gateway 시작 (BlueZ 만 사용) (external/bluez/utils/tools/)

service hfag /system/bin/sdptool add --channel=10 HFAG
    user bluetooth
    group bluetooth net_bt_admin
    disabled
    oneshot

 

13) hsag : Bluetooth headset audio gateway 시작 (BlueZ 만 사용) (external/bluez/utils/tools/)

service hsag /system/bin/sdptool add --channel=11 HSAG
    user bluetooth
    group bluetooth net_bt_admin
    disabled
    oneshot

 

14) install : install package daemon 시작 (frameworks/cmds/installd/)

service installd /system/bin/installd
    socket installd stream 600 system system

 

15) flash_recovery : /system/recovery.img를 로드 (bootable/recovery/mtdutils/)

service flash_recovery /system/bin/flash_image recovery /system/recovery.img
    oneshot 

 

  

강조한 것처럼 바와 같이 servicemanager을 이용해서 zygote와 media를 시작하고 SD 카드와 USB의 mount 등을 처리하는 vold 데몬을 올린다. 이후 부팅 사운드를 재생하거나 다른 필요한 데몬들을 올리게 된다. 이후 다시 강조한 부분을 보면 zygote를 이용해서 system server 를 시작한다. 이후 media를 다시 시작한다(?)

 

안드로이드의 system server는 Native Service와 여러가지 기본 서비스(core system service 라고도 한다)들로 이루어진다.

 

/framework/base/cmds/system_server/library/system_init.cpp 를 보면 system_init() 함수가 있다. 이 파일은 실제 사용한다기 보다는 초기화 흐름을 알게 해주는 코드로 보인다.

 

extern "C" status_t system_init()
{
    LOGI("Entered system_init()");
    
    sp<ProcessState> proc(ProcessState::self());
    
    sp<IServiceManager> sm = defaultServiceManager();
    LOGI("ServiceManager: %p\n", sm.get());
    
    sp<GrimReaper> grim = new GrimReaper();
    sm->asBinder()->linkToDeath(grim, grim.get(), 0);
    
    char propBuf[PROPERTY_VALUE_MAX];
    property_get("system_init.startsurfaceflinger", propBuf, "1");
    if (strcmp(propBuf, "1") == 0) {
        // Start the SurfaceFlinger
        SurfaceFlinger::instantiate();
    }

    // On the simulator, audioflinger et al don't get started the
    // same way as on the device, and we need to start them here
    if (!proc->supportsProcesses()) {

        // Start the AudioFlinger
        AudioFlinger::instantiate();

        // Start the media playback service
        MediaPlayerService::instantiate();

        // Start the camera service
        CameraService::instantiate();
    }

    // And now start the Android runtime.  We have to do this bit
    // of nastiness because the Android runtime initialization requires
    // some of the core system services to already be started.
    // All other servers should just start the Android runtime at
    // the beginning of their processes's main(), before calling
    // the init function.
    LOGI("System server: starting Android runtime.\n");
    
    AndroidRuntime* runtime = AndroidRuntime::getRuntime();

    LOGI("System server: starting Android services.\n");
    runtime->callStatic("com/android/server/SystemServer", "init2");
        
    // If running in our own process, just go into the thread
    // pool.  Otherwise, call the initialization finished
    // func to let this process continue its initilization.
    if (proc->supportsProcesses()) {
        LOGI("System server: entering thread pool.\n");
        ProcessState::self()->startThreadPool();
        IPCThreadState::self()->joinThreadPool();
        LOGI("System server: exiting thread pool.\n");
    }
    return NO_ERROR;
}

 

 

코드를 보면 알겠지만 크게 다음 순서로 초기화 과정이 이루어진다.

1. Native Service 초기화

SurfaceFlinger

AudioFlinger

MediaPlayerService

CameraService

 

2. Android RunTime 시작

SystemServer

 

안드로이드가 동작하기 위한 여러가지 기본 서비스들(또는 JAVA 서비스)을 시작하는 곳이 SystemServer이다.

 

파일 위치는 다음과 같다.

 

/frameworks/base/services/java/com/android/server/SystemServer.java

 

SystemServer 클래스는 init2() 함수를 호출하게되면 ServerThread를 동작시키고 여기에서 다양한 서비스들을 시작한다.

서비스 시작하는 순서대로 대충 정리하면 다음과 같다.(run 함수의 내용을 그냥 정리한 것이며 * 표시 없는 것은 주서비스이고 * 표시 있는 것은 부가 서비스이다)

 

Power Manager 시작

Activity Manager 생성

 

ActivityManager를 생성해서 context를 받으면 이후 서비스들에는 context를 전달한다.

(아직 이 구조는 잘 모르겠지만 서비스들에서 설정이나 특정 이벤트들을 브로드 캐스팅하기 위해서는 context를 이용해야 하는 것 같다. ActivityManger에서 이를 위한 초기화 과정이 이루어지고 이후 서비스들은 context를 가지고 서로간의 통신이나 Focus 이동이 생길 수 있도록 하고 있는 것 같다. 이것은 아직은 어디까지나 추측이다.)

 

Telephony Registry 시작

Package Manager 시작

System Process로 Activity Manager 설정

Context Manager 시작

System Context Providers 시작

Battery Service 시작

* Hardware Service 시작

Alarm Manager 시작

Sensor Service 시작

 

Window Manager 시작

 

Bluetooth Service 시작

BluetoothDeviceService

BluetoothA2dpService

 

*Status Bar Service 시작

ClipboardService

InputMethodManagerService

 

*NetStat Service 시작

*Connectivity Service 시작

 

*Notification Manager 시작

Mount Service 시작

*DeviceStorageMonitor Service 시작

 

*Location Manager 시작

*SearchManager Service 시작

 *Clipboard Service 시작

*Fallback Checkin Service 시작

*WallpaperService 시작

*Audio Service 시작

*Headset Observer 시작

(?)AppWidgetService

 

*AdbSettingsObserver 시작

(?)HDMISettingsObserver 시작

  


http://blog.daum.net/baramjin/16010975

'Platform > Android' 카테고리의 다른 글

[Android] Init 프로세스 동작  (0) 2010.03.18
[Android] Android 의 개요  (0) 2010.03.18
[Android] Boot Animation  (0) 2010.03.18
[Android] SGL  (0) 2010.03.18
[Android] Surface Flinger의 쓰레드  (1) 2010.03.18

안드로이드의 개요이다.

기본부터 해야하는데 난 이상하게 시작한거 같다. 

========================================================================================================


안드로이드에서 사용하는 언어는 자바이지만 SUN의 자바와 API와 버추얼머신이 다르다. 
Dalvik 이라는 회사의 버추얼머신이 있었는데 구글에서 회사를 통째로 사들였다고 한다. 이때문에 안드로이드는 SUN의 라이센스로부터 자유롭다. 



안드로이드 플랫폼은 크게 네 부분으로 나뉜다. 커널부분, 하드웨어추상레이어, 라이브러리, 응용프로그램 프레임워크. 라이브러리들은 C/C++로 구현이 되어 있다. 기존의 여러 프로젝트 들을 통합하였기 때문에 C로 구현된 것들이 대부분이고 C++도 있다. 응용프로그램 프레임워크는 자바로 구현되어 있다. 프레임워크와 라이브러리 사이에는 자바와 C 사이의 서로다른 호출 규약등을 맵핑하는 JNI라는 마셜러가 존재한다. 하드웨어 드라이버를 C로 만들면 JNI를 구현해야 한다. 이를 위한 구글 가이드가 있고 샘플도 공개되어 있다. 그리고 SDK를 생성하여 응용프로그램 개발자에게 전달하면 된다. 하드웨어 부분은 구글에서 표준으로 정해두었기 때문에 따로 만질 필요가 없다. 추상레이어도 스펙이 다 되어있어 응용프로그램을 개발할 때는 API로 보이게 된다. 
 기존 임베디드리눅스와 안드로이드의 차이점중 한가지는 장치드라이버들( 그래픽, 오디오, 카메라 등 )이 커널영역이 아닌 유저모드에서 동작한다. 유저모드로 동작 시키면 자신에게 할당되어 있는 번지에만 접근할 수 있기 때문에 조금 더 안전하지만 하드에어 제어시에는 MMU등을 참조하는 등의 추가적인 작업이 필요하기 때문에 조금 불편할 수 있다. 이렇게 드라이버들을 유저영역에 넣은 이유는 라이센스문제, 안정성, 문서화의 편리성( 커널버전이 업데이트 될 때마다 드라이버에 대한 문서를 갱신할 필요가 없다 ), 플러그인 방식으로 드라이버 배포가 가능한점이 있다. 안드로이드에서는 기존 리눅스의 라이브러리로 만든 것들은 동작하지 않는다고 보면 된다. 같은 이름의 라이브러리라 할지라도 안드로이드의 라이브러리들은 스마트폰에 탑재하는 것을 기준으로 불필요한 코드들을 제거하고 최적화를 해두었기 때문이다. POSIX Thread 관련 라이브러리를 예로 들면 create, join 등의 필수 함수를 제외한 나머지함수는 모두 제거되어있다. 

Android Kernel

왜 안드로이드는 리눅스 커널 기반일까. 생각하기 나름이겠지만 우선 오픈소스이고 안전하게 입증된 드라이버 모델(well abstracted )이 적용되어 있고, 메모리 프로세스 관리 모델을 제공하고, 하드웨어와 입출력에 대한 보안모델을 제공하고, 공유라이브러리를 지원하는 등 비용과 시간이 적게 드는 이유가 한몫 한것 같다. 

현재 커널 안정화버전은 2.6.27 이다. 구글에서 순수 리눅스 커널을 가져다가 안드로이드 용으로 패치한 것이다. 기존의 리눅스와는 다르게 GLIBC를 지원하지 않고 EABI( Embedded Arm Binary Interface?)와 Binder를 사용한다. 그리고 커널 기능 향상을 위해 Alarm, Ashamem(공유메모리), Low memory-killer, debugger, logger 컴포넌트가 추가되었다. 

유저모드는 허가된 자원에만 접근이 가능하고 프로그램의 안정성을 높일 수 있지만 하드웨어 효율성은 떨어질 수 있다. 반대로 커널모드는 하드웨어 효율성은 높지만 안정성은 떨어질 수 있다. 안드로이드는 기본 드라이버들을 제외한 나머지 드라이버들은 유저스페이스에서 동작한다. 일반 PC는 사람의 생명을 빼았지는 않지만, 심장박동 컨트롤러, 핸드폰만 가지고 조난 당했을 경우와 같이 스마트폰을 모델로 하면 생명과 연결될 수 있기 때문에 안정성이 속도나 효율보다 더 중요하다. 
 
이전에는 일반적으로 일반 gcc를 썼지만 최근의 리눅스 커널을 빌드할 경우에는 ARM사에서 표준으로 정해둔 ABI( arm binary interface ) 규격의 컴파일러를 사용한다. 현재 안드로이드는 EABI( Extended??..) 기능을 지원한다. 일반 컴파일러에서 빌드한 것을 EABI에서 실행할 수 없기 때문에 EABI 컴파일러로 다시 빌드해야 한다. 최근의 임베디드 컴파일러는 대부분 EABI컴파일러 이다. 실수 연산방식에 있어서 하드플로팅포인트와 소프트웨어 플로팅포인트를 혼합하여 빌드할 수 있다. 기존의 ABI는 빌드옵션에서 VFP(Vector Floating Point)를 선택하거나 해제할 수 있었는데 이는 환경이 달라지면 다시 빌드 해야하는 단점이 있었다. EABI는 혼용이 되기 때문에 빌드의 부담이 줄어든다. 또한 구조체 팩킹이 편해졌고 호환성이 증가하였다. 그리고 syscall 규약에 변경사항이 있다. 이전에는 전달인자 4개까지는 레지스터를 쓰고 5개부터는 스택을 사용했는데 레지스터를 r0, r1, r2, r4 처럼 연속된 순서로 했었다.  EABI는 레지스터 간격이 달라지는등 호출 규약이 달라졌다. 

Kernel Enhancement

기존의 임베디드 리눅스에 없던 안드로이드만의 향상된 기능에는 알람, 로우메모리킬러, 공유메모리드라이버, 커널디버거, 바인더, 파워매니지먼트, 로거가 있다. 결국 포팅 이슈는 이것들과 관련될 것이다. 스마트폰으로 만들다 보니까 알람이 기본적으로 제공되어야 한다. 포팅 시 이 기능을 사용하고 싶지 않다면 다른기능과 맞물려 있기 때문에 확인해보고 작업을 진행해야 한다. 공유메모리드라이버는 안드로이드에서 커스터마이징을 했고 메모리 할당이 필요하면 전부 ashmem에 있는 라이브러리를 이용한다. 
리눅스에서는 각 프로세스마다 우선순위를 주고 메모리가 부족하면 우선순위가 낮은 것을 없앤다. 안드로이드는 프로세스마다 우선순위를 주지 않고 그룹마다 우선순위를 준다. 그 이유는 바인더 때문이다.  메모리 부족 시 우선순위가 낮은 그룹을 제거한다. 안드로이드에는 프로그램 종료 기능이 없다. 화면은 내릴 수 있지만 알아서 프로그램이 제거되기 때문이다. 자바로 응용프로그램을 만들기 때문에 가비지컬렉터가 있다. 종료버튼이 있다 하더라도 UI가 없어질 뿐 그 자체가 바로 종료되는 것은 아니다.
이중 가장 중요한 것은 바인더라고 할 수 있다. 바인더에 의해 안드로이드와 기존 리눅스의 차이가 생긴다. 커널버전 2.6.30이상부터는 커널과 안드로이드가 통합된다는 말이 있는데 2.6.29이전의 커널은 바인더폴트( 메모리 회수에 문제가 있는 버그)가 있었다. 그럼 기존에 나왔던 안드로이드폰들도 이 버그가 있었을까? 아마도 개발인력이 많기 때문에 자체적으로 해결했을 것 같다. 바인더 서비스를 넣으면 기존의 전통적인 커널구조가 객체지향 구조로 바뀐다. 컴포넌트 중심으로 양쪽간 메시지를 주고받는 구조로 바뀐다는 말이다. 이것의 장점은 한쪽이 잘못 되어도 반대쪽까지 잘못되지는 않는다는 점. 응답을 못받거나 결과만 이상하게 나올 뿐이다. 
전원관리기능은 기존의 리눅스가 가지고 있는 3단계에 2개를 더해 5단계로 이루어진다. 안드로이드 커널을 설정할때는 자신의 하드웨어에 전원관리 모듈이 없어도 반드시 포함시켜야 한다. 포팅을 하게 되면 시간이 지날수록 이것들에 집중해야 할 것이다. 

Binder


바인더는 프로그램과 데이터를 공유하기 위한 것이다. 기존의 IPC는 보안이슈와 오버헤드이슈가 있다. 리눅스에서도 오픈바인더를 쓸 수 있지만 안드로이드에서 더 활성화를 시켜 두었다. 2.6.29에 기존 버그들이 완벽하게 패치되었다. 이전의 방식은 커널을 통해서 데이터를 공유하였다.(커널의 공유메모리 영역에 데이터를 올려두고 시그널등의 이벤트를 받아 메시지 큐 등을 써서 - 세마포어, 뮤텍스, 스레드 등 ). 바인더는 커널쪽의 메인호스팅이 없고 바인드라는 서비스가 있는 것이다. 바인드를 호출하면 응용프로그램과 디바이스쪽에 바인드서비스가 붙어 서로 통신하게 된다. 바인드서비스는 커널이 아니다. 서로다른 서비스간, 응용프로그램간 데이터를 주고받을 때 동작하게 된다. 여기서의 단점은, 기존의 방식은 하나의 매개체가 있고 공유해서 썼지만 바인드는 각각의 서비스마다 바인더가 있어야 하기 때문에 메모리 낭비가 되는 측면이 있다. 그럼에도 불구하고 쓰는 이유는 바인더는 각각 별개로 동작하기 때문에 주고 받는 통신을 가로챔 당할 가능성이 더 낮아지므로 보안성이 더 오르기 때문이다. 메모리 낭비를 줄이기 위해 바인더를 230kb정도의 크기로 최소화 시켰다. 바인더는 원래 PalmOS에 있던 것이기 때문에 라이센스가 있다. 약 3000라인정도 된다. 
바인더는 스레드 풀을 유지한다. 드라이버, 매니저 서비스마다 바인더가 양쪽에 붙게 되는데 풀로 관리한다는 것은 요구가 있기 전에 미리 자료구조화 시켜 자원을 확보해 두고 바로 맵핑해서 쓸 수 있도록, 바인드 하는데 시간이 걸리지 않도록 되어 있다.  응용 A가 B 디바이스드라이버와 통신을 하게 되면 채널이 생성되는데 만약 채널안에 연결되는 프로그램이 A 이외에 C도 존재 한다면 B가 종료되었다고 해서 바인더가 종료되면 안된다. 이를 위해 참조계수가 있어서 참조계수가 0이 되면 바인드서비스를 해제한다. 

Low Memory Killer
리소스가 부족할 때 자동으로 실행된다. 안드로이드는 그룹 당 우선순위를 주어 해당되는 그룹을 한번에 해제한다. 그룹은 참조계수랑 연관이 있다.  ( 아마도 참조계수가 0 인 것들이 먼저 해제 될듯. ) 
아래 그림은 Low Memory Killer 소스의 일부분이다. 프로세스가 실행되면 프로세스테이블에 등록되어 관리가 된다. 메모리 부족현상이 생기면 shirink 함수가 실행되어 링크드리스트로 되어있는 프로세스컨트롤 블록들을 끝까지 탐색한다. 제거되어야 할 프로세스가 있으면 SIGKILL을 전송한다. 


Power Management 
기존 리눅스드라이버의 파워매니저에 계층을 하나 더 올렸다. 보통 핸드폰은 5단계를 사용한다. 키보드-액정-CPU 순으로 전원이 차단되면서 제일 마지막은 D램에만 전원을 공급하는 상태가 된다. 전원은 CPU가 아닌 PMIC라는 전원공급칩이 제어를 한다. D램에만 전원이 공급되는 상태라도 터치스크린은 잠기지 않는다. 이 상태에서 발생하는 인터럽트도 PMIC가 관리한다. 

Native Libraries

기존의 라이브러리를 그대로 사용하지 않고 EABI로 빌드된 라이브러리이다. 기존의 리눅스는 PC가 모태이기 때문에, 스마트 디바이스환경을 위해 만들어진 것이 아니기 때문에 arm또는 스마트디바이스에 최적화 되어 있지 않다. 바인더가 있고 자바의 JNI를 통해 C코드와 연결되기 때문에 필수적으로 사용되는 C라이브러리 사이즈를 줄이고 효율을 더 좋게 하였다. 안드로이드의 네이티브 라이브러리는 Bionic Lib이라고 부른다. 임베디드 리눅스쪽 라이브러리를 안드로이드로 가져오면 동작하지 않는다. 대부분이 BSD 라이센스기 때문에 코드공개의 의무가 없다. 그리고 프로세스마다 로드 되므로 작은 사이즈( libc.so 사이즈 231Kb )로 되어 있고 경량 pthread가 구현되어 있다. 모든 Native 코드는 bionic과 함께 컴파일 되어야 한다. 웹킷은 오픈소스 브라우저 기반이다. 애플의 사파리, 노키아의 심비안에 이미 적용되어 성능은 검증되어 있다. 브라우저 속도가 아주 빠른것이 특징이다. HTML 표준을 따르고 Active X는 지원하지 않는다. 미디어프레임워크는 PocketVideo OpenCORE 플랫폼 기반이다. 동영상 디코딩을 하며 표준 Video, Audio, Still-frame 포맷을 지원한다. 이를 이용해 상용제품을 양산할 경우 코덱 라이센스에 대한 비용이 발생할 수 있다. SQLite 는 기본 데이터 베이스이다. Mysql과 거의 유사하다. 위치기반 서비스등을 할 때 유용하게 쓰일 수 있다. 
Surface Manager는 모든 응용프로그램의 surface rendering을 프레임버퍼로 전달한다. 프레임버퍼는 LCD와 CPU속도에 차이가 있기 때문에  DRAM 또는 SRAM에 똑같은 구조를 만들어 두고 메모리 블록 전체를 복사해서 한번에 LCD에 출력한다. 이 때 그 메모리 공간을 프레임버퍼라고 한다. 기존의 임베디드리눅스는 2D, 3D를 따로 처리했지만 이 경우에는 동시에 2D, 3D를 처리한다. 화면 합성이나 결합, 반투명 효과등을 한번에 처리할 수 있다. 2D는 단일버퍼로 충분하지만 3D는 데이터량이 많아 단일버퍼로는 병목현상이 생길 수 있기 때문에 프레임버퍼를 더블프레임을 쓴다.  기존의 버퍼 사이즈를 두배로 늘려주면 된다. 더블버퍼링을 2D에도 적용하면 전경그림과 배경그림을 별도로 관리할 수 있어 오버헤드가 줄어든다. 

Audio Manager는 오디오 처리를 한다. 오디로 출력 라우팅 기능이 구현되어 있다. 이전에는 OSS를 사용했는데 안드로이드에서 제대로 동작하지 않기 때문에 ALSA를 써야한다. 기본적으로 ALSA는 디폴트 볼륨이 0 으로 설정되어 있기 때문에 테스트를 하기 위해서는 init 부분에서 볼륨설정을 먼저 해줘야 한다. 

Android Runtime
 

SUN의 자바는 명령들이 전부 8비트 길이를 가지지만 안드로이드는 4바이트이다. 기존의 SUN은 명령어가 스택에 들어가기 때문에 PUSH,POP명령어를 쓰고 Dalvik은 뱅크드레지스터를 이용한 복사명령을 이용하고 레지스터에서 바로 실행시키기 때문에 속도가 더 빠르다. 4바이트가 레지스터에 전부 들어가기 때문에 낮은사양에서도 느려지지 않는 효과도 있다. 프로그램동작은 자바코드이고 드라이버들은 대부분 C/C++이지만 그 사이에 JNI가 있기때문에 동작이 가능하다. JNI는 자바코드에서 C나 C++  라이브러리를 호출할 수 있도록 만들어진 규약이다. 안드로이드에서 응용프로그램은 C/C++로도 만들 수 있다. 대신 UI를 가지기는 힘들다. 백그라운드 서비스를 제작할 경우 굳이 자바로 할 필요는 없다.

HAL ( Hardware Abstraction Layer )

예전에는 하드웨어 드라이버를 하드웨어 제작자가 만들었지만 요즘은 추상계층을 두어 상위 드라이버나 하위 네이티브 드라이버를 서로 독립적으로 개발할 수 있고 응용프로그램도 독립적으로 동작할 수 있다. 이는 일관된 함수 이름과 형식이 있기때문에 가능하다. 개발자가 구현하기 쉽게 표준화된 API들이 존재하며 모든 제조사가 자신의 컴포넌트를 안드로이드 플랫폼에 넣을 수 있도록 구성되었다. HAL은 라이센스문제를 피하고 안정성을 위해 유저스페이스에 존재한다. 

Application Framework
액티비티 매니저는 응용프로그램의 생명주기를 담당한다. 패키지 매니저는 시스템에서 동작중인 응용프로그램들의 정보를 담당한다. 윈도우 매니저는 모든 응용프로그램과 관련된 화면을 담당한다. 뷰 시스템은 표준 위젯을 담당한다. 처음 바탕화면이 위젯이다. 윈도우는 dll 파일이 많지만 안드로이드는 하나의 패키지 파일로 되어있어 프로그램 배포가 쉽다. 

Bootup Sequence

리눅스는 기본적으로 init이 가장먼저 실행된다. init.rc 라는 이름의 파일에는 init이 해야할 작업들이 기록되어 있다. 파일시스템 마운팅, 폴더 권한설정, 캐시폴더 삭제, 시작프로그램 동작 등이 기록되어 있다. 우선 데몬을 올린다. 데몬은 init에 의해 리눅스와 같이 시작되었다가 리눅스가 종료될 때 없어지는 프로그램으로서 데몬을 작성하는 규격에 따라 만들어져야 한다. Zygote가 Dalvik을 초기화 한다. C 밑에 있는 기본라이브러리들은 런타임을 통해 실행되고 상위 서비스들은 Dalvik을 통해 실행된다. 이러한 과정들을 위한 설정은 해당하는 config 파일을 수정하면 된다. 어떤 동작들을 바꾸고 싶으면 기본적으로 init.rc를 바꾸면 되고 Zygote를 바꾸고 싶으면 그 설정파일을 바꾸면 된다. 그리고 시스템서버, 서페이스매니저, 오디오매니저들이 올라간다. 그 다음에는 시스템 서비스들이 활성화 된다. 이들은 서비스이므로 서비스매니저에 등록된다. 

Bootup Sequence - Zygote
Zygote가 실행되면 시스템 서비스가 활성화 된다. 응용프로그램에서는 android.process.* 을 가지고 접근할 수있다. Zygote와 시스템서버간에는 IPC 소켓으로( 127.0.0.x ) 통신을 한다. 

< 부팅 완료 후 각 프로세스들의 상태 >
Android Internals Reference
http://code.google.com/intl/ko/android/
http://groups.google.com/group/android-internals
http://www.android-internals.org/
http://groups.google.com/groups/profile?enc_user=_EKOshMAAADzFnauhYxa0ga8JtF8CI5fWMj6vob75xS36mXc24h6ww
http://groups.google.com/groups/profile?enc_user=lYDbNxEAAAD8uJiqPP7Wd-bc9b1O3waCkdEasx1kiYTQavV7mdW13Q

'Platform > Android' 카테고리의 다른 글

[Android] Init 프로세스 동작  (0) 2010.03.18
[Android] Android 부팅  (0) 2010.03.18
[Android] Boot Animation  (0) 2010.03.18
[Android] SGL  (0) 2010.03.18
[Android] Surface Flinger의 쓰레드  (1) 2010.03.18

Boot Animation 변경 방법

========================================================================================================
출처 : http://blog.daum.net/baramjin/16010965

사실 Surface Flinger 응용이라고 했지만 맞는 표현은 아닌것 같다.

 

BootAnimation은 Surface Flinger가 생성되고 가장 먼저 동작하는 것이다. 당연히 아직 안드로이드 플랫폼의 어플리케이션이 동작하지 않은 상태에서 초기화 과정 중에 화면을 표시하는 영역이라서 한번 쯤 봐두어야 하는 부분이다.

 

단 이 부분에서 Surface Flinger와 관련된 전체적인 내용이 있다고는 생각되지 않는다. (도리어 EGL 초기화 과정이 한번 더 반복되고 OpenGL 사용 예가 나온다는 말이 맞다.)

 

Surface Flinger 에서 초기화를 위하여 사용하는 클래스가 DisplayHardware 이고 여기서는 surface를 생성하기 위하여 EGLDisplaySurface 클래스를 생성한다(여기서 Fb 디바이스 드라이버가 열리고, Frame Buffer 관련된 정보를 얻는다.) 이미 이런 초기화 작업이 이루어 졌기 때문에 BootAnimation에서 Fb 디바이스 드라이버 관련된 내용은 없고 새로운 surface를 EGLNativeWindowSurface로 생성한다.

 

생성자에서 보면 composer를 받아서 binding 하는 과정만 있다 (관련 정보는 mSession에 저장된다) 사실 binding이라는 의미는 아직도 낯설다.

BootAnimation::BootAnimation(const sp<ISurfaceComposer>& composer) :
    Thread(false) {
    mSession = SurfaceComposerClient::clientForConnection(
            composer->createConnection()->asBinder());
}

 

초기화 과정을 보면 다음과 같다.

 

status_t BootAnimation::readyToRun() {
    mAssets.addDefaultAssets();

 

    DisplayInfo dinfo;
    status_t status = session()->getDisplayInfo(0, &dinfo);
    if (status)
        return -1;

 

    // create the native surface
    sp<Surface> s = session()->createSurface(getpid(), 0, dinfo.w, dinfo.h,
            PIXEL_FORMAT_RGB_565);
    session()->openTransaction();
    s->setLayer(0x40000000);
    session()->closeTransaction();

 

    // initialize opengl and egl
    const EGLint attribs[] = { EGL_RED_SIZE, 5, EGL_GREEN_SIZE, 6,
            EGL_BLUE_SIZE, 5, EGL_DEPTH_SIZE, 0, EGL_NONE };
    EGLint w, h, dummy;
    EGLint numConfigs;
    EGLConfig config;
    EGLSurface surface;
    EGLContext context;
    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    eglChooseConfig(display, attribs, &config, 1, &numConfigs);

    mNativeWindowSurface = new EGLNativeWindowSurface(s);
    surface = eglCreateWindowSurface(display, config, 
            mNativeWindowSurface.get(), NULL);

 

    context = eglCreateContext(display, config, NULL, NULL);
    eglQuerySurface(display, surface, EGL_WIDTH, &w);
    eglQuerySurface(display, surface, EGL_HEIGHT, &h);
    eglMakeCurrent(display, surface, surface, context);
    mDisplay = display;
    mContext = context;
    mSurface = surface;
    mWidth = w;
    mHeight = h;
    mFlingerSurface = s;

 

    // initialize GL
    glShadeModel(GL_FLAT);
    glEnable(GL_TEXTURE_2D);
    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

    return NO_ERROR;
}

 

초기화 과정을 보면 DisplayHardware 에서 했던 EGL 초기화와 같다. (위에서 말한 것처럼 다른 클래스를 이용하여 Fb 디바이스 드라이버를 오픈하지 않는 것만 다르다)

 

BootAnimation에서 일단 EGL 초기화가 되었으면 쓰레드에서 일정 시간 단위로 로고를 변화시켜주는 역활이 다이다.

이 부분은 대부분 OpenGL API 로 구현되어 있기 때문에 대충 이해하면 될 것 같고 하나의 준비가 되면 버퍼 스왑하는 과정이 보여진다.

 

bool BootAnimation::android() {
    initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
    initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");

 

    // clear screen
    glDisable(GL_DITHER);
    glDisable(GL_SCISSOR_TEST);
    glClear(GL_COLOR_BUFFER_BIT);
    eglSwapBuffers(mDisplay, mSurface);

 

    const GLint xc = (mWidth  - mAndroid[0].w) / 2;
    const GLint yc = (mHeight - mAndroid[0].h) / 2;
    const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h);

 

    // draw and update only what we need
    mNativeWindowSurface->setSwapRectangle(updateRect.left,
            updateRect.top, updateRect.width(), updateRect.height());

 

    glEnable(GL_SCISSOR_TEST);
    glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(),
            updateRect.height());

 

    // Blend state
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

 

    const nsecs_t startTime = systemTime();
    do {
        nsecs_t now = systemTime();
        double time = now - startTime;
        float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w;
        GLint offset = (1 - (t - floorf(t))) * mAndroid[1].w;
        GLint x = xc - offset;

 

        glDisable(GL_BLEND);
        glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);
        glDrawTexiOES(x,                 yc, 0, mAndroid[1].w, mAndroid[1].h);
        glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h);

 

        glEnable(GL_BLEND);
        glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);
        glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h);

        eglSwapBuffers(mDisplay, mSurface);
        
        // 12fps: don't animate too fast to preserve CPU
        const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);
        if (sleepTime > 0)
            usleep(sleepTime); 
    } while (!exitPending());

 

    glDeleteTextures(1, &mAndroid[0].name);
    glDeleteTextures(1, &mAndroid[1].name);
    return false;
}

 

흠 정리하고 보니 사실 Surface Flinger와 관련된 내용은 초기에 SurfaceComposerClient의 API로 session을 구성하는 것은 관련이 있지만 대부분은 EGL 초기화와 Buffer Swap 관련된 내용이 대부분이다.

 

단 BootAnimation::android()에서 png 파일을 바꾸면 다른 부팅 화면을 구현할 수 있다는 정도 정리한 것에 만족해야 할 것 같다.


http://blog.daum.net/baramjin/16010965

'Platform > Android' 카테고리의 다른 글

[Android] Android 부팅  (0) 2010.03.18
[Android] Android 의 개요  (0) 2010.03.18
[Android] SGL  (0) 2010.03.18
[Android] Surface Flinger의 쓰레드  (1) 2010.03.18
[Android] Surface Flinger와 초기화 과정  (0) 2010.03.18

SGL 이 무엇인지 이 글을 읽고 처음 알게 되었다.
해야할건 많은데 어디서부터 해야할지 잘 모르겠다. 

========================================================================================================

출처 : http://blog.daum.net/baramjin/16010901

안드로이드의 기본 구조를 보면 library에 SGL이란 것이 있다. 

 


SGL은 "Scalable Graphic Library"의 약자로 안드로이드에서 Low Level Graphic (2D Graphic)을 담당하고 있는 것 같다. native code로 rendering (vector graphic rendering)을 담당하고 있다고 하는데 프레임워트의 High Level Graphic이라고 하는 Window Manager나 Surface Manager와 연결되어 있는 것 같다.


솔직히 Nucleus 기반의 프로젝트에서 GDI API를 만들기도 해서 이부분이 상당히 궁금하다.

Web에서 검색해보면 Skia란 곳에서 개발되었고 2005년에 Google이 합병했다고 하는 것 외에 거의 알려진 정보가 없다.


일단 적용된 프로젝트는 안드로이드와 크롬(Chrome)인 것 같다.


기본 기능은 텍스트, 기하학(Geometries) 모형, 이미지 그리는 2D 라이브러리이고, 다음 기능을 지원하는 것 같다.

  • 3x3 matrices w/ perspective
  • antialiasing, transparency, filters
  • shaders, xfermodes, maskfilters, patheffects


일단 관련 라이브러리는 libsgl.so 이다.


공식적인 위치는 


http://code.google.com/p/skia/


크롬이 발표되면서 관련 소스가 공개되었다. 관련 포스팅은 


http://www.kandroid.org/board/board.php?board=kandroidshow&command=body&no=25

http://www.atoker.com/blog/2008/09/06/skia-graphics-library-in-chrome-first-impressions/


시간 나면 한번 분석해 봐야 겠다.


http://blog.daum.net/baramjin/16010901

'Platform > Android' 카테고리의 다른 글

[Android] Android 의 개요  (0) 2010.03.18
[Android] Boot Animation  (0) 2010.03.18
[Android] Surface Flinger의 쓰레드  (1) 2010.03.18
[Android] Surface Flinger와 초기화 과정  (0) 2010.03.18
[Android] EGL 이란?  (0) 2010.03.18

Surface Flinger 의 연속~

========================================================================================================
출처 : http://blog.daum.net/baramjin/16010960


Surface Flinger의 초기화가 끝나면 사실 쓰레드가 하나 돌면서 Event를 처리하고 2-3가지의 일을 해주는 것이 주된 업무다. (맞나? 쓰레드 하는 일이 다 그렇지...)

 

SurfaceFlinger의 threadLoop()를 보면 다음과 같다.

 

bool SurfaceFlinger::threadLoop()
{
    waitForEvent();

    // check for transactions
    if (UNLIKELY(mConsoleSignals)) {
        handleConsoleEvents();
    }

 

    if (LIKELY(mTransactionCount == 0)) {
        // if we're in a global transaction, don't do anything.
        const uint32_t mask = eTransactionNeeded | eTraversalNeeded;
        uint32_t transactionFlags = getTransactionFlags(mask);
        if (LIKELY(transactionFlags)) {
            handleTransaction(transactionFlags);
        }
    }

 

    // post surfaces (if needed)
    handlePageFlip();

 

    const DisplayHardware& hw(graphicPlane(0).displayHardware());
    if (LIKELY(hw.canDraw())) {
        // repaint the framebuffer (if needed)

        handleRepaint();

 

        // release the clients before we flip ('cause flip might block)
        unlockClients();
        executeScheduledBroadcasts();

 

        // sample the cpu gauge
        if (UNLIKELY(mDebugCpu)) {
            handleDebugCpu();
        }

 

        postFramebuffer();
    } else {
        // pretend we did the post
        unlockClients();
        executeScheduledBroadcasts();
        usleep(16667); // 60 fps period
    }
    return true;
}

 

handleRepaint()는 flags에 따라서 업데이트할 방법이나 영역을 결정하고 모든 surface를 조합한다(이게 결국 Surface Flinger의 역활이지...)

 

void SurfaceFlinger::handleRepaint()
{
    // set the frame buffer
    const DisplayHardware& hw(graphicPlane(0).displayHardware());
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

 

    if (UNLIKELY(mDebugRegion)) {
        debugFlashRegions();
    }

 

    // compute the invalid region
    mInvalidRegion.orSelf(mDirtyRegion);

 

    uint32_t flags = hw.getFlags();
    if (flags & DisplayHardware::BUFFER_PRESERVED) {
        // here we assume DisplayHardware::flip()'s  implementation
        // performs the copy-back optimization.
    } else {
        if (flags & DisplayHardware::UPDATE_ON_DEMAND) {
            // we need to fully redraw the part that will be updated
            mDirtyRegion.set(mInvalidRegion.bounds());
        } else {
            // we need to redraw everything
            mDirtyRegion.set(hw.bounds());
            mInvalidRegion = mDirtyRegion;
        }

    }

 

    // compose all surfaces
    composeSurfaces(mDirtyRegion);

 

    // clear the dirty regions
    mDirtyRegion.clear();
}

 

void SurfaceFlinger::composeSurfaces(const Region& dirty)
{
    if (UNLIKELY(!mWormholeRegion.isEmpty())) {
        // should never happen unless the window manager has a bug
        // draw something...
        drawWormhole();
    }
    const SurfaceFlinger& flinger(*this);
    const LayerVector& drawingLayers(mDrawingState.layersSortedByZ);
    const size_t count = drawingLayers.size();
    LayerBase const* const* const layers = drawingLayers.array();
    for (size_t i=0 ; i<count ; ++i) {
        LayerBase const * const layer = layers[i];
        const Region& visibleRegion(layer->visibleRegionScreen);
        if (!visibleRegion.isEmpty())  {
            const Region clip(dirty.intersect(visibleRegion));
            if (!clip.isEmpty()) {
                layer->draw(clip);
            }
        }
    }
}

 

코드를 보면 조건에 따라 Update할 영역이 일부인지 전체인지를 결정하고 결국 composerSurfaces()를 호출하여 surface들을 조합하게 된다. composerSurfaces()는 Layer에서 보여지는 영역을 잘라오고 이를 draw 한다. (음 이는 Layer도 또 분석하라는 말인가?)

 

다시 threadLoop()를 가면 handleRepaint()에 의해서 그려질 내용은 정리되었고 이를 실제 FrameBuffer로 내보내야 한다. 이를 하는 것이 postFrameBuffer()이다.

 

void SurfaceFlinger::postFramebuffer()
{
    const bool skip = mOrientationAnimation->run();
    if (UNLIKELY(skip)) {
        return;
    }

 

    if (!mInvalidRegion.isEmpty()) {
        const DisplayHardware& hw(graphicPlane(0).displayHardware());

        if (UNLIKELY(mDebugFps)) {
            debugShowFPS();
        }

 

        hw.flip(mInvalidRegion);

 

        mInvalidRegion.clear();

 

        if (Layer::deletedTextures.size()) {
            glDeleteTextures(
                    Layer::deletedTextures.size(),
                    Layer::deletedTextures.array());
            Layer::deletedTextures.clear();
        }
    }
}

 

일단 처음에 보면 Animation이 돌고 있으면 바로 return하게 된다. 이는 BootAnmation을 통해서 나중에 좀 더 생각해 보고 일단 주된 동작은

graphicPlane으로부터 처음 초기화에서 만들어 놓은 DisplayHardware의 포인터를 얻고 DisplayHardware의 flip을 호출해서 버퍼을 swap 시켜주게 된다.

 

handleRepaint()와 postFrameBuffer()를 보면 Surface Flinger는 업데이트할 영역 관리를 두개의 영역 변수로 관리한다. 하나는 mDirtyRegion으로 handleRepaint()에서 계산해서 필요한 일을 한 후에 mInvalidRegion에 저장하고 mDirtyRegion은 클리어 된다. postFrameBuffer()는 mInvalidRegion이 있는 경우에만 더블 버퍼 스왑을 하고 역시 mInvalidRegion은 클리어 한다.

 

void DisplayHardware::flip(const Region& dirty) const
{
    checkGLErrors();

 

    EGLDisplay dpy = mDisplay;
    EGLSurface surface = mSurface;

 

    Region newDirty(dirty);
    newDirty.andSelf(Rect(mWidth, mHeight));

 

    if (mFlags & BUFFER_PRESERVED) {
        const Region copyback(mDirty.subtract(newDirty));
        mDirty = newDirty;
        mDisplaySurface->copyFrontToBack(copyback);
    }

 

    if (mFlags & SWAP_RECTANGLE_EXTENSION) {
        const Rect& b(newDirty.bounds());
        mDisplaySurface->setSwapRectangle(
                b.left, b.top, b.width(), b.height());

    }

 

    eglSwapBuffers(dpy, surface);
    checkEGLErrors("eglSwapBuffers");

}

 

 사실 코드를 보면 eglSwapBuffers만 부르기 때문에 어떻게 더블 버퍼를 관리하는지는 다시 EGL의 몫으로 넘겨졌다. 또한 copyFrontToBack등을 통해서 기존에 그려진 내용을 저장해 두는 과정 등이 있는데 이 부분이 왜 필요한지, 또 성능에 영향은 없는지도 고민되는 사항이다.

 

모 그런 부분은 일단 알아서 다 잘 되겠지 하는 생각 또는 나중에 시간나면 분석하는 것 둘 중에 하나의 일일 것이다.


http://blog.daum.net/baramjin/16010960


'Platform > Android' 카테고리의 다른 글

[Android] Boot Animation  (0) 2010.03.18
[Android] SGL  (0) 2010.03.18
[Android] Surface Flinger와 초기화 과정  (0) 2010.03.18
[Android] EGL 이란?  (0) 2010.03.18
[Android] Hello Android 출력하기  (0) 2010.03.09

화면에 보여주기 위한 기본 Surface Flinger

========================================================================================================

출처 : http://blog.daum.net/baramjin/16010959

Android Native Server 개념에 크게 Surface Flinger와 Audio Flinger란 것이 있다.

사실 Flinger란 단어가 참 낯설게 느껴지는데 사전적인 의미로는 투수(무언가 던지거나 차는 사람)란 의미가 가장 의미가 와 닿는 것 같은데 Application에서의 데이터를 한데 모아서 출력쪽으로 던져주는 역활을 한다는 의미로 사용한 것이 아닐까 싶다. (언제나 그렇듯이 추측이다)

 

Surface Flinger는 단연히 여러가지 Surface 데이터를 한데 모아서 Frame Buffer를 업데이트하는 것이고 Audio Flinger는 여러가지 오디오 샘플을 한데 모아서, 즉 Audio Mixing을 해서 ALSA 쪽으로 보내는 역활을 한다고 볼 수 있다.

 

Surface Flinger에 대한 가장 잘 표현된 그림은 다음과 같다.

 

Surface Flinger가 하는 기본적인 내용을 번역해 보면 다음과 같다.

 

  • Provides system-wide surface "composer", handling all surface rendering to frame buffer device

composer라고 하는 surface들을 합치는 기능을 제공한다는 의미인 것 같다. (비슷한 말들이 계속 나온다)

 

  • Can combine 2D and 3D surfaces and surfaces from multiple applications

2D, 3D surface를 결합하거나, 여러 응용들의 surface를 결합할 수 있다. 이 말이 결국 Suface Flinger의 역활을 한마디로 표현한 것이 아닌가 싶다.

 

  • Surfaces passed as buffers via Binder IPC calls

Binder IPC calls에 의하여 버퍼를 패스한다. (사실 아직 Binder란 개념이 낯설어서 이 말은 이해가 전혀 안된다.)

 

  • Can use OpenGL ES and 2D hardware accelerator for its compositions

Surface 들을 결합하기 위하여 OpenGL ES와 2D H/W 가속을 이용할 수 있다.

 

  • Double-buffering using page-flip

Page Flip을 위해서 더블 버퍼링을 사용한다.

 

결론적으로 OpenGL ES와 연동이 되면서 Surface들을 합쳐서 Frame Buffer로 전달할 수 있는 구조이며 page flip을 위해 double buffering을 사용한다는 것으로 요약할 수 있을 것이다.

 

OpenGL ES와 연동하기 위하여 Surface Flinger의 초기화 과정을 보면 EGL을 이용하여 Display와 Surface 등을 생성하는 과정을 확인할 수 있다.

 

관련 소스를 보면 다음과 같다.

\frameworks\base\libs\surfaceflinger\SurfaceFlinger.cpp

\frameworks\base\libs\surfaceflinger\displayhardware\DisplayHardware.cpp

\frameworks\base\libs\ui\EGLDisplaySurface.cpp

\frameworks\base\opengl\libagl\Egl.cpp

 

SurfaceFlinger.cpp를 보면 SurfaceFlinger 클래스는 여러 클래스로부터 다중 상속된 클래스이다. 특히 Thread 클래스로부터 상속된 클래스인테 이는 실행되면 하나의 threadLoop()가 동작함을 의미한다. 이 threadLoop가 동작하기 전에 readyToRun()으로 초기화 과정이 이루어 진다.

 

SurfaceFlinger의 readyToRun()을 보면 초기화 과정 중 main display를 초기화 하는 과정과 OpenGL ES를 초기화하는 과정이 있음을 알 수 있다. main display를 초기화 하는 과정을 보면 다음과 같다.

 

status_t SurfaceFlinger::readyToRun()

        ......

 

        // initialize the main display
        GraphicPlane& plane(graphicPlane(dpy));
        DisplayHardware* const hw = new DisplayHardware(this, dpy);
        plane.setDisplayHardware(hw);

 

 

즉 DisplayHardware를 생성하는 과정이 필요하다. 생성되는 DisplayHaraware는 GraphicPlane에 설정된다.

DisplayHardware 클래스를 보면 init 함수에서 EGL 관련된 내용들을 초기화 하고 있다.

DisplayHardware::DisplayHardware(
        const sp<SurfaceFlinger>& flinger,
        uint32_t dpy)
    : DisplayHardwareBase(flinger, dpy)
{
    init(dpy);
}

 

 

init() 함수에서 하는 내용을 순서대로 정리하면 다음과 같다.

 

1) Display 정보 얻어서 EGL 초기화

- EGL extenstion 정보 얻는 과정도 포함하며 이를 이용하여 mFlag에 조건 저장

2) Surface 생성

3) OpenGL ES의 Context 생성

4) OpenGL ES의 Extension 정보 얻음

- 이를 위해서 Display와 Surface, Context를 bind 하는 과정 있음, 모든 정보를 얻으면 다시 unbind

- Extension 정보를 저장하여 mFlag에 조건 저장

5) 생성된 내용을 저장

6) Hardware Module 초기화

- copybit

- overlay

 

init()에서 하는 과정은 대부분은 EGL 초기화 과정과 디바이스 드라이버(특히 fb, scaler 등)를 여는 것이라고 볼 수 있으며 이에 대한 내용은

http://www.khronos.org/registry/egl/specs/eglspec.1.3.pdf 을 참고하는 것이 좋다.

 

1) Display 정보 얻어서 EGL 초기화

 

void DisplayHardware::init(uint32_t dpy)

{

    ......

 

    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);

    eglInitialize(display, NULL, NULL);
    eglGetConfigs(display, NULL, 0, &numConfigs);
    eglChooseConfig(display, attribs, &config, 1, &n); 

}

 

2) Surface 생성

Surface 생성은 크게 EGLDisplaySurface 클래스를 생성하는 과정과 이로부터 실제 EGL의 windows surface를 생성하는 과정으로 나뉘어 진다.

EGL은 세종류의 surface type(windows, pbuffers, pixmaps)를 지원하는데 windows는 onscreen rendering에 사용되고, pbuffers는 offscreen rendering에 사용되며 pixmaps는 nativa API로 접근하는 버퍼들에 대한 offscreen rendering에 사용된다. 말이 좀 어려운데 결국 onscreen rendering은 우리가 보통 얘기하는 frame buffer와 직접적인 관련이 있다는 것을 알 수 있다.

결국 여기에서 frame buffer로의 기본 출력 (fb device와의 연결)을 위한 기본 동작이 수행된다.

 

void DisplayHardware::init(uint32_t dpy)

{

    ......

 


    /*
     * Create our main surface
     */
 

    mDisplaySurface = new EGLDisplaySurface();

    surface = eglCreateWindowSurface(display, config, mDisplaySurface.get(), NULL);
    //checkEGLErrors("eglCreateDisplaySurfaceANDROID");

 

    if (eglQuerySurface(display, surface, EGL_SWAP_BEHAVIOR, &dummy) == EGL_TRUE) {
        if (dummy == EGL_BUFFER_PRESERVED) {
            mFlags |= BUFFER_PRESERVED;
        }
    }
    
    GLint value = EGL_UNKNOWN;
    eglQuerySurface(display, surface, EGL_HORIZONTAL_RESOLUTION, &value);
    if (value == EGL_UNKNOWN) {
        mDpiX = 160.0f;
    } else {
        mDpiX = 25.4f * float(value)/EGL_DISPLAY_SCALING;
    }
    value = EGL_UNKNOWN;
    eglQuerySurface(display, surface, EGL_VERTICAL_RESOLUTION, &value);
    if (value == EGL_UNKNOWN) {
        mDpiY = 160.0f;
    } else {
        mDpiY = 25.4f * float(value)/EGL_DISPLAY_SCALING;
    }
    mRefreshRate = 60.f;

}

 

EGLDisplaySurface  클래스를 생성하는 것을 보면 mapFrameBuffer()를 호출하는데  

EGLDisplaySurface::EGLDisplaySurface()
    : EGLNativeSurface<EGLDisplaySurface>()
{
    egl_native_window_t::version = sizeof(egl_native_window_t);
    egl_native_window_t::ident = 0;
    egl_native_window_t::incRef = &EGLDisplaySurface::hook_incRef;
    egl_native_window_t::decRef = &EGLDisplaySurface::hook_decRef;
    egl_native_window_t::swapBuffers = &EGLDisplaySurface::hook_swapBuffers;
    egl_native_window_t::connect = 0;
    egl_native_window_t::disconnect = 0;

    mFb[0].data = 0;
    mFb[1].data = 0;
    mBlitEngine = 0;
    egl_native_window_t::fd = mapFrameBuffer();

 

    ......

 

status_t EGLDisplaySurface::mapFrameBuffer()
{
    char const * const device_template[] = {
            "/dev/graphics/fb%u",
            "/dev/fb%u",

            0 };
    int fd = -1;
    int i=0;
    char name[64];
    while ((fd==-1) && device_template[i]) {
        snprintf(name, 64, device_template[i], 0);
        fd = open(name, O_RDWR, 0);
        i++;
    }
     if (fd < 0)
        return -errno;

 

    struct fb_fix_screeninfo finfo;
    if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1)
        return -errno;

 

    struct fb_var_screeninfo info;
    if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)
        return -errno;

 

   ......

 

    /*
     * Open and map the display.
     */

 

    void* buffer  = (uint16_t*) mmap(
            0, finfo.smem_len,
            PROT_READ | PROT_WRITE,
            MAP_SHARED,
            fd, 0);

 

    if (buffer == MAP_FAILED)
        return -errno;

 

    // at least for now, always clear the fb
    memset(buffer, 0, finfo.smem_len);

 

    uint8_t* offscreen[2];
    offscreen[0] = (uint8_t*)buffer;
    if (flags & PAGE_FLIP) {
        offscreen[1] = (uint8_t*)buffer + finfo.line_length*info.yres;
    } else {
        offscreen[1] = (uint8_t*)malloc(finfo.smem_len);
        if (offscreen[1] == 0) {
            munmap(buffer, finfo.smem_len);
            return NO_MEMORY;
        }
    }

 

    mFlags = flags;
    mInfo = info;
    mFinfo = finfo;
    mSize = finfo.smem_len;
    mIndex = 0;
    for (int i=0 ; i<2 ; i++) {
        mFb[i].version = sizeof(GGLSurface);
        mFb[i].width   = info.xres;
        mFb[i].height  = info.yres;
        mFb[i].stride  = finfo.line_length / (info.bits_per_pixel >> 3);
        mFb[i].data    = (GGLubyte*)(offscreen[i]);
        mFb[i].format  = GGL_PIXEL_FORMAT_RGB_565;
    }
    return fd; 


}

 

간단히 정리하면 여기에서는

1) fb device를 찾아서 열고

2) FSCREEN_INFO와 VSCREEN_INFO를 얻은 후,

3) mmap()으로 fb device에 직접 억세스할 수 있는 버퍼 포인터를 얻고 (mmap은 리눅스의 char형 Device Driver를 File I/O 형태로 접근하지 않고 pointer 형태로 접근하도록 해준다. 이와 관련된 내용은 추후 다시 정리)

4) 이를 mFb에 저장한다.

 

즉 EGLDisplaySurface 클래스를 만듬으로써 SurfaceFlinger가 실제 Fb Devie Driver로 접근하기 위한 준비를 하게 되는 것이다. eglCreateWindowSurface를 호출하여 window surface를 만드는 것은 EGL을 사용하기 위하여 반드시 필요한 과정이다. 또한 이 과정이 EGLDisplaySurface 클래스로 생성된 내용을 실제 연결하는 과정이라고 생각해도 될 것 같다.

 

 

3) OpenGL ES의 Context 생성은 원래 EGL의 초기화 과정 중의 하나이다. 부담없이 보면 다음과 같다. context를 생성한 후, width와 height 정보를 얻어서 저장한다.

 

void DisplayHardware::init(uint32_t dpy)

{

    ......

 

    /*
     * Create our OpenGL ES context
     */
    
    context = eglCreateContext(display, config, NULL, NULL);
    //checkEGLErrors("eglCreateContext");
    
    eglQuerySurface(display, surface, EGL_WIDTH, &mWidth);
    eglQuerySurface(display, surface, EGL_HEIGHT, &mHeight); 

}

 

4)의 과정은 정보를 얻기 위하여 display, surface, context를 bind, unbind 하는 과정을 볼 수 있다는 것 외에 특별히 유의 사항은 없을 것 같다. 5)의 과정도 생성된 내용을 저장해 둔다는 것에만 주의하면 된다.

 

6) Hardware Module 초기화 과정에서는 2개의 중요한 모듈 copybit과 overlay를 초기화 한다. 6의 과정은 여기에서 하드웨어 모듈이 초기화 된다는 것이 중요하다. 이러한 하드웨어 모듈은 다시 말하면 GPU가 제공해주는 기능이다. 안드로이드의 경우 다양한 CPU Platform을 지원할 수 있고, 이는 같은 ARM 코어를 사용하는 경우라도 GPU 인터페이스는 다르다는 것을 의미한다. 즉 copybit 과 overlay 엔진에 대한 코드는 Chip vendor에서 만들어 두고 제공해 주어야만 바로 여기에서 그 장치를 열고 추후에 Surface Flinger를 사용할 때, CPU Platform의 고유한 성능을 낼 수 있는 것이다.

 

void DisplayHardware::init(uint32_t dpy)

{

    ......

 

    hw_module_t const* module;

    mBlitEngine = NULL;
    if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) {
        copybit_open(module, &mBlitEngine);

    }

    mOverlayEngine = NULL;
    if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) {
        overlay_control_open(module, &mOverlayEngine);

    } 

}

 

copybit에 대해서는 추후에 더 정리해야 겠지만 bitblt와 stretchbitblt를 지원하는 모듈이다. 여기에서 무슨 기능인지 알 수 있는 사람도 있겠지만 다시 설명하면 이미지 드로잉에서 많이 사용하는 복사와 Scaleing을 지원하는 장치라고 생각하면 될 것 같다. 즉 fb device 외에도 GPU의 scaler와 copy 기능을 위한 장치도 여기에서 열리게 된다.

 

초기화 과정에서 얻은 EGL의 정보로부터 mFlag에 특정 조건을 저장하는데 이는 나중에 Surface Flinger의 더블 버퍼 스왑 등의 처리 조건으로 사용된다. 이는 추후 다시 정리한다.

 

대략적인 초기화 과정에 대한 설명은 이것으로 끝이다. 그런데 왜 Surface Flinger의 초기화 과정 대부분이 EGL 초기화 과정일까?

모 이에 대한 답은 추론할 수 밖에 없다. 안드로이는 처음부터 2D Drawing은 Skia를 이용하면서 OpenGL ES를 지원하도록 설계되어 있다. OpenGL ES를 지원하려면 EGL를 이용하여 Native Window와 연결할 수 있는 구조를 만들어 주어야 한다. 따라서 일반적인 Embedded System의 단순한 GDI 함수와 Screen Buffer (Frame Buffer)의 개념이 아닌 EGL를 통한 display, surface, context를 이용한 개념으로 부터 출발할 수 밖에 없다. 만약 OpenGL ES를 지원하지 않도록 설계되었다면 Surface Flinger라고 하지 않고 Display Flinger나 Screen Flinger라고 했을 수도 있을 것 같다.

 

이런 개념 때문에 OpenGL 등을 잘 모르는 Embedded Programer 입장에서는 단순히 GDI 개념을 이해하기에도 머리 복잡한 내용이 너무 많아진다. 당연히 fb 디바이스 드라이버만 오픈하고 이렇게 오픈된 디바이스로부터 mmap으로 메모리 포인터를 얻어와서 GDI 함수를 구현하는 것에 익숙해져 있는데 (사실 이것도 리눅스 하는 사람 아니면 익숙하지 않다) 여기에 한가지 개념이 더 포함된 것이다.

 

하여간 추측으로 분석하고 적당히 마무리하지만 안드로이드 시스템에서 표준 방식으로 fb 디바이스 드라이버를 여는 곳은 여기이고 이의 구조를 이해해야만 middleware의 접근이 쉬워진다.

 

 

기타 참고로 부팅시 로그를 받아보면 다음과 같다.

 

I/SurfaceFlinger(  739): SurfaceFlinger is starting
I/SurfaceFlinger(  739): showcpu enabled
I/SurfaceFlinger(  739): showfps enabled
I/SurfaceFlinger(  739): SurfaceFlinger's main thread ready to run. Initializing graphics H/W...
I/SurfaceFlinger(  739): EGL informations:
I/SurfaceFlinger(  739): # of configs : 6
I/SurfaceFlinger(  739): vendor    : Android
I/SurfaceFlinger(  739): version   : 1.31 Android META-EGL
I/SurfaceFlinger(  739): extensions:
I/SurfaceFlinger(  739): Client API: OpenGL ES
I/EGLDisplaySurface(  739): using (fd=21)
I/EGLDisplaySurface(  739): id           = xxx_fb
I/EGLDisplaySurface(  739): xres         = 800 px
I/EGLDisplaySurface(  739): yres         = 480 px
I/EGLDisplaySurface(  739): xres_virtual = 800 px
I/EGLDisplaySurface(  739): yres_virtual = 960 px
I/EGLDisplaySurface(  739): bpp          = 16
I/EGLDisplaySurface(  739): r            = 11:5
I/EGLDisplaySurface(  739): g            =  5:6
I/EGLDisplaySurface(  739): b            =  0:5
I/EGLDisplaySurface(  739): width        = 800 mm (25.400000 dpi)
I/EGLDisplaySurface(  739): height       = 480 mm (25.400000 dpi)
I/EGLDisplaySurface(  739): refresh rate = 60.00 Hz
W/SurfaceFlinger(  739): ro.sf.lcd_density not defined, using 160 dpi by default.
I/SurfaceFlinger(  739): OpenGL informations:
I/SurfaceFlinger(  739): vendor    : Android
I/SurfaceFlinger(  739): renderer  : Android PixelFlinger 1.0
I/SurfaceFlinger(  739): version   : OpenGL ES-CM 1.0
I/SurfaceFlinger(  739): extensions: GL_OES_byte_coordinates GL_OES_fixed_point GL_OES_single_precision GL_OES_read_format G
L_OES_compressed_paletted_texture GL_OES_draw_texture GL_OES_matrix_get GL_OES_query_matrix GL_ARB_texture_compression GL_AR
B_texture_non_power_of_two GL_ANDROID_direct_texture GL_ANDROID_user_clip_plane GL_ANDROID_vertex_buffer_object GL_ANDROID_g
enerate_mipmap

 


http://blog.daum.net/baramjin/16010959

'Platform > Android' 카테고리의 다른 글

[Android] SGL  (0) 2010.03.18
[Android] Surface Flinger의 쓰레드  (1) 2010.03.18
[Android] EGL 이란?  (0) 2010.03.18
[Android] Hello Android 출력하기  (0) 2010.03.09
[Android] 개발환경6 - Virtual Device 생성(에뮬레이터)  (0) 2010.03.09

Application 개발보다는 Android 내부에 대해 좀 더 공부해야겠다.. ㅠ_ㅠ
근데 자료가 많지 않아 공부하기가 쉽진 않은것 같다.

========================================================================================================
출처 : http://blog.daum.net/baramjin/16010958

관련 내용은 http://www.khronos.org/egl/ 의 내용을 번역한 것이다.

 

EGL

EGL at a glance

EGL provides mechanisms for creating rendering surfaces onto which client APIs like OpenGL ES and OpenVG can draw, creates graphics contexts for client APIs, and synchronizes drawing by client APIs as well as native platform rendering APIs. This enables seamless rendering using both OpenGL ES and OpenVG for high-performance, accelerated, mixed-mode 2D and 3D rendering.

 

EGL은 OpenGL ES, OpenVG 와 같은 클라이언트 API가 그림을 그리거나, Graphic context를 생성하거나, nativa platfrom rendering API뿐만 아니라 클라이언트 API에 의해서 그려진 내용을 동기화할 수 있는 rendering surface를 생성할 수 있는 방법을 제공한다.

이것은 고성능, 가속화된 2D와 3D 혼합 redering을 위하여 OpenGL ES와 OpenVG를 사용하는 한결같은 rendering을 가능하게 한다.

 

(흠 번역해도 말이 이해가 바로 되지는 않네요? 중요한 것은 redering surface를 생성할 수 있는 방법을 제공한다는 것이겠죠)

EGL (Native Platform Graphics Interface)

EGL Native Platform Graphics Interface is an interface portable layer for graphics resource management - and works between rendering APIs such as OpenGL ES or OpenVG and the underlying native platform window system. Learn More...

The EGL 1.4 specification, header files, and optional extension specifications are available in theKhronos Registry

 

EGL Native Platform Graphic Interface는 graphics resource 관리를 위한 interface portable layer이다. OpenGL ES나 OpenVG와 같은 rendering API와 native platform window system(X window와 windows와 같은 GUI 기반의 windows UI platform을 의미) 사이에서 동작한다.

 

 

 

EGL이 하는 것을 정리하면....

 

1. 클라이언트 API들이 그림을 그리거나 공유할 수 있는 rendering surfaces(windows, pbuffers, pixmaps)를 생성하는 매커니즘 제공 

(결국 OpenGL ES나 OpenVG를 위한 그림판을 제공한다고 생각하면 되겠네요, surface는 아무래도 그림판이라고 이해하는 것이 가장 좋을 듯...)

2. 클라이언트 API들을 위한 graphic contexts를 생성하거나 관리하는 방법

3. nativa platform rendering APIs들과 클라이언트 API들에 의해 그려진 내용을 동기화(synchronize) 하는 방법

(이 내용을 안드로이드 개념에서 이해하면 결국 Skia로 그려진 2D내용과 OpenGL ES에 그려진 3D 내용을 합칠수 있다는 말로 이해되지요?)

 

 

 

 

안드로이드의 \frameworks\base\opengl\include\EGL을 보면 EGL 1.2 가 포팅되어 있는 것 같다.

안드로이드에서 EGL에 대하여 봐야 하는 이유는 SufaceFlinger의 초기화 과정을 보면 EGL을 이용하여 display, surface, context 등을 초기화하는 과정이 있기 때문이다.


따라해서 하긴 했지만 드디어~!!

Hello Android 를 찍었습니다.

음.. 여기서부터 Android 가 시작인가보네요~

========================================================================================================
출처 : http://moozi.tistory.com/trackback/28

프로그램밍언어를 처음 공부할 때 책에서  일반적으로 처음 접하는 프로그램이 "Hello World"와 같은 문자를 출력하는 것입니다.

안드로이드 개발자 사이트도 예외는 아니어서 "Hello Android"를 출력하는 예제를 소개하고 있습니다.

이번 강좌에서는 "Hello Android"를 출력 예제를 다루도록 하겠습니다.

1. 먼저 AVD(Android Virtual Device)를 생성해 두어야 합니다.
(AVD생성은 http://moozi.tistory.com/26 글을 참조하세요)

2. 이클립스에서 새로운 안드로이드 프로젝트를 생성합니다.

1) File -> New -> Project 선택


2) New Project창에서 Android Projcet 선택 후 Next버튼 클릭

3) New Android Project 창에서 다음과 같이 입력한 후 Finish버튼을 누릅니다. 
Min SDK Version 은 Build Target을 지정하면 자동 입력됩니다.


4.프로젝트가 생성되면, 왼쪽 탐색기에서 HelloAndroid.java 를 더블클릭합니다.


HelloAndroid.java 파일을 열면 기본적으로 코드가 생성됩니다. 위 그림에서 빨간색 네모상자로 표시한 + 기호를 클릭하면 다른 코드를 더 볼 수 있습니다.

5. 다음 코드를 추가로 입력합니다.

6. 이제 실행할 차례입니다.

1) Run -> Run 을 선택합니다.

2) Run As 창에서 Android Application을 선택 한 후 OK를 누릅니다.

3) 만일 코딩후 저장을 안 했다면 Yes버튼을 눌러서 저장합니다.



7. 결과화면입니다.

필자의 경우은 Run 실행후 에뮬레이터초기 화면상태에서 오른쪽의 동그란 MENU버튼을 눌러야 위의 결과화면을 볼 수 있었습니다. (MENU버튼을 누른 후에도 몇개의 경고메세지가 나왔는데, 버튼을 눌러 닫고 나니 결과물이 보입니다.)

간단한 프로그램이지만 시작이 반이라고 많은 것을 한 느낌이에요 ^^;

http://moozi.tistory.com/trackback/28

+ Recent posts