안드로이드 스터디모임에서 첫 결과물이 나왔습니다.

init 프로세스 정리 문서 :

* 스프링노트 사라짐.

IAMROOT 에서 세미나:


세미나(0.2)20101118.pdf





'안드로이드 > 플랫폼' 카테고리의 다른 글

ADB 사용  (0) 2009.10.18
SystemServer, MediaServer  (0) 2009.10.12
service_manager 흐름  (0) 2009.08.25
init 분석 open 하는 디바이스 - null, kmsg  (0) 2009.08.18
init 분석 - 메인루프의 poll 의 timeout  (0) 2009.07.28
Posted by blee
,

void open_devnull_stdio(void) 함수에서 "/dev/__null__" 이름의 노드를 만들어서 open 후 곧 삭제 합니다.

static const char *name = "/dev/__null__";
if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
:


void log_init(void) 함수에서 "/dev/__kmsg__" 이름의 노드를 만들어서 open 후 곧 삭제 합니다.

static const char *name = "/dev/__kmsg__";
if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {
:

노드는 open 후 모두 삭제 하고, __null__ 를 stdin, stdout, stderr 로 지정 합니다.
__kmsg__ 는 로그를 남기기 위해서 open 하며, __kmsg__ 는 내부적으로 printk 함수를 사용해서 로그를 남깁니다.

커널 소스 kernel/drivers/char/mem.c 에서 정의된 memory device 목록 메이져 번호는 모두 1 번입니다.

static const struct {
    unsigned int        minor;
    char            *name;
    umode_t         mode;
    const struct file_operations    *fops;
} devlist[] = { /* list of minor devices */
#ifdef CONFIG_DEVMEM
    {1, "mem",     S_IRUSR | S_IWUSR | S_IRGRP, &mem_fops},
#endif
#ifdef CONFIG_DEVKMEM
    {2, "kmem",    S_IRUSR | S_IWUSR | S_IRGRP, &kmem_fops},
#endif
    {3, "null",    S_IRUGO | S_IWUGO,           &null_fops},
#ifdef CONFIG_DEVPORT
    {4, "port",    S_IRUSR | S_IWUSR | S_IRGRP, &port_fops},
#endif
    {5, "zero",    S_IRUGO | S_IWUGO,           &zero_fops},
    {7, "full",    S_IRUGO | S_IWUGO,           &full_fops},
    {8, "random",  S_IRUGO | S_IWUSR,           &random_fops},
    {9, "urandom", S_IRUGO | S_IWUSR,           &urandom_fops},
    {11,"kmsg",    S_IRUGO | S_IWUSR,           &kmsg_fops},
#ifdef CONFIG_CRASH_DUMP
    {12,"oldmem",    S_IRUSR | S_IWUSR | S_IRGRP, &oldmem_fops},
#endif
};

'안드로이드 > 플랫폼' 카테고리의 다른 글

결과물  (0) 2009.09.30
service_manager 흐름  (0) 2009.08.25
init 분석 - 메인루프의 poll 의 timeout  (0) 2009.07.28
init 분석 - firmware  (0) 2009.07.28
init 분석 - 안드로이드 보안 및 권한  (0) 2009.07.28
Posted by blee
,

poll 에서 사용하는 timeout 변수의 계산은 재 시작해야 하는 서비스의 시작 시간을 맞추기 위해서 입니다. 재 시작 할 서비스는 이전에 시작 한 시간에서 5 초 후에 재 시작을 해주는 룰을 적용하고 있습니다.

서비스가 시작되면 time_started 에 시작 시간을 지정합니다.
서비스의 시작은 service_start 함수에서 처리하며, 이 함수의 마지막 단계에서 서비스의 시작 시간을 gettime() 으로 지정합니다.
( gettime() 은 초 단위로 리턴합니다. )

svc->time_started = gettime();

다음은 init 의 메인 루프 코드 입니다.
여기서 중요한 것들은 restart_processes, process_needs_restart, timeout , wait_for_one_process 의 관계입니다.

 

for(;;) {
    :
        restart_processes();

        if (process_needs_restart) {
            timeout = (process_needs_restart - gettime()) * 1000;
            if (timeout < 0)
                timeout = 0;
        }

        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;
        }
     :
    }

서비스가 종료 되어, SIGCHLD 시그널을 받으면( 여러 논의가 있었지만 여기서는 이것만 주목 하겠습니다.)
wait_for_one_process 함수를 호출 합니다.
wait_for_one_process 에서 주목 할 점은 해당 서비스의 flags 에 SVC_RESTARTING 속성을 더합니다.
실제로 재 시작 시키지는 않습니다.

svc->flags |= SVC_RESTARTING;

이후 init 의 for 루프를 다시 돌아서 restart_processes 를 호출 합니다.

static void restart_processes()
{
    process_needs_restart = 0;
    service_for_each_flags(SVC_RESTARTING,
                           restart_service_if_needed);
}

restart_processes 함수에서 process_needs_restart 변수를 0 으로 초기화 하고,
서비스 리스트 중에서 flags 에 SVC_RESTARTING 속성이 있는 것에 한해서 restart_service_if_needed 함수를 호출 합니다.
여기서 주목할 점은 링크드 리스트를 돈다는 것입니다. process_needs_restart 를 restart_service_if_needed 함수 내에서 설정 또는 변경 하는데, 모든 리스트가 다 돌고 난 후에 그 process_needs_restart 값이 init 에서 사용 됩니다.

 

static void restart_service_if_needed(struct service *svc)
{
    time_t next_start_time = svc->time_started + 5;

    if (next_start_time <= gettime()) {
        svc->flags &= (~SVC_RESTARTING);
        service_start(svc);
        return;
    }

    if ((next_start_time < process_needs_restart) ||
        (process_needs_restart == 0)) {
        process_needs_restart = next_start_time;
    }
}

restart_service_if_needed 함수에서는 svc->time_started 변수를 참조하는데, 이는 위에서도 설명 했듯이
서비스의 시작 시간 이였습니다. next_start_time 변수는 서비스가 이전에 시작 되었던 시간에서 5초를 더한 시간입니다.

결국 서비스가 재 시작 되어야 할 시간입니다. 서비스가 재 시작되어야 할 시간이 지났으면, service_start 함수를 호출해서 해당 서비스를 시작 시킴니다.
아직 재 시작 시킬 시간이 안되었다면, process_needs_restart 를 갱신하게 됩니다.

process_needs_restart 값이 초기값(0)이면, 지금 서비스의 재 시작 시간으로 갱신 되지만,
이 서비스 보다 링크드 리스트의 이전에 있던 서비스에 의해 갱신된 process_needs_restart 값을 가질 수도  있습니다.
그래서 지금 서비스의 재 시작 시간이 이전 서비스의 재 시작 시간보다 작으면, 지금 서비스의 재 시작 시간으로 process_needs_restart 를 갱신합니다.


결국 재시작 하려고 하는 서비스 중 가장 먼저 재 시작 되어야 할 시간으로 process_needs_restart 가 갱신 됩니다.

다시 init 으로 돌아 와서

      if (process_needs_restart) {
            timeout = (process_needs_restart - gettime()) * 1000;
            if (timeout < 0)
                timeout = 0;
        }

 

timeout 값은 재 시작 되어야 할 서비스들 중에서 가장 먼저 재 시작 할 서비스의 재 시작 시간 만큼을 위한 대기 시간이 지정됩니다.

그리고 그 값을 poll 의 timeout 으로 지정합니다. poll 의 timeout 발생후 다시 restart_processes 함수를 호출 하게 됩니다.

'안드로이드 > 플랫폼' 카테고리의 다른 글

service_manager 흐름  (0) 2009.08.25
init 분석 open 하는 디바이스 - null, kmsg  (0) 2009.08.18
init 분석 - firmware  (0) 2009.07.28
init 분석 - 안드로이드 보안 및 권한  (0) 2009.07.28
프로세스, 기본 환경  (0) 2009.07.28
Posted by blee
,

오늘날 리눅스 커널에서 firmware를 사용하는 가장 광범위한 방법은 header file 에 static 하게 link 하는 것입니다.
이는 정책적으로 기술적으로 다음과 같은 이슈가 있습니다.

1. 드라이버의 firmware 는 업그레이드 되어 재배포 될 수 있습니다.
2. 비록 단 한번의 사용 일지라도 static 하게 link 된 firmware 는 영구히 메모리를 차지 합니다.
3. 사용자들은 firmware 를 메모리에서 해제하고, 드라이버를 제거하는 것에 대해 신경쓰지 않습니다.

그러한 이유로 request_firmware() 와 hotplug 간의 interface 나오게 된 것입니다.

다음의 리눅스에서 driver 와 hotplug 간의 일련의 절차 입니다.

kernel 에서 처리 : 
driver 초기화 시에 request_firmware 함수를 호출합니다.

  if(request_firmware(&fw_entry, $FIRMWARE, device) == 0)
  copy_fw_to_device(fw_entry->data, fw_entry->size);
 release(fw_entry);

$FIRMWARE 는 파일 이름입니다. 이는 제조사에서 배포되는 특정 hw가 동작 되도록 하는 code image 일 것입니다.
request_firmware -> _request_firmware -> fw_setup_device 함수를 거치면서,
/sys/class/firmware/xxx/loading, /sys/class/firmware/xxx/data 파일을 생성합니다.
 
( hotplug 를 호출 하기 전에 firmware 를 __start_builtin_fw ~ __end_builtin_fw 영역에서 먼저 찾습니다.
이는 kernel 에 static 하게 포함된 firmware 들 중에 맞는 것이 있는지 먼저 검색합니다. 이 영역에 있다면
hotplug 관련 절차를 거치지 않습니다. )
 
그리고 사용자 공간의 hotplug 가 호출 됩니다.
request_firmware 함수는 block 됩니다.

( /sys/class/firmware/timeout 에 지정된 시간 만큼 입니다. )

사용자 공간 - hotplug 에서 처리 :

사용자 영역에서 /sys/class/firmware/xxx/loading, /sys/class/firmware/xxx/data 파일이 나타나며,
 $DEVPATH, $FIRMWARE 는 환경변수로 전달 됩니다.

 HOTPLUG_FW_DIR=/usr/lib/hotplug/firmware/

 echo 1 > /sys/$DEVPATH/loading
 cat $HOTPLUG_FW_DIR/$FIRMWARE > /sysfs/$DEVPATH/data
 echo 0 > /sys/$DEVPATH/loading

 /sys/$DEVPATH/loading 에 1 를 write 하므로써, firmware 복사가 시작됨을 알립니다.
펌웨어 파일 $HOTPLUG_FW_DIR/$FIRMWARE 를 /sysfs/$DEVPATH/data 로 복사를 합니다.
/sys/$DEVPATH/loading 에 0 를 write 하므로써, firmware 복사가 완료됨을 알립니다. request_firmware 함수는 성공을 리턴합니다.
/sys/$DEVPATH/loading 에 -1 를 write 하면, 중지되며, request_firmware 함수는 실패를 리턴합니다.
 
kernel 에서 처리 :
request_firmware() 함수가 return 되면, fw_entry->data, fw_entry->size 에 firmware와 size 정보가 저장됩니다.
이를 driver 에서 특정 hw 에 맞는 작업을 수행하면 됩니다.

사용자 공간으로 전달되는 환경변수 :

SUBSYSTEM=module
DEVPATH=/module/firmware_sample_driver
LD_LIBRARY_PATH=:/user/app/lib
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/user/app/bin
ACTION=add
PWD=/
SHLVL=1
HOME=/
SEQNUM=933
_=/usr/bin/env

정리 :
안드로이드 플래폼의 init 에서 uevent 를 이용하여, add 절차를 거치면서, firmware 를 필요로 하는 driver에서
firmware 를 전달 하는 과정(hotplug 와 driver 사이의 일어나는 일련의 절차)를 처리 해주고 있는 것입니다.

static int load_firmware(int fw_fd, int loading_fd, int data_fd)
{
    struct stat st;
    long len_to_copy;
    int ret = 0;

    if(fstat(fw_fd, &st) < 0)                     <--(주석) fstat는 열린 파일이나 핸들과 연관된 디렉토리의 정보를 stat 구조체에 저장
        return -1;
    len_to_copy = st.st_size;

//------------------------------------------------------------ 
//! echo 1 > /sys/$DEVPATH/loading
//------------------------------------------------------------ 
    write(loading_fd, "1", 1);  /* start transfer */

//------------------------------------------------------------ 
//! cat $HOTPLUG_FW_DIR/$FIRMWARE > /sysfs/$DEVPATH/data
//------------------------------------------------------------ 
    while (len_to_copy > 0) {
        char buf[PAGE_SIZE];
        ssize_t nr;

        nr = read(fw_fd, buf, sizeof(buf));    
        if(!nr)
            break;
        if(nr < 0) {
            ret = -1;
            break;
        }

        len_to_copy -= nr;
        while (nr > 0) {
            ssize_t nw = 0;

            nw = write(data_fd, buf + nw, nr); 
            if(nw <= 0) {
                ret = -1;
                goto out;
            }
            nr -= nw;
        }
    }

//------------------------------------------------------------ 
// echo 0 > /sys/$DEVPATH/loading 
//------------------------------------------------------------ 
out:
    if(!ret)
        write(loading_fd, "0", 1);  /* successful end of transfer */
    else
        write(loading_fd, "-1", 2); /* abort transfer */

    return ret;
}

참고 :  
kernel/Documentation/firmware_class/README
linux device driver model 관련 문서


Posted by blee
,