内核线程同步之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