如何在Linux内核模块中立即取消工作队列的工作项?

时间:2017-05-15 07:21:40

标签: c linux linux-kernel

内核模块代码:

#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/workqueue.h>

MODULE_LICENSE("GPL");

static struct workqueue_struct *queue;

static void work_func(struct work_struct *work)
{
    int i = 0;
    while (i < 5) {
        printk(KERN_INFO "%d\n", i);
        usleep_range(1000000, 1000001);
        i++;
    }
}

DECLARE_WORK(work, work_func);

int init_module(void)
{
    queue = create_workqueue("myworkqueue");
    queue_work(queue, &work);
    return 0;
}

void cleanup_module(void)
{
    cancel_work_sync(&work);    
    destroy_workqueue(queue);
}

如果我这样做:

insmod mymod.ko
rmmod mymod

rmmod挂起cancel_work_sync,等待工作完成,直到计算结束。

是否可以立即取消该工作项?

最小可运行示例here

在Linux内核4.9中测试。

2 个答案:

答案 0 :(得分:1)

还有另一种方法来停止带有信号的kthread。这种方法比您的方法更好,因为它不需要您的线程定期唤醒并使用kthread_should_stop()轮询stop变量。不会浪费CPU时间,它允许您的线程只要有需要就可以睡眠。

static int kthread_handle(void *param)
{
    allow_signal(SIGINT);
    allow_signal(SIGKILL);

    for (;;)
    {
        // ...
        // Some blocking functions such as kernel_read()/kernel_write()
        // ...
        if (signal_pending(current))
        {
            goto end;
        }

        // ...
        // Some interruptible functions
        // ...
        if (mutex_lock_interruptible(...) == -EINTR)
        {
            goto end;
        }
    }

end:
    while (!kthread_should_stop())
    {
        schedule();
    }

    return 0;
}

static int __init drv_init(void)
{
    // Create and start kernel thread
    kthread = kthread_run(kthread_handle, NULL, "kthread");

    return 0;
}

static void __exit drv_exit(void)
{
    send_sig(SIGKILL, kthread, 1);
    kthread_stop(kthread);
}

module_init(drv_init);
module_exit(drv_exit);

我不知道如何将信号发送到工作队列,因此该解决方案目前仅适用于kthreads。

答案 1 :(得分:0)

原子控制变量

我找不到停止在工作队伍中工作的方法,但使用简单的控制变量是一种可能的解决方案。

#include <linux/delay.h> /* usleep_range */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h> /* atomic_t */
#include <linux/workqueue.h>

MODULE_LICENSE("GPL");

static struct workqueue_struct *queue;
static atomic_t run = ATOMIC_INIT(1);

static void work_func(struct work_struct *work)
{
    int i = 0;
    while (atomic_read(&run)) {
        printk(KERN_INFO "%d\n", i);
        usleep_range(1000000, 1000001);
        i++;
        if (i == 10)
            i = 0;
    }
}

DECLARE_WORK(work, work_func);

int init_module(void)
{
    queue = create_workqueue("myworkqueue");
    queue_work(queue, &work);
    return 0;
}

void cleanup_module(void)
{
    atomic_set(&run, 0);
    destroy_workqueue(queue);
}

kthread kthread_stop

工作队列基于kthreads,工作队列在该示例中基本没用,所以我们可以直接使用kthreads。

kthread_stop等待线程返回。

另见:

kthreads中的信号处理似乎是一个论战主题,现在不可能:https://unix.stackexchange.com/questions/355280/how-signals-are-handled-in-kernel

#include <linux/delay.h> /* usleep_range */
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>

MODULE_LICENSE("GPL");

static struct task_struct *kthread;

static int work_func(void *data)
{
    int i = 0;
    while (!kthread_should_stop()) {
        printk(KERN_INFO "%d\n", i);
        usleep_range(1000000, 1000001);
        i++;
        if (i == 10)
            i = 0;
    }
    return 0;
}

int init_module(void)
{
    kthread = kthread_create(work_func, NULL, "mykthread");
    wake_up_process(kthread);
    return 0;
}

void cleanup_module(void)
{
    kthread_stop(kthread);
}

<强>定时器

直接在中断上下文中运行,因此更准确,但更受限制。

另请参阅:How to use timers in Linux kernel device drivers?

#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/timer.h>

MODULE_LICENSE("GPL");

static void callback(unsigned long data);
static unsigned long onesec;

DEFINE_TIMER(mytimer, callback, 0, 0);

static void callback(unsigned long data)
{
    pr_info("%u\n", (unsigned)jiffies);
    mod_timer(&mytimer, jiffies + onesec);
}

int init_module(void)
{
    onesec = msecs_to_jiffies(1000);
    mod_timer(&mytimer, jiffies + onesec);
    return 0;
}

void cleanup_module(void)
{
    del_timer(&mytimer);
}