程序员

内核线程同步之signal

2019-10-20  本文已影响0人  网路元素

对于内核线程,尤其是在同一模块里有多个时,我们想终止其中一个,而又不想卸载模块,此时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

上一篇下一篇

猜你喜欢

热点阅读