본문 바로가기
Research/Linux

kernel thread 사용하기

by sunnyan 2005. 10. 25.
728x90
http://linuxkernel.net/faq/index.php?cmd=read§ion=kernelprog-advanced2&num=3

kernel thread 사용하기
Writer: 이호 (flyduck@linuxkernel.net) Date & Time: 2003-03-26 15:23:58

Q : 모듈에서 커널 쓰레드를 만들고, 다른 프로세스를 실행하려면 어떻게 합니까?

A : 커널 쓰레드를 만드는 데에는 kernel_thread() 함수를 사용합니다. 이 함수의 prototype은 include/asm/processor.h에 정의되어 있습니다.

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

이 함수의 사용법은 첫번째 인자로 쓰레드로 실행할 함수의 포인터를 넘기고, 두번째 인자로 쓰레드 함수에 전달할 인자를, 세번째로 flag를 전달합니다. 그러면 커널은 쓰레드를 생성하여, 넘겨준 함수를 실행합니다. 프로세스를 생성하려면 생성된 쓰레드에서 execve() 함수를 사용합니다. 이러한 예로는 init/main.c에 있는 init()나 do_linuxrc()가 있습니다.
init() 프로세스를 생성하는 예 (init/main.c) :

start_kernel() :
    kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);

int init(void *unused)
{
    if (open"/dev/console", O_RDWR, 0) < 0)
        printk("Warning: unable to open an initial console.n");

    (void) dup(0);
    (void) dup(0);

    execve("/sbin/init", argv_init, envp_init);
}


다음은 Martin Frey가 작성한 글로 모듈에서 커널 쓰레드를 생성하는 방법에 대한 것입니다. 이 글의 원본은 http://www.scs.ch/~frey/linux/kernelthreads.html에 있습니다.

Linux Kernel Threads in Device Drivers

Purpose

This examples shows how to create and stop a kernel thread.
The driver is implemented as a loadable module. In the init_module() routine a kernel thread is created. This kernel thread sleeps one second, wakes up, prints a message and falls asleep again. On unload of the module (cleanup_module), the kernel thread is killed.
The example has been tested with Linux kernel 2.2.14 on Intel (several, uni and multi processor) and Alpha platform (COMPAQ DS20, SMP and COMPAQ ES40, SMP).
The example is based on the code used for the SCSI error handler in the Linux kernel.

Functions in example

  • launch_thread: creates a new kernel thread
  • kill_thread: kills the thread. Must not be called by the thread to be terminated itself.
  • setup_thread: sets the environment of the new threads. Is to be called out of the created thread.
  • leave_thread: needs to be called by the thread to be terminated on exit

Launch of new Thread

A new thread is created with kernel_thread(). For whatever reason on the Alpha platform __kernel_thread() is used. There is a small wrapper function.
Our start function launch_thread() uses a semaphore to block until the new thread is running. A down() blocks the launch_thread() routine. The corresponding up() call is in setup_thread().

Kill of new Thread

kill_thread() sets a flag that the thread uses to determine whether do die or not and sends a SIGKILL to the thread. This signal causes the thread to be woken up. On wakeup it will check for the flag and then terminate itself. With a semaphore the kill_thread() function blocks until the thread terminated.

Setup of new Thread

Within the new created thread, setup_thread() needs to be called. This function releases the memory map from the insmod process, sets the session leader and process group to the init process, sets an new name for the process, disconnects from the filesystem and connects to the filesystem usage of the init process, sets a signal mask, initialises a wait queue and the termination flag. With a up() call it notifies the creator that the setup is done.

Exit of new Thread

When the thread receives the notification to terminate itself, is calls the leave_thread() function. Leave thread releases the bindings to the filesystem of the init process and notifies the kill_thread() function that it terminated with an up() call.

The new Thread itself

The new thread is implemented in the example_thread() function. It runs an endless loop (for(;;)). In the loop it falls asleep with the interruptible_sleep_on_timeout() function. It comes out of this function either when the timeout expires or when a signal got caught.
The "work" in the thread is to print out a message with printk.

Example Device Driver Code

#include <linux/config.h>
#include <linux/version.h>
#include <linux/module.h>
#if defined(MODVERSIONS)
#include <linux/modversions.h>
#endif

#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/smp_lock.h>

#include <asm/signal.h>

/* device open */
int threaddrv_open(struct inode *inode, struct file *file);
/* device close */
int threaddrv_release(struct inode *inode, struct file *file);

/* a structure to store all information we need
   for our thread */
typedef struct
{
        /* Linux task structure of thread */
        struct task_struct *thread;
        /* semaphore needed on start and creation of thread */
        struct semaphore   startstop_sem;
        /* flag to tell thread whether to die or not */
        int terminate;
        /* queue thread is waiting on */
        struct wait_queue *queue;
                /* process id of the thread */
                pid_t pid;
} my_threads;

/* prototype of our example thread */
void example_thread(my_threads *thread);

/* prototype to create a new thread */
static void launch_thread(int (*func)(void *), my_threads *thread);

/* prototype to kill a running thread */
static void kill_thread(my_threads *thread);

/* the ordinary device operations */
static struct file_operations threaddrv_fops =
{
        NULL,    /* lseek */
        NULL,    /* read*/
        NULL,    /* write */
        NULL,    /* readdir */
        NULL,    /* poll */
        NULL,    /* ioctl */
        NULL,    /* mmap */
        threaddrv_open,    /* open */
        NULL,    /* flush */
        threaddrv_release, /* release */
        NULL     /* fsync */
};

/* the variable that contains the thread data */
my_threads example;

/* load the module */
int init_module(void)
{
        /* register the device */
        if (register_chrdev(250, "threaddrv", &threaddrv_fops)) {
                printk("threaddrv: unable to get major number 250n");
                return (-EIO);
        }

        /* create a new kernel thread */
        launch_thread((int (*)(void *))example_thread, &example);
        
        return(0);
}

/* remove the module */
void cleanup_module(void)
{
        /* terminate the kernel thread */
        kill_thread(&example);
        
        /* unregister the device */
        unregister_chrdev(250, "threaddrv");
        return;
}
/* device open method */
int threaddrv_open(struct inode *inode, struct file *file)
{
        MOD_INC_USE_COUNT;
        return(0);
}

/* device close method */
int threaddrv_release(struct inode *inode, struct file *file)
{
        MOD_DEC_USE_COUNT;
        return(0);
}

/* private functions */

/* this is way to complicated. Why is the kernel thread interface
   different on Alpha platform ?
*/
#ifdef __alpha
extern long __kernel_thread(unsigned long, int (*)(void *), void *);

static inline long kernel_thread(int (*func)(void *), void *arg, unsigned long flags)
{
        return __kernel_thread(flags | CLONE_VM, func, arg);
}
#endif

/* create a new kernel thread */
static void launch_thread(int (*func)(void *), my_threads *thread)
{
        thread->startstop_sem = MUTEX_LOCKED;

        /* create the new thread */
        kernel_thread(func, (void *)thread, 0);

        /* wait till it has reached the setup_thread routine */
        down(&thread->startstop_sem);
               
}

static void kill_thread(my_threads *thread)
{
        if (thread->thread == NULL)
        {
                printk("thread_drv: killing non existing thread!n");
                return;
        }

        /* get the global lock to prevent a race in the
           fall-asleep phase of the thread */
        thread->startstop_sem = MUTEX_LOCKED;
#ifdef __alpha
        /* on Alpha platfrom we need to do a memory barrier
           here to be sure that the flags are visible on
           all CPUs. This is not portable, we should e.g.
           use atomic_t here for passing the information.
        */
        mb();
#endif

        /* set flag to request thread termination */
        thread->terminate = 1;
#ifdef __alpha
        /* on Alpha platfrom we need to do a memory barrier
           here to be sure that the flags are visible on
           all CPUs. This is not portable, we should e.g.
           use atomic_t here for passing the information.
        */
        mb();
#endif
        kill_proc(SIGKILL, thread->pid, 1);
       
        /* block till thread terminated */
        down(&thread->startstop_sem);
}


static void setup_thread(my_threads *thread)
{
        /* lock the kernel */
        lock_kernel();
        /* release insmod's memory map, use the kernel one's */
        exit_mm(current);
        /* set session leader and process group to init process */
        current->session = 1;
        current->pgrp = 1;
        /* set name of this process (max 15 chars + 0 !) */
        sprintf(current->comm, "example thread");

        /* disconnect from the fs usage */
        exit_files(current);
        exit_fs(current);

        /* fill in thread structure */
        thread->thread = current;

        /* set signal mask to what we want to respond */
        siginitsetinv(&current->blocked, sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM));

        /* initialise wait queue */
        thread->queue = NULL;

        /* initialise termination flag */
        thread->terminate = 0;
        
        /* let others run */
        unlock_kernel();

        /* tell the creator that we are ready and let him run */
        up(&thread->startstop_sem);
        
}

static void leave_thread(my_threads *thread)
{
        /* we are terminating */

                /* lock the kernel, the exit will unlock it */
        thread->thread = NULL;
#ifdef __alpha
        mb();
#endif
        /* notify the kill_thread() routine that we are terminating. */
        up(&thread->startstop_sem);
}

/* this is the thread function that we are executing */
void example_thread(my_threads *thread)
{
        /* setup the thread environment */
        setup_thread(thread);

        printk("hi, here is the kernel threadn");
        
        /* an endless loop in which we are doing our work */
        for(;;)
        {
                /* fall asleep for one second */
                interruptible_sleep_on_timeout(&thread->queue, HZ);

#ifdef __alpha
                /* on the Alpha platform we need a memory barrier here in
                   order to be sure that the flag below is visible on all
                   CPUs.
                */
                mb();
#endif
                /* here we are back from sleep, either due to the timeout
                   (one second), or because we caught a signal.
                */
                if (thread->terminate)
                {
                        /* we received a request to terminate ourself */
                        break;    
                }
                
                /* this is normal work to do */
                printk("example thread: thread woke upn");
        }
        /* here we go only in case of termination of the thread */

        /* cleanup the thread, leave */
        leave_thread(thread);
}

Makefile for Intel Platform

CFLAGS  = -D__KERNEL__ -I/usr/src/linux-2.2.14/include/linux -Wall -Wstrict-prototypes -O2 
          -fomit-frame-pointer -pipe -fno-strength-reduce -m486 -malign-loops=2 
          -malign-jumps=2 -malign-functions=2 -DCPU=686 -DMODULE -DMODVERSIONS 
          -include /usr/src/linux-2.2.14/include/linux/modversions.h   -DEXPORT_SYMTAB

thread_mod.o:     thread_drv.o
        ld -r -o thread_mod.o thread_drv.o

thread_drv.o:     thread_drv.c
        gcc $(CFLAGS) -c thread_drv.c

Makefile for Alpha Platform

CFLAGS  = -D__KERNEL__ -I/usr/src/linux-2.2.14.frey.modified/include/linux -Wall -Wstrict-prototypes -O2 
          -fomit-frame-pointer -fno-strict-aliasing -mno-fp-regs -ffixed-8 
          -D__SMP__ -DMODULE 
          -include /usr/src/linux-2.2.14.frey.modified/include/linux/modversions.h

thread_mod.o:     thread_drv.o
        ld -r -o thread_mod.o thread_drv.o

thread_drv.o:     thread_drv.c
        gcc $(CFLAGS) -c thread_drv.c

Comments, Corrections

Please send comments, corrections etc. to the address below.
frey@scs.ch

by flyduck & Martin Frey 2000/07/06

Add a comment


Last Updated : 2003/03/26
First Written : 2003/03/07


728x90

'Research > Linux' 카테고리의 다른 글

.vimrc  (0) 2005.11.07
프로세스 관리  (0) 2005.10.25
Ipsysctl tutorial 1.0.4  (0) 2005.10.24
왜 커널을 컴파일 할때 -msoft-float 옵션을 사용합니까?  (0) 2005.10.07
terminfo  (0) 2005.09.23