Friday, November 11, 2016

Kernel threads

So, today we will talk about Kernel Threads.

What are Kernel threads

The purpose of a kernel threads is to make a tasks which will run in the background. Usually kernel threads wait for some asynchronous events and then wake up to serve them. For example, the kswapd is kernel thread which runs in the background and wait for expiration of a swap timer, then it handles swapping memory to disk if the number of free pages in the system is low.
If you type the following in your terminal:
$ ps -aux

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0 185512  6100 ?        Ss   אוק06   0:04 /lib/systemd/systemd --system --deserialize 28
root         2  0.0  0.0      0     0 ?        S    אוק06   0:00 [kthreadd]
root         3  0.0  0.0      0     0 ?        S    אוק06   0:00 [ksoftirqd/0]
root         5  0.0  0.0      0     0 ?        S<   אוק06   0:00 [kworker/0:0H]
root         7  0.0  0.0      0     0 ?        S    אוק06   0:23 [rcu_sched]
root         8  0.0  0.0      0     0 ?        S    אוק06   0:00 [rcu_bh]
root         9  0.0  0.0      0     0 ?        S    אוק06   0:00 [migration/0]
root        10  0.0  0.0      0     0 ?        S    אוק06   0:00 [watchdog/0]
root        11  0.0  0.0      0     0 ?        S    אוק06   0:00 [watchdog/1]
root        12  0.0  0.0      0     0 ?        S    אוק06   0:00 [migration/1]
root        13  0.0  0.0      0     0 ?        S    אוק06   0:00 [ksoftirqd/1]
...

All this processes which name is surrounded by square brackets are kernel threads.

Lets see some code:

Low level kernel thread usage

I will begin, by showing you a low level thread API, then we look at higher level API which should be usually used.

This is how to create a linux thread:

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

There parameters we pass here tells which resources should be shared between the parent thread and the child thread. For example - CLONE_FILES, share opened files, CLONE_SIGHAND, share signal handlers.
Each thread in Linux has a single parent and usually the new created threads are re-parented to kthreadd thread, to avoid the new thread to become a zombie if a parent process dies without waiting for its child to exit.



static DECLARE_WAIT_QUEUE_HEAD(myevent_waitqueue);
rwlock_t myevent_lock;

static int mythread(void *unused)
{
    unsigned int event_id = 0;
    DECLARE_WAITQUEUE(wait, current);
 
    /* performs initial housekeeping and changes the parent of the calling thread 
    to a kernel thread called kthreadd */
    daemonize("mythread");
 
    /* all signals are blocked by default, so we need to enable a particular signal. */
    allow_signal(SIGKILL);
 
    /* The thread sleeps on this wait queue until it's woken up */
    add_wait_queue(&myevent_waitqueue, &wait);
 
    for (;;) 
    {
        /* Request to opt out of the scheduler run queue */
        set_current_state(TASK_INTERRUPTIBLE); 

        /* asks the scheduler to choose and run a new task from its run queue */ 
        schedule();
 
        /* At this point scheduler executes another task and it will not execute
           the current kthread anymore. */
 
        /* Die if SIGKILL received, it is the only signal that may be received... */
        if (signal_pending(current)) 
        { 
            break;
        } 
 
        /* The thread is put back into the scheduler run queue.
           This could only happend because of an event over the wait queue. */
        read_lock(&myevent_lock); 
 
        /* Do something here... */
 
        read_unlock(&myevent_lock);
    } 
 
    /* 
    Changed to TASK_RUNNING, so there is no race condition even if the wake up occurs 
    between the time the task state is set to TASK_INTERRUPTIBLE and the time schedule() 
    is called.
    */
    set_current_state(TASK_RUNNING);
 
    remove_wait_queue(&myevent_waitqueue, &wait);
 
    return 0;
}

Notes about kthread

Kernel threads may be preempted if the kernel was compiled with CONFIG_PREEMPT flag.A code may prevent preemption, even of the CONFIG_PREEMPT flag, by disabling local interrupts.

A kernel thread can be in any of the following process states:

  • TASK_RUNNING: In the scheduler run queue, so should run in future.
  • TASK_INTERRUPTIBLE: Waiting for an event and is not in the
    scheduler run queue.
  • TASK_UNINTERRUPTIBLE: Receipt of a signal will not put the task back into the run queue.
  • TASK_STOPPED: Stopped execution as a result of receiving a signal.
  • TASK_TRACED: If traced by application such as strace.
  • EXIT_ZOMBIE: His parent didn't wait for it to complete.
  • EXIT_DEAD: Exited gracefully.
 
set_current_state()
Selects kthread's state.

To wake up a thread which waits on a wait queue, we use wake_up_interruptible(&myevent_waitqueue) function.

The higher level thread API

This API is actually one of several kernel helper interfaces which exist to make a kernel programmer's life a little easier...



#include <linux/kthread.h>

static int my_thread(void *unused)
{
    DECLARE_WAITQUEUE(wait, current);

    while (!kthread_should_stop()) 
    {
        /* ... */
    }

    __set_current_state(TASK_RUNNING);
    remove_wait_queue(&my_thread_wait, &wait);

    return 0;
}

void my_thread_init(void)
{
    my_task = kthread_create(my_thread, NULL, "%s", "my_thread");
}

void my_thread_wakeup(void)
{
    if (my_task)
    {
        wake_up_process(my_task);
    }
}

void my_thread_release(void)
{
    kthread_stop(my_task);
}

This is a more proper way to handle threads.

This is for today, thanks for reading!

No comments:

Post a Comment