LinuxQuestions.org
Latest LQ Deal: Latest LQ Deals
Go Back   LinuxQuestions.org > Articles > Technical
User Name
Password

Notices


By dahuja76 at 2006-10-08 06:34

LINUX KERNEL SERIES

Author:Dinesh Ahuja
Title:Linux Kernel Series: Linux Kernel Threads

The purpose of this article is to cover Linux implementation of kernel threads and how
these kernel threads can be used in kernel modules. This article will show how a kernel
thread can be put to use for a monitoring purpose.

Most of today's operating systems provide multi-threading support and linux is not different from these operating systems. Linux support threads as they provide concurrency or parallelism on multiple processor systems. Most of the operating systems like Microsoft Windows or Sun Solaris differentiate between a process and a thread i.e. they have an explicit support for threads which means different data structures are used in the kernel to represent a thread and a process.

Linux implementation of threads is totally different as compared to the above-mentioned operating systems. Linux implements threads as a process that shares resources among themselves. Linux does not have a separate data structure to represent a thread. Each thread is represented with task_struct and the scheduling of these is the same as that of a process. It means the scheduler does not differentiate between a thread and a process.

KERNEL THREADS

Kernels can attain parallelism by spawning some operations in the background. The kernel achieves this by delegating these operations to the kernel threads. A kernel thread is a process that exists only in kernel space and does not have access to user address space i.e. mm pointer is NULL. Being an integral part of a kernel and running in a kernel address space, they have an access to kernel data structures. A Kernel thread does not have a privilege over other processes and hence they are also as schedulable and pre-emptable as any other process.

The kernel symbol table contains the addresses of global kernel items – functions and
variables that can be accessed by modules. The symbols exported by statically linked kernels
and all linked-in modules can be retrieved by reading the /proc/ksyms file. Even kernel
modules can export their respective symbols so that they can be accessed by other kernel
modules.

The interface to create a kernel thread is as below:

int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)

This is defined in arch/i386/kernel/process.c. In this, a new process executes a function specified by fn argument and arg is passed as an argument to this function.

kernel_thread() function is exported by a kernel and hence it can be accessed from kernel modules. To export a symbol from kernel or a module, use EXPORT_SYMBOL(symbol) macro.This macro causes symbol to be added into a kernel symbol table i.e. __ksymtab section of kernel code.

IMPLEMENTATION DETAILS

The implementation of kernel_thread() function is available in arch/i386/kernel/process.c. The new task is created via do_fork() system call. A kernel_thread() system call is invoked with a special flag CLONE_KERNEL as an argument. This is defined in include/linux/sched.h as below in

define CLONE_KERNEL (CLONE_FS | CLONE_FILES | CLONE_SIGHAND)

Internally, kernel_thread() passes another two flags CLONE_VM and CLONE_UNTRACED to
do_fork() system call. It means a newly created process will share following resources with its parent process.

  • File System Information.
  • File Descriptor Table.
  • Signal Handlers.
  • Memory space.
  • A do_fork() is responsible to create a new task structure on the basis of the
    flags passed to it, to set a state of newly created task to TASK_RUNNING and put
    this task structure on a runqueue list. It depends upon scheduler when this task
    is being picked up for execution.

    SAMPLE CODE

    A sample code will create a kernel thread whose job is to iterate over a tasklist
    associated with a CPU and prints all its details using printk() function. A kernel thread
    will be responsible to see whether a process is a zombie process. A Zombie process blocks
    the memory in a process table and this memory should be released as it blocks resources.

    A schedule_timeout() function makes a current task sleep until timeout jiffies have
    expired. On return, the task is guaranteed to be in a TASK_RUNNING state. A following code
    will cause a current task to get removed from runqueue.

    set_current_state(TASK_INTERRUPTIBLE);
    schedule_timeout(30*HZ);

    A schedule_timeout() will be internally invoke schedule() which will cause a current task
    to be removed from a runqueue, if there is no pending signals for the current process.
    Each call to schedule_timeout will cause timer_list to get added into a global list of
    timers on FIFO basis.

    struct timer_list {
    struct list_head list;
    unsigned long expires;
    unsigned long data;
    void (*function)(unsigned long);
    };

    For every timer_list, the function pointer is set to an address of process_timeout()
    whose implementation is given below:

    static void process_timeout(unsigned long __data)
    {
    wake_up_process((task_t *)__data);
    }

    The purpose of this function is to put a task back into a runqueue and set its state
    to TASK_RUNNING before returning. The TIMER_BH bottom half is responsible to invoke a
    run_timer_list() function which internally invokes process_timeout() with timer_list->
    data as an argument.

    Note: Invoking schedule_timeout() without setting task state to TASK_INTERRUPTIBLE would
    not cause it to get removed from a runqueue and therefore will have no impact on process's
    scheduling.

    To spawn a kernel thread, we need to define a thread function which is defined as below:

    static int print_taskinfo(void * data)
    {
    printk("<1>::Inside Thread Function::\n");
    printk("Kernel Thread::Current Process is \"%s \" (pid: %i) \n",
    current->comm,current->pid);

    sprintf(current->comm,"MyTask" );

    set_current_state(TASK_INTERRUPTIBLE);
    void * sZombie = kmalloc(10 * sizeof(char), GFP_KERNEL);
    memset(sZombie,'\0', 10 * sizeof(char));

    // Not very clear who will clean up a temporary memory allocated for
    // string literals "ZOMBIE" & "NON-ZOMBIE"...

    memcpy(sZombie, "ZOMBIE", 7);
    void * sNonZombie = kmalloc(15 * sizeof(char), GFP_KERNEL);
    memset(sNonZombie,'\0', 15 * sizeof(char));
    memcpy(sNonZombie, "NON-ZOMBIE", 12);
    struct task_struct *p ;
    printk("<1>Starting Iteration\n");

    for (;;)
    {
    for_each_process(p) {
    // Iterating each element of task list.
    printk ("Task List::Process PID = %i,::PPID = %i,::State = %ld ::Zombie=%s\n",
    p->pid,p->parent->pid,p->state, (p->state == TASK_ZOMBIE)? (char*)sZombie :
    (char*)sNonZombie);
    }
    schedule_timeout(30*HZ); // sleep for 30 seconds.
    }
    // Release memory allocated via kmalloc.

    kfree(sZombie);
    kfree(sNonZombie);
    printk("<1> Exiting Thread Function::");
    }

    To create a new thread, use kernel_thread() function and pass an address of thread
    function as an argument to this.

    kernel_thread((int (*)(void *))print_taskinfo,NULL, 0);

    To test this module, just create a simple program to create a zombie process.

    int main()
    {
    pid_t pid;
    if ((pid = fork()) < 0)
    exit(1);
    else if (pid == 0)
    exit(0);
    sleep(120);
    exit(0);
    }

    Execute above code and just see an output of ps command which depicts that child
    process had died before a parent process. And as parent process has not waited on a
    child process, so we have a zombie process also called &lt defunct &gt .

    Output of ps command:
    dahuja 2099 1998 6 11:33 pts/3 00:00:01 ./Example10_3
    dahuja 2100 2099 0 11:33 pts/3 00:00:00 [Example10_3 &lt defunct &gt]
    dahuja 2108 1835 0 11:33 pts/0 00:00:00 grep Example10

    Output of dmesg command:
    Task List::Process PID = 2078,::PPID = 2076,::State = 1 ::Zombie=NON-ZOMBIE
    Task List::Process PID = 2082,::PPID = 2076,::State = 1 ::Zombie=NON-ZOMBIE
    Task List::Process PID = 2083,::PPID = 2076,::State = 1 ::Zombie=NON-ZOMBIE
    Task List::Process PID = 2099,::PPID = 1998,::State = 1 ::Zombie=NON-ZOMBIE
    Task List::Process PID = 2100,::PPID = 2099,::State = 8 ::Zombie=ZOMBIE
    Task List::Process PID = 2109,::PPID = 1924,::State = 0 ::Zombie=NON-ZOMBIE
    Task List::Process PID = 2110,::PPID = 2109,::State = 0 ::Zombie=NON-ZOMBIE

    We could see that our module has shown us that a zombie process exists i.e. PID=2100.


    by cmontr on Wed, 2010-10-27 19:56
    Do you have a better example in steps?
    Thnx

    by cmontr on Wed, 2011-01-12 10:15
    Quote:
    Originally Posted by dahuja76 View Post
    This thread is to discuss the article titled:
    Linux Kernel Thread
    Thanks but what is this? Is it s a copy from a book/doc?

    I already wrote a documentation in steps myself. If you need it, email me.

    A good engineer, needs to document it properly. Please do NOT copy and paste from some others work. It is against the ethics, read IEEE ethics online.

    Open source, sharing information is cool. However, again, you MUST document in steps, make it clear, define platforms, kernel versions, any other info etc.

    Thanks
    CM

    by waisermehmood on Tue, 2012-10-23 19:04
    Quote:
    Originally Posted by cmontr View Post
    Thanks but what is this? Is it s a copy from a book/doc?

    I already wrote a documentation in steps myself. If you need it, email me.

    A good engineer, needs to document it properly. Please do NOT copy and paste from some others work. It is against the ethics, read IEEE ethics online.

    Open source, sharing information is cool. However, again, you MUST document in steps, make it clear, define platforms, kernel versions, any other info etc.

    Thanks
    CM

    Please send the document,

    by dijetlo on Fri, 2014-06-06 20:48
    I just want to thank you for this article, as a person with a growing interest in the topic, I found it extremely well written and informative. Thanks for taking the time to share this with the rest of us.

    by shamim101 on Sun, 2016-07-17 23:13
    how i disable dhcp in home network and assign static ip addresses on centos 7 by command line .
    my existing mode is GUI.

    by linuxtech1990 on Thu, 2017-01-26 21:33
    Thanks


      



    All times are GMT -5. The time now is 06:07 AM.

    Main Menu
    Advertisement
    Advertisement
    My LQ
    Write for LQ
    LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
    Main Menu
    Syndicate
    RSS1  Latest Threads
    RSS1  LQ News
    Twitter: @linuxquestions
    Open Source Consulting | Domain Registration