init 과정에서 uevent와 ueventd의 활용

29 8월

uevent와 ueventd는 부팅중 init 과정에서 디바이스 노드를 만드는데 사용된다.

리눅스에서는 다비아스 노드 파일을 생성할수 있게 mknod유틸리티를 제공하지만 안드로이드에서는 보안문제로 인해 제공하지 않는다.

init 프로세스는 다음 두가지 방식으로 디바이스 노드 파일을 생성한다.

1. hot plug : 시스템 동작중 디바이스 장치가 삽입될때 이에대한 이벤트 처리로 ueventd를 거쳐 해당장치의 디비이스 노드 파일을 동적으로 생성

2. cold plug : 미리 정의된 디바이스 정보를 바탕으로 init 프로세스가 실행될때 일괄적으로 디바이스 노드 파일을 생성

cold plug 방식이 있는이유는 hot plug 방식을 사용하려면 이미 ueventd가 띄워져 있어야 하는데 init과정에서 ueventd가 뜨기전에 생성된 디바이스 드라이버 대한 디바이스 노드를 생성하기 위함이다.

cold plug 방식을 자세히 보면

ueventd가 뜨기전의 디바이스 드라이버는 우선 /sys 디렉토리 밑에 디바이스 노드 파일을 생성하는데 필요한 정보를 저장한다.

그후에 ueventd가 뜨면서 디바이스 노드 파일을 생성하지 못한 드라이버에 대하 cold flug처리를 한다.

             

위그림을 참고하면

우선 init과정에서 ueventd를 부른다.

그럼 호출되는 것이 ueventd_main 함수이다.

int ueventd_main(int argc, char **argv)
{
    struct pollfd ufd;
    int nr;
    char tmp[32];

        /* Prevent fire-and-forget children from becoming zombies.
         * If we should need to wait() for some children in the future
         * (as opposed to none right now), double-forking here instead
         * of ignoring SIGCHLD may be the better solution.
         */
    signal(SIGCHLD, SIG_IGN);

    open_devnull_stdio();
    klog_init();

    INFO(“starting ueventd\n”);

    /* Respect hardware passed in through the kernel cmd line. Here we will look
     * for androidboot.hardware param in kernel cmdline, and save its value in
     * hardware[]. */
    import_kernel_cmdline(0, import_kernel_nv);

    get_hardware_name(hardware, &revision);

    ueventd_parse_config_file(“/ueventd.rc”);

    snprintf(tmp, sizeof(tmp), “/ueventd.%s.rc”, hardware);
    ueventd_parse_config_file(tmp);

    device_init();

    ufd.events = POLLIN;
    ufd.fd = get_device_fd();

    while(1) {
        ufd.revents = 0;
        nr = poll(&ufd, 1, -1);
        if (nr <= 0)
            continue;
        if (ufd.revents == POLLIN)
               handle_device_fd();
    }
}

여기서 중요한 함수는 ueventd_parse_config_file와  device_init함수이다.

ueventd_parse_config_file함수는 ueventd.rc파일과 ueventd.%hardware%.rc 파일을 읽어 디바이스 노드 파일을 만드는 정보를 얻는다

여기에 저장되어 있는 정보는 device 이름, permission, gid, uid 이다.

저장되어 있지 않는 device는 디폴트로 600, 0, 0이 세팅된다.

 

device_init함수는 uevent_socket을 열고 coldboot 함수를 실행한다.

여기서 연 소켓은 uevent를 보낼때 쓰이는 것이 아니라 나중에 발생한 uevent를 받을때 쓰인다.

void device_init(void)
{
    suseconds_t t0, t1;
    struct stat info;
    int fd;

    /* is 64K enough? udev uses 16MB! */
    device_fd = uevent_open_socket(64*1024, true);
    if(device_fd < 0)
        return;

    fcntl(device_fd, F_SETFD, FD_CLOEXEC);
    fcntl(device_fd, F_SETFL, O_NONBLOCK);

    if (stat(coldboot_done, &info) < 0) {
        t0 = get_usecs();
        coldboot(“/sys/class”);
        coldboot(“/sys/block”);
        coldboot(“/sys/devices”);
        t1 = get_usecs();
        fd = open(coldboot_done, O_WRONLY|O_CREAT, 0000);
        close(fd);
        log_event_print(“coldboot %ld uS\n”, ((long) (t1 – t0)));
    } else {
        log_event_print(“skipping coldboot, already done\n”);
    }
}

 

호출 되는 coldboot는 내부적으로 do_coldboot를 호출한다.

static void do_coldboot(DIR *d)
{
    struct dirent *de;
    int dfd, fd;

    dfd = dirfd(d);

    fd = openat(dfd, “uevent”, O_WRONLY);
    if(fd >= 0) {
        write(fd, “add\n”, 4);
        close(fd);
        handle_device_fd();
    }

    while((de = readdir(d))) {
        DIR *d2;

        if(de->d_type != DT_DIR || de->d_name[0] == ‘.’)
            continue;

        fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
        if(fd < 0)
            continue;

        d2 = fdopendir(fd);
        if(d2 == 0)
            close(fd);
        else {
            do_coldboot(d2);
            closedir(d2);
        }
    }
}

디바이스 노드를 생성하지 못한 디바이스가 저장한 /sys 밑의 각각의 해당 폴더를 들어가 uevent 파일에 “add” 메시지를 써넣어 강제로 uevent를 발생시킨다

그후  handle_device_fd 함수를 통해 uevent 를 파싱해 디바이스 노드를 만든다. 이 과정에서 ueventd_parse_config_file에서 얻어온 정보를 사용한다.

 

 

답글 남기기

댓글을 게시하려면 다음의 방법 중 하나를 사용하여 로그인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Google photo

Google의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중

%d 블로거가 이것을 좋아합니다: