gpt4 book ai didi

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

转载 作者:太空狗 更新时间:2023-10-29 16:05:53 52 4
gpt4 key购买 nike

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

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

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

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

如何唤醒并停止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. 如果 take_from_list 返回一个有效的条目,consumer_kthread 对该条目做一些事情并调用 up(&sem_list_consumer) 来匹配调用到 take_from_list 中的 down_interruptible(&sem_list_consumer)

  2. 如果 take_from_list 返回 NULLconsumer_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;
}

修改

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

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

关于c - Linux 内核 - 如何停止等待信号量的 kthread?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40148586/

52 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com