内核线程同步之signal

对于内核线程,尤其是在同一模块里有多个时,我们想终止其中一个,而又不想卸载模块,此时signal就帮上我们一个大忙了,在设计时让线程接收信号并处理,想停止时发信号给它,而在内核里需要用到如下信号相关函数:
1.signal_pending
在Linux内核源码include/linux/sched.h文件里,有如下函数定义:
static inline int signal_pending(struct task_struct *p)
{
        return unlikely(test_tsk_thread_flag(p,TIF_SIGPENDING));
}
该函数用于判断指定线程有没有接收到信号,即信号有没有被阻塞。
上面的参数我们一般是判断当前线程,故而会乃至include/asm-generic/current.h里的current宏,定义如下:
#define current get_current()
2.allow_signal
在Linux内核源码kernel/exit.c有该函数的实现,而在include/linux/sched.h文件里有如下声明:
extern int allow_signal(int);
其参数是SIGKILL等宏,在include/uapi/asm-generic/signal.h文件中有相应的宏定义。
内核线程允许接收信号后,还需要确保该线程是可打断的,需要用到如下宏:
1.set_current_state
在include/linux/sched.h文件中有如下宏定义:
#define set_current_state(state_value) \
        set_mb(current->state, (state_value))
2.TASK_INTERRUPTIBLE
在同一头文件中如下宏定义:
#define TASK_INTERRUPTIBLE 1
上面两个宏结合使用用于配置当前线程的状态是可被中断的。
接下来上代码吧:
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/delay.h>

static struct task_struct * slam_thread = NULL;
static int is_signal_exited = 0;

static int slam_func(void *data)
{
        printk("<xinu>%s()!\n", __func__);
        allow_signal(SIGKILL);
        mdelay(1000);

        while(!signal_pending(current) && !kthread_should_stop())
        {
                printk("<xinu>jiffies(%lu)\n", jiffies);
                set_current_state(TASK_INTERRUPTIBLE);
                schedule_timeout(msecs_to_jiffies(5000));
        }

        printk("<xinu>leave slam_func!\n");

        is_signal_exited = 1;

        return 0;
}

static __init int kthread_signal_example_init(void)
{
        slam_thread = kthread_run(slam_func, NULL, "slam");
        printk("<xinu>kthread_signal_example_init()!\n");

        return 0;
}

static __exit void kthread_signal_example_exit(void)
{
        if(!is_signal_exited && !IS_ERR(slam_thread))
        {
                kthread_stop(slam_thread);
        }
        printk("<xinu>%s()!\n", __FUNCTION__);
}

module_init(kthread_signal_example_init);
module_exit(kthread_signal_example_exit);
相应的Makefile文件内容如下:
obj-m += kthread_signal_example.o

CUR_PATH:=$(shell pwd)
LINUX_KERNEL_PATH:=/home/xinu/linux-3.13.6

all:
        make -C $(LINUX_KERNEL_PATH) M=$(CUR_PATH) modules

clean:
        make -C $(LINUX_KERNEL_PATH) M=$(CUR_PATH) clean
对应的源码文件目录树如下:
/home/xinu/xinu/linux_kernel_driver_l1/kthread_signal_example/
├── kthread_signal_example.c
└── Makefile
当我们编译加载模块后,使用ps aux命令可以看到有如下一行信息:
root 11390 0.0 0.0 0 0 ? S 17:46 0:00 [slam]
其中11390就是该线程的PID,此时我们通过使用kill -SIGKILL 11390发信号终止线程和rmmod卸载模块的方式来对比dmesg的输出情况,就可以知道信号在什么时候使用,赢得每执行一条命令都看下dmesg信息。
参考网址:
http://www.cnblogs.com/zhuyp1015/archive/2012/06/13/2548494.html
http://lwn.net/Articles/53563/
http://reneeciou.blogspot.com/2013/08/linux-kernel-threads.html

评论

此博客中的热门博文

I/O映射之I/O端口

通过Netlink检测网线插拔

使用seq_file