본문 바로가기

Embedded/Device Driver

Device Driver] Kernel Facilities

Kernel Facilities 는 드라이버 개발자의 toolbox안의 유용한 components이다.

우리가 알아볼 Kernel Facilities는 Kernel Thread , Helper Interfaces 이다.

Helper Interfaces 에서는 연결리스트(Linked list), 해시리스트(Hash list), 작업큐(Work Queues), Notifier Chains, Completion Interface,
Kthread Helpers, Error Handling Aids 를 살펴보자.


◎.Kernel Thread

커널 스레드는 쉽게 커널에 존재하는 스레드라 생각하자.
유저스레드와의 차이라고 한다면 커널공간에 존재하고, 커널 함수와 커널 자료구조에 접근할 수 있다는 것이다.
그리고 대개 커널 스레드가 유저스레드보다 우선순위가 높아서 선점형 스케줄링에서는 우선적으로 처리가 된다.

다음과 같은 경우에 커널 스레드로 구현한다.

ㅡ.비동기식 이벤트를 배경작업으로 기다려야 하는 경우
ㅡ.시간을 많이 소비하는 연산을 수행하는데 커널 자료 구조에 접근할 필요가 있는 경우


nfs와 같은경우 nfsd라는 커널 스레드 집합을 사용한다.
 

※.커널 스레드 생성 : kernel_thread()

ret = kernel_thread(mykthread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD);

커널 스레드는 일반적으로 디바이스 드라이버에게 서비스를 제공할 도우미로 동작한다.

 
※.커널 스레드 시작 : daemonize()

daemonize("mykthread");


daemonize는 기본적으로 모든 시그널을 막고, 초기화 기능을 수행한 다음에 호출한 스레드의 부모를 kthreadd라는 커널 스레드로 변경한다.

특정 시그널을 처리하고 싶다면 allow_signal()을 사용하고, signal_pending()을 사용해서 시그널을 점검하고 적절한 행동을 취한다.
부모를 kthreadd 로 바꾸는 이유는 부모가 먼저 죽은뒤 자식이 죽으면 좀비 프로세스가 생기는데 이를 막기위해 kthreadd로 만들어 준다. (kthreadd 는 자식이 죽을때까지 기다린후 죽으면 적절한 처리를 해줌.)


kernel_thread()보다 kthread_API를 사용하는 방식이 바람직하다.

Kernel_thread를 사용하는 예제를 미리 살펴보자.

static DECLARE_COMPLETION(my_thread_exit)
static DECLARE_WAIT_QUEUE_HEAD(my_thread_wait);
int pink_slip = 0;

static int my_thread(void *unused)
{
    DECLARE_WAITQUEUE(wait, current);
    
    daemonize("mykthread");    
    add_wait_queue(&my_thread_wait, &wait);

    while(1){
        set_currnet_state(TASK_INTERRUPTIBLE);
        schedule();

        if(pink_slip) break;
    }

    __set_current_state(TASK_RUNNING);
    remove_wait_queue(&my_thread_wait, &wait)
    
    complete_and_exit(&my_thread_exit, 0);
}

static int __init my_init(void)
{
    kernel_thread(my_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD);
}

static void __exit my_release(void)
{
    pnik_slip = 1;
    wake_up(&my_thrhead_wait);
    wait_for_completion(&my_thread_exit);
}

◎.Kthread_API

    기존의 스레드 생성과정을 보다 단순한 인터페이스로 바꿔서 관리작업을 보다 단순하게 만들어 준다.
    위에 나온 소스가 어떻게 변했는지를 보면서 이해 해보자^^

#include <linux/kthread.h>

static int my_thread(void *unused)
{
    DECLARE_WAITQUEUE(wait, current);
    
    daemonize("mykthread");
    add_wait_queue(&my_thread_wait, &wait);

    while(1){
    while(!kthread_shoule_stop()) {
        set_currnet_state(TASK_INTERRUPTIBLE);
        schedule();

        if(pink_slip) break;
    }

    __set_current_state(TASK_RUNNING);
    remove_wait_queue(&my_thread_wait, &wait)
    
    complete_and_exit(&my_thread_exit, 0);
    return 0;
}

struct task_struct *my_task;

static int __init my_init(void)
{
    kernel_thread(my_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD);
    my_task = kthread_create(my_thread, NULL, "%s", "my_thread");
    if(my_task) wake_up_process(my_task);
}

static void __exit my_release(void)
{
    pnik_slip = 1;
    wake_up(&my_thrhead_wait);
    wait_for_completion(&my_thread_exit);
    kthread_stop(my_task);
}

    생성 변경 : kernel_thread() à kthread_create() & wake_up_process()
        (kthread_create() 와 wake_up_process()는 kthread_run() 으로 대체될 수 있다.)
종료 변경 : pink_slip = 1; & wake_up() & wait_for_completion() à kthread_stop();