kthread 在没有 运行 的情况下停止
kthread stopped without running
如果我使用 kthread_run
创建内核线程,然后立即 kthread_stop
,内核线程可能会在没有 运行ning 的情况下停止。我在Linux-5.4.73
中查看了kthread_run
和kthread_stop
的源代码
/**
* kthread_run - create and wake a thread.
* @threadfn: the function to run until signal_pending(current).
* @data: data ptr for @threadfn.
* @namefmt: printf-style name for the thread.
*
* Description: Convenient wrapper for kthread_create() followed by
* wake_up_process(). Returns the kthread or ERR_PTR(-ENOMEM).
*/
#define kthread_run(threadfn, data, namefmt, ...) \
({ \
struct task_struct *__k \
= kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
if (!IS_ERR(__k)) \
wake_up_process(__k); \
__k; \
})
/**
* kthread_stop - stop a thread created by kthread_create().
* @k: thread created by kthread_create().
*
* Sets kthread_should_stop() for @k to return true, wakes it, and
* waits for it to exit. This can also be called after kthread_create()
* instead of calling wake_up_process(): the thread will exit without
* calling threadfn().
*
* If threadfn() may call do_exit() itself, the caller must ensure
* task_struct can't go away.
*
* Returns the result of threadfn(), or %-EINTR if wake_up_process()
* was never called.
*/
int kthread_stop(struct task_struct *k)
{
struct kthread *kthread;
int ret;
trace_sched_kthread_stop(k);
get_task_struct(k);
kthread = to_kthread(k);
set_bit(KTHREAD_SHOULD_STOP, &kthread->flags);
kthread_unpark(k);
wake_up_process(k);
wait_for_completion(&kthread->exited);
ret = k->exit_code;
put_task_struct(k);
trace_sched_kthread_stop_ret(ret);
return ret;
}
看来内核线程应该在kthread_stop
return之前就被唤醒了,但也有可能没有。我真的很困惑,有人可以帮助我吗?
我的测试代码如下
test1.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/semaphore.h>
#include <linux/spinlock.h>
MODULE_LICENSE("GPL");
static struct task_struct *t1;
static struct task_struct *t2;
static struct task_struct *t3;
static int func(void *__para)
{
const char *msg = (const char *)__para;
printk("%s %s\n", __func__, msg);
/* Wait for kthread_stop */
set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
schedule();
set_current_state(TASK_INTERRUPTIBLE);
}
set_current_state(TASK_RUNNING);
printk("%s %s return\n", __func__, msg);
return 0;
}
static int __init start_init(void)
{
printk(KERN_INFO "Thread Creating...\n");
t1 = kthread_run(func, "t1", "t1");
if (IS_ERR(t1)) {
WARN_ON(1);
return 0;
}
t2 = kthread_run(func, "t2", "t2");
if (IS_ERR(t2)) {
WARN_ON(1);
return 0;
}
printk("Stopping t2\n");
kthread_stop(t2);
printk("t2 stopped\n");
t3 = kthread_run(func, "t3", "t3");
if (IS_ERR(t3)) {
WARN_ON(1);
return 0;
}
return 0;
}
static void __exit end_exit(void)
{
printk(KERN_INFO "Cleaning Up...\n");
if (IS_ERR(t1))
return;
printk("Stopping t1\n");
kthread_stop(t1);
printk("t1 stopped\n");
printk("Stopping t3\n");
kthread_stop(t3);
printk("t3 stopped\n");
}
module_init(start_init)
module_exit(end_exit)
生成文件
obj-m += test1.o
all:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=`pwd`
clean:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=`pwd` clean
命令运行
sudo insmod test1.ko
sudo rmmod test1
第一个运行
的dmesg
[10914.046211] Thread Creating...
[10914.046515] func t1
[10914.046530] Stopping t2
[10914.046531] func t2
[10914.046533] func t2 return
[10914.046538] t2 stopped
[10914.046555] func t3
[10938.895544] Cleaning Up...
[10938.895545] Stopping t1
[10938.895552] func t1 return
[10938.895561] t1 stopped
[10938.895562] Stopping t3
[10938.895566] func t3 return
[10938.895587] t3 stopped
t2在本次停止前已经执行
第二个的dmesg运行
[10940.775771] Thread Creating...
[10940.776109] func t1
[10940.776126] Stopping t2
[10940.776138] t2 stopped
[10940.776162] func t3
[10956.375606] Cleaning Up...
[10956.375607] Stopping t1
[10956.375613] func t1 return
[10956.375674] t1 stopped
[10956.375674] Stopping t3
[10956.375678] func t3 return
[10956.375697] t3 stopped
t2 没有 运行ning 就停止了。但是 t1 和 t3 在停止之前没有立即停止 运行。
(此答案对应 Linux 内核版本 5.4。)
新创建的内核线程任务执行“kernel/kthread.c”中的函数kthread
。如果一切顺利,kthread
将调用 kthread_run
(或 kthread_create
的)threadfn
参数引用的线程函数。然而,在调用threadfn
函数指针之前的最后测试是检查内核线程的KTHREAD_SHOULD_STOP
位。如果设置了 KTHREAD_SHOULD_STOP
位,则不会调用 threadfn
函数指针,新的内核线程任务将调用 do_exit
,退出代码为 -EINTR
。 kthread
函数末尾的相关代码如下:
ret = -EINTR;
if (!test_bit(KTHREAD_SHOULD_STOP, &self->flags)) {
cgroup_kthread_ready();
__kthread_parkme(self);
ret = threadfn(data);
}
do_exit(ret);
虽然kthread_run
在returning之前唤醒了新创建的内核线程任务,但是可以调用kthread_stop
函数并设置内核线程的KTHREAD_SHOULD_STOP
位在内核线程到达其 KTHREAD_SHOULD_STOP
位的最终检查之前调用 threadfn
函数指针。在这种情况下,内核线程将在 threadfn
函数指针未被调用的情况下退出。
OP的原始代码可以更改为打印kthread_stop
的return值,如下所示:
int exit_code;
/* ... */
printk("Stopping t2\n");
exit_code = kthread_stop(t2);
printk("t2 stopped, exit code %d\n", exit_code);
然后它应该显示线程 t2
以代码 -EINTR
(可能是 -4
)退出,如果它在入口函数 func
被调用之前被停止。
如果我使用 kthread_run
创建内核线程,然后立即 kthread_stop
,内核线程可能会在没有 运行ning 的情况下停止。我在Linux-5.4.73
kthread_run
和kthread_stop
的源代码
/**
* kthread_run - create and wake a thread.
* @threadfn: the function to run until signal_pending(current).
* @data: data ptr for @threadfn.
* @namefmt: printf-style name for the thread.
*
* Description: Convenient wrapper for kthread_create() followed by
* wake_up_process(). Returns the kthread or ERR_PTR(-ENOMEM).
*/
#define kthread_run(threadfn, data, namefmt, ...) \
({ \
struct task_struct *__k \
= kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
if (!IS_ERR(__k)) \
wake_up_process(__k); \
__k; \
})
/**
* kthread_stop - stop a thread created by kthread_create().
* @k: thread created by kthread_create().
*
* Sets kthread_should_stop() for @k to return true, wakes it, and
* waits for it to exit. This can also be called after kthread_create()
* instead of calling wake_up_process(): the thread will exit without
* calling threadfn().
*
* If threadfn() may call do_exit() itself, the caller must ensure
* task_struct can't go away.
*
* Returns the result of threadfn(), or %-EINTR if wake_up_process()
* was never called.
*/
int kthread_stop(struct task_struct *k)
{
struct kthread *kthread;
int ret;
trace_sched_kthread_stop(k);
get_task_struct(k);
kthread = to_kthread(k);
set_bit(KTHREAD_SHOULD_STOP, &kthread->flags);
kthread_unpark(k);
wake_up_process(k);
wait_for_completion(&kthread->exited);
ret = k->exit_code;
put_task_struct(k);
trace_sched_kthread_stop_ret(ret);
return ret;
}
看来内核线程应该在kthread_stop
return之前就被唤醒了,但也有可能没有。我真的很困惑,有人可以帮助我吗?
我的测试代码如下
test1.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/semaphore.h>
#include <linux/spinlock.h>
MODULE_LICENSE("GPL");
static struct task_struct *t1;
static struct task_struct *t2;
static struct task_struct *t3;
static int func(void *__para)
{
const char *msg = (const char *)__para;
printk("%s %s\n", __func__, msg);
/* Wait for kthread_stop */
set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
schedule();
set_current_state(TASK_INTERRUPTIBLE);
}
set_current_state(TASK_RUNNING);
printk("%s %s return\n", __func__, msg);
return 0;
}
static int __init start_init(void)
{
printk(KERN_INFO "Thread Creating...\n");
t1 = kthread_run(func, "t1", "t1");
if (IS_ERR(t1)) {
WARN_ON(1);
return 0;
}
t2 = kthread_run(func, "t2", "t2");
if (IS_ERR(t2)) {
WARN_ON(1);
return 0;
}
printk("Stopping t2\n");
kthread_stop(t2);
printk("t2 stopped\n");
t3 = kthread_run(func, "t3", "t3");
if (IS_ERR(t3)) {
WARN_ON(1);
return 0;
}
return 0;
}
static void __exit end_exit(void)
{
printk(KERN_INFO "Cleaning Up...\n");
if (IS_ERR(t1))
return;
printk("Stopping t1\n");
kthread_stop(t1);
printk("t1 stopped\n");
printk("Stopping t3\n");
kthread_stop(t3);
printk("t3 stopped\n");
}
module_init(start_init)
module_exit(end_exit)
生成文件
obj-m += test1.o
all:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=`pwd`
clean:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=`pwd` clean
命令运行
sudo insmod test1.ko
sudo rmmod test1
第一个运行
的dmesg[10914.046211] Thread Creating...
[10914.046515] func t1
[10914.046530] Stopping t2
[10914.046531] func t2
[10914.046533] func t2 return
[10914.046538] t2 stopped
[10914.046555] func t3
[10938.895544] Cleaning Up...
[10938.895545] Stopping t1
[10938.895552] func t1 return
[10938.895561] t1 stopped
[10938.895562] Stopping t3
[10938.895566] func t3 return
[10938.895587] t3 stopped
t2在本次停止前已经执行
第二个的dmesg运行
[10940.775771] Thread Creating...
[10940.776109] func t1
[10940.776126] Stopping t2
[10940.776138] t2 stopped
[10940.776162] func t3
[10956.375606] Cleaning Up...
[10956.375607] Stopping t1
[10956.375613] func t1 return
[10956.375674] t1 stopped
[10956.375674] Stopping t3
[10956.375678] func t3 return
[10956.375697] t3 stopped
t2 没有 运行ning 就停止了。但是 t1 和 t3 在停止之前没有立即停止 运行。
(此答案对应 Linux 内核版本 5.4。)
新创建的内核线程任务执行“kernel/kthread.c”中的函数kthread
。如果一切顺利,kthread
将调用 kthread_run
(或 kthread_create
的)threadfn
参数引用的线程函数。然而,在调用threadfn
函数指针之前的最后测试是检查内核线程的KTHREAD_SHOULD_STOP
位。如果设置了 KTHREAD_SHOULD_STOP
位,则不会调用 threadfn
函数指针,新的内核线程任务将调用 do_exit
,退出代码为 -EINTR
。 kthread
函数末尾的相关代码如下:
ret = -EINTR;
if (!test_bit(KTHREAD_SHOULD_STOP, &self->flags)) {
cgroup_kthread_ready();
__kthread_parkme(self);
ret = threadfn(data);
}
do_exit(ret);
虽然kthread_run
在returning之前唤醒了新创建的内核线程任务,但是可以调用kthread_stop
函数并设置内核线程的KTHREAD_SHOULD_STOP
位在内核线程到达其 KTHREAD_SHOULD_STOP
位的最终检查之前调用 threadfn
函数指针。在这种情况下,内核线程将在 threadfn
函数指针未被调用的情况下退出。
OP的原始代码可以更改为打印kthread_stop
的return值,如下所示:
int exit_code;
/* ... */
printk("Stopping t2\n");
exit_code = kthread_stop(t2);
printk("t2 stopped, exit code %d\n", exit_code);
然后它应该显示线程 t2
以代码 -EINTR
(可能是 -4
)退出,如果它在入口函数 func
被调用之前被停止。