OS/Linux

Linux kernel Test code

아르비스 2010. 5. 19. 17:39


[kthread.h]
#ifndef _KTHREAD_H
#define _KTHREAD_H
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/wait.h>

#include <linux/unistd.h>
#include <linux/semaphore.h>

/* a structure to store all information we need
   for our thread */
typedef struct kthread_struct
{
        /* private data */

        /* Linux task structure of thread */
        struct task_struct *thread;
        /* function to be started as thread */
        void (*function) (struct kthread_struct *kthread);
        /* semaphore needed on start and creation of thread. */
        struct semaphore startstop_sem;

        /* public data */

        /* queue thread is waiting on. Gets initialized by
           init_kthread, can be used by thread itself.
        */
        wait_queue_head_t queue;
        /* flag to tell thread whether to die or not.
           When the thread receives a signal, it must check
           the value of terminate and call exit_kthread and terminate
           if set.
        */
        int terminate;
        /* additional data to pass to kernel thread */
        void *arg;
} kthread_t;

/* prototypes */

/* start new kthread (called by creator) */
void start_kthread(void (*func)(kthread_t *), kthread_t *kthread);

/* stop a running thread (called by "killer") */
void stop_kthread(kthread_t *kthread);

/* setup thread environment (called by new thread) */
void init_kthread(kthread_t *kthread, char *name);

/* cleanup thread environment (called by thread upon receiving termination signal) */
void exit_kthread(kthread_t *kthread);

#endif



[kthread.c]
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/signal.h>
#include <linux/workqueue.h>
#include <linux/timer.h>

#include <linux/semaphore.h>
#include <linux/smp_lock.h>

#include "kthread.h"

static void kthread_launcher(void *data);
static DECLARE_DELAYED_WORK(kthread_wq, kthread_launcher);

/* private functions */
static void kthread_launcher(void *data)
{
        kthread_t *kthread = data;
        kernel_thread((int (*)(void *))kthread->function, (void *)kthread, 0);

}

/* public functions */

/* create a new kernel thread. Called by the creator. */
void start_kthread(void (*func)(kthread_t *), kthread_t *kthread)
{
        /* initialize the semaphore:
           we start with the semaphore locked. The new kernel
           thread will setup its stuff and unlock it. This
           control flow (the one that creates the thread) blocks
           in the down operation below until the thread has reached
           the up() operation.
         */
        init_MUTEX_LOCKED(&kthread->startstop_sem);

        /* store the function to be executed in the data passed to
           the launcher */
        kthread->function=func;

        /* create the new thread my running a task through keventd */

        /* initialize the work queue structure */
// need to changing task queue to work queue
          printk(" [Start kthread]\n");
          schedule_work(&kthread_wq);

//        kthread->tq.sync = 0;
//        INIT_LIST_HEAD(&kthread->tq.list);
//        kthread->tq.routine =  kthread_launcher;
//        kthread->tq.data = kthread;

        /* and schedule it for execution */
//        schedule_task(&kthread->tq);

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

/* stop a kernel thread. Called by the removing instance */
void stop_kthread(kthread_t *kthread)
{
        if (kthread->thread == NULL)
        {
                printk("stop_kthread: killing non existing thread!\n");
                return;
        }

        /* this function needs to be protected with the big
           kernel lock (lock_kernel()). The lock must be
           grabbed before changing the terminate
           flag and released after the down() call. */
        lock_kernel();

        /* initialize the semaphore. We lock it here, the
           leave_thread call of the thread to be terminated
           will unlock it. As soon as we see the semaphore
           unlocked, we know that the thread has exited.
        */
        init_MUTEX_LOCKED(&kthread->startstop_sem);

        /* We need to do a memory barrier here to be sure that
           the flags are visible on all CPUs.
        */
        mb();

        /* set flag to request thread termination */
        kthread->terminate = 1;

        /* We need to do a memory barrier here to be sure that
           the flags are visible on all CPUs.
        */
        mb();
// need to check
//        kill_proc(kthread->thread->pid, SIGKILL, 1);

        /* block till thread terminated */
        down(&kthread->startstop_sem);

        /* release the big kernel lock */
        unlock_kernel();

        /* now we are sure the thread is in zombie state. We
           notify keventd to clean the process up.
        */
// need to check
//        kill_proc(2, SIGCHLD, 1);

}

/* initialize new created thread. Called by the new thread. */
void init_kthread(kthread_t *kthread, char *name)
{
        /* lock the kernel. A new kernel thread starts without
           the big kernel lock, regardless of the lock state
           of the creator (the lock level is *not* inheritated)
        */
        lock_kernel();

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

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

        /* initialise wait queue */
        init_waitqueue_head(&kthread->queue);

        /* initialise termination flag */
        kthread->terminate = 0;

        /* set name of this process (max 15 chars + 0 !) */
        sprintf(current->comm, name);

        /* let others run */
        unlock_kernel();

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

}

/* cleanup of thread. Called by the exiting thread. */
void exit_kthread(kthread_t *kthread)
{
        /* we are terminating */

        /* lock the kernel, the exit will unlock it */
        lock_kernel();
        kthread->thread = NULL;
        mb();

        /* notify the stop_kthread() routine that we are terminating. */
        up(&kthread->startstop_sem);
        /* the kernel_thread that called clone() does a do_exit here. */

        /* there is no race here between execution of the "killer" and real termination
           of the thread (race window between up and do_exit), since both the
           thread and the "killer" function are running with the kernel lock held.
           The kernel lock will be freed after the thread exited, so the code
           is really not executed anymore as soon as the unload functions gets
           the kernel lock back.
           The init process may not have made the cleanup of the process here,
           but the cleanup can be done safely with the module unloaded.
        */

}

[thread_drv.c]
#include <linux/module.h>
#if defined(MODVERSIONS)
#include <linux/modversions.h>
#endif

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

#include "kthread.h"

#define NTHREADS 5

/* the variable that contains the thread data */
kthread_t example[NTHREADS];

/* prototype for the example thread */
static void example_thread(kthread_t *kthread);

/* load the module */
int init_module(void)
{
        int i;

        /* create new kernel threads */
        for (i=0; i <NTHREADS; i++)
                start_kthread(example_thread, &example[i]);

        return(0);
}

/* remove the module */
void cleanup_module(void)
{
        int i;

        /* terminate the kernel threads */
        for (i=0; i<NTHREADS; i++)
                stop_kthread(&example[i]);

        return;
}

/* this is the thread function that we are executing */
static void example_thread(kthread_t *kthread)
{
        /* setup the thread environment */
        init_kthread(kthread, "example thread");

        printk("hi, here is the kernel thread\n");

        /* an endless loop in which we are doing our work */
        for(;;)
        {
                /* fall asleep for one second */
                interruptible_sleep_on_timeout(&kthread->queue, HZ);

                /* We need to do a memory barrier here to be sure that
                   the flags are visible on all CPUs.
                */
                 mb();

                /* here we are back from sleep, either due to the timeout
                   (one second), or because we caught a signal.
                */
                if (kthread->terminate)
                {
                        /* we received a request to terminate ourself */
                        break;
                }

                /* this is normal work to do */
                printk("example thread: thread woke up\n");
        }
        /* here we go only in case of termination of the thread */

        /* cleanup the thread, leave */
        exit_kthread(kthread);

        /* returning from the thread here calls the exit functions */
}

[Makefile]
KERNELDIR =     /lib/modules/$(shell    uname   -r)/build

obj-m   := thread_drv.o
obj-m   += kthread.o
#obj-m  += thread_mod.o

#all:   thread_mod.o

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

#kthread.o:     kthread.c #kthread.h

#thread_drv.o:  thread_drv.c #kthread.h


KDIR    :=      /lib/modules/$(shell    uname   -r)/build
PWD     :=      $(shell pwd)

default:
        $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

clean:
        rm      -rf *.ko
        rm      -rf *.mod.*
        rm      -rf .*.cmd
        rm      -rf *.o
        rm      -rf *.symvers
        rm      -rf *.order
        rm      -rf *.markers
        rm      -rf .tmp_versions


--------------------
컴파일은 된다. 그러나.. 아직 insmod로 올려 보진 못했다..
잘되길 빌며..

tast queue를 work queue로 바꿔야 하는 중요한 일이 남았당 ㅠㅠ 휴~~