Linux 内核 - 如何停止等待信号量的 kthread?

2024-06-26

在编写 Linux 内核模块时,我遇到了一个 kthread 问题,在等待信号量解锁时无法唤醒该 kthread。这会导致线程不可停止并且rmmod尝试卸载模块时冻结。

请注意:该模块在 3.10 内核上运行,我无法将其更新到较新的版本(客户要求在具有 3.10 内核的现有 CentOS 7 上运行)。

以下是模块源代码中有趣的部分。它代表一个简单的生产者消费者问题,列表的大小不受限制(因此不需要生产者信号量)并由互斥锁保护。从列表中获取某些内容的函数由信号量保护,该信号量由生产者增加并由消费者减少。生产者函数是从未在此代码片段中显示的外部事件(实际上是字符设备)调用的,以保持尽可能小。除了模块卸载之外,该过程运行良好。

导致冻结的部分在代码片段中用注释标记。我知道停止 kthread 的唯一方法是调用kthread_stop在它上面,在这种情况下会失败,因为它显然无法唤醒休眠线程。因为它等待线程退出,所以调用永远不会返回,模块也不会卸载。

如何唤醒并停止等待信号量成功卸载模块的 kthread?

列表实现:

#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/semaphore.h>

static LIST_HEAD(list);
DEFINE_MUTEX(list_lock);
DEFINE_SEMAPHORE(sem_list_consumer);

void add_to_list(struct *some_struct) {
    int rv = mutex_lock_interruptible(&list_lock);
    if(rv != 0) {
        return;
    }

    list_add(&some_struct->list, &list);
    mutex_unlock(&list_lock);
    up(&sem_list_consumer);
}

struct some_struct * take_from_list() {
    int rv;
    some_struct *entry;

    /* this is where the kthread will freeze when module is unloaded */
    rv = down_interruptible(&sem_list_consumer);
    if(rv != 0) {
        return NULL;
    }

    rv = mutex_lock_interruptible(&list_lock);
    if(rv != 0) {
        up(&sem_list_consumer);
        return NULL;
    }

    if (list_empty(&list)) {
        mutex_unlock(&list_lock);
        return NULL;
    } else {
        entry = list_last_entry(&list, struct some_struct, list);
        if (entry) {
            list_del(&entry->list);
        }
    }

    mutex_unlock(&list_lock);
    return entry;
}

消费者kthread实现:

#include <linux/kthread.h>
#include <linux/sched.h>

int consumer_kthread(void *data) {
    struct some_struct *entry;

    set_current_state(TASK_INTERRUPTIBLE);
    while (!kthread_should_stop()) {
        /* Here the function including the semaphore is called */
        entry = take_from_list();
        if(entry != NULL) {
            /* Do something with 'entry' here */
        } else {
            /* Some handling of returned NULL pointers */
        }

        set_current_state(TASK_INTERRUPTIBLE);
    }
    set_current_state(TASK_RUNNING);

    return 0;
}

模块实现:

#include <linux/init.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/sched.h>

static struct task_struct *consumer_task;

static int __init initModule(void) {
    consumer_task = kthread_run(consumer_kthread, NULL, "list-consumer");

    return 0;
}

static void __exit exitModule(void) {
    /* this call will cause rmmod to freeze forever */
    kthread_stop(consumer_task);
}

module_init(initModule);
module_exit(exitModule);

MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("My Module");

缺少的代码意味着这个答案只能使用有根据的猜测。

以下是我对您丢失的代码的假设:

  1. If take_from_list返回有效条目,consumer_kthread对条目进行一些操作并调用up(&sem_list_consumer)匹配调用down_interruptible(&sem_list_consumer) in take_from_list.

  2. If take_from_list回报NULL, consumer_kthread做一些处理NULL指针,并假设sem_list_consumer信号量处于原始状态。

鉴于这些假设,存在一个错误take_from_list因为它有时会返回NULL不打电话up(&sem_list_consumer)第一的。这意味着任何后续调用take_from_list将阻止调用down_interruptible(&sem_list_consumer)直到它们被信号打断。要修复该错误,请更改take_from_list每当信号量返回时,始终将其保持在其离开时的状态NULL:

struct some_struct * take_from_list() {
    int rv;
    some_struct *entry;

    rv = down_interruptible(&sem_list_consumer);
    if(rv != 0) {
        return NULL;
    }

    rv = mutex_lock_interruptible(&list_lock);
    if(rv != 0) {
        up(&sem_list_consumer);
        return NULL;
    }

    if (list_empty(&list)) {
        mutex_unlock(&list_lock);
        up(&sem_list_consumer);  /* <-- this line was missing */
        return NULL;
    } else {
        entry = list_last_entry(&list, struct some_struct, list);
        if (entry) {
            list_del(&entry->list);
        }
    }

    mutex_unlock(&list_lock);
    return entry;
}

AMENDED

如果有某个地方缺少代码consumer_kthread将其自身添加到等待队列并进入睡眠状态,调用kthread_should_stop()应包含在唤醒条件中。唤醒条件应由其他条件满足 OR (||) kthread_should_stop().

致电kthread_stop(consumer_task)从你的exitModule函数将唤醒消费者线程。如果它正在等待一个事件,它要做的第一件事就是检查唤醒条件,如果不满足则返回睡眠状态。通过包括kthread_should_stop()作为可能的唤醒条件之一,您确保使用者线程不会立即返回睡眠状态。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux 内核 - 如何停止等待信号量的 kthread? 的相关文章

  • 根据当前文化调用不同(本地化)视图

    我在用着LocalizationAttribute它实现了ActionFilterAttribute本地化视图 我简单地说 Localize 在控制器上 我使用 LocalizeStrings resx 文件根据当前线程上的语言进行应用 一
  • MVC。网络错误:初始化字符串的格式不符合从索引 0 开始的规范

    我的连接字符串是
  • HTML 文档

    有没有一个工具可以从 VS2010 生成的 XML 文档文件生成 HTML 页面 我在谷歌上搜索了这样的工具 但没有找到 我下载并安装了SandCastle 但我不明白如何使用它 尝试使用Sandcastle 帮助文件生成器 http sh
  • gets 和 scanf 有什么区别?

    如果代码是 scanf s n message vs gets message 有什么区别 似乎两者都获取消息的输入 基本区别 参考您的特定场景 scanf 遇到一个时结束接受输入whitespace newline or EOF gets
  • 在子目录中构建共享库

    我正在尝试构建一个使用一些 C 代码的 R 包 我有一个编译为可执行文件的 C 库 可以从命令行调用 有一个与之关联的 Makefile 我正在尝试获取信息here http cran r project org doc manuals R
  • 使用索引避免迭代器失效,维护干净的接口

    我创建了一个MemoryManager
  • 在宏中使用 # [重复]

    这个问题在这里已经有答案了 请解释一下代码 include
  • 用 C# 中的字典中的值替换字符串中的单词

    我有一个简单的dictionary像这样 var fruitDictionary new Dictionary
  • 如何处理作为参数传递到方法中的 Lambda 表达式 - C# .NET 3.5

    我对 Lambda 表达式的了解有点不稳定 虽然我可以编写使用 Lambda 表达式 又名 LINQ 的代码 但我正在尝试编写自己的方法 该方法采用一些 Lambda 表达式类型的参数 背景 我正在尝试编写一个方法 该方法从任何其他对象类型
  • 在 Windows 服务中使用 OleDb 从 Excel 读取数据?

    免责声明 我知道这是一种不好的做事方式 这是我们与客户的唯一选择 Problem 我们需要每隔 x 时间从 Excel 文件读取数据 数据通过第三方 Excel 插件不断变化 应用程序的环境是 Windows XP SP1 和 Net 2
  • 如何在 C++ 中从模板基类的构造函数调用模板超类的构造函数?

    我正在使用 sublimetext3 用 c 进行编程 我的程序有一个名为 Array 的超类和一个名为 IntArray 的子类 这两个类都是模板类 目前 我在编译该程序时遇到问题 它不断在我的 IntArray cpp 文件中给出错误
  • ASP.NET 中的 thread.sleep

    我正在为我的网站模拟彗星实时馈送协议 因此在我的控制器中我添加 while nothing new before timeout Thread Sleep 1000 但我注意到添加此功能后整个网站变慢了 调试后我得出结论 当我打电话时Thr
  • argc 和 argv 在 Windows 中没有用吗?

    在 Linux 中 argc 和 argv 计算终端中的参数 但在 Windows 中 我找不到放置第二个参数的地方 事实上 每次我运行该程序时 它都会创建那个丑陋的黑色窗口 我什至没有机会给出任何争论 那么这两个变量在Windows平台下
  • 内存不足异常

    我正在使用 C 和 asp net 开发一个网络应用程序 我一直收到内存不足的异常 该应用程序的作用是从数据源读取一堆记录 产品 可能是数百 数千 通过向导中的设置处理这些记录 然后使用处理的产品信息更新不同的数据源 虽然有多个 DB 类
  • 如何在 Windows 8.1 上打开多个 Visual Studio 窗口? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我使用的是 Windows 7 我能够启动多个 Visual Studio 并同时工作 现在我有 Windows 8 1 操作系统 每当我
  • SwingWorker 和 Executor 的区别

    我正在使用 SwingWorker 在我正在制作的应用程序上执行一些重负载任务 虽然今天我遇到了 Executor 类和这个例子 Executors newCachedThreadPool execute new Runnable publ
  • 将 R 值传递给采用 L 值的函数时出现过载歧义

    我有 2 个重载函数 一个采用 L 值 另一个采用 R 值 目的是让该函数可以像这样调用 Obj obj foo obj OR foo Obj 所以 我写了2个重载函数 template
  • 在 C# 中为 ListBox 分配数据源时,如何从 ListBox 中删除所选项目?

    在 C 中为 ListBox 分配数据源时 如何从 ListBox 中删除所选项目 尝试删除时出现错误 设置 DataSource 属性后 无法修改项目集合 但是当我尝试从数据源 数据表 中删除项目时 它会抛出错误 因为 数据行不在当前行集
  • 矩阵行列式算法 C++

    我是编程新手 我一直在寻找一种找到矩阵行列式的方法 我在网上找到了这段代码 但我很难理解这里的算法 我对递归的基础没有问题 但继续和主循环我很难理解 非常感谢任何可以向我解释该算法的人 int determ int a MAX MAX in
  • 有关 Endian 性和 .Net 的详细信息?

    我有几个关于字节顺序的问题 这些问题足够相关 我保证将它们作为一个问题提出 1 字节顺序是由 Net还是由硬件决定的 2 如果是由硬件决定的 我怎样才能在C 中找出硬件的字节序 3 字节序是否影响二进制交互 例如 OR AND OR 或移位

随机推荐