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

C 如何立即取消Linux内核模块中工作队列的工作项?,c,linux,linux-kernel,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)

内核模块代码:

#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);
}
rmmod
挂起
cancel\u work\u sync
,它首先等待工作完成,直到计数结束

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

最小可运行示例


在Linux内核4.9中测试。

原子控制变量

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

#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);
}
计时器

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

另见:

#包括
#包括
#包括
#包括
模块许可证(“GPL”);
静态无效回调(无符号长数据);
静态无符号长一秒;
定义_定时器(mytimer,callback,0,0);
静态无效回调(无符号长数据)
{
pr_信息(“%u\n”,(未签名)jiffies);
mod_定时器(&mytimer,jiffies+onesec);
}
int init_模块(void)
{
一秒=毫秒到毫秒(1000);
mod_定时器(&mytimer,jiffies+onesec);
返回0;
}
空洞清理_模块(空洞)
{
del_定时器(&mytimer);
}

还有另一种方法可以通过信号停止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。

我认为您应该使用“取消工作”而不是“取消工作同步”。@rk1825谢谢!由于某些原因,我在重新编译后的插入时得到了“未知符号取消工作”,但它编译得很好!我将查看相关线程。@rk1825该符号显然未导出:即使您将找到非等待替换的
cancel\u work\u sync
destroy\u workqueue()。因此,您的模块不会发生任何更改。(实际上,您可以省略模块中的
取消\u工作\u同步()
调用)?是否要在执行模块的某个函数时卸载该模块?这会崩溃。或者,您是否希望在卸载模块时中断函数的执行,使其在卸载后不再继续?如果是这样,那就完全不同了。他们都可以睡觉,这意味着没有办法立即取消工作。@0安德烈:是的,这也是我发现的。我只发布尽可能接近我想做的事情的例子。
#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);
}
#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);
}
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);