kthread_stop问题探讨

通用做法的问题

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <linux/uaccess.h>
#include <linux/kthread.h>
#include <linux/unistd.h>
#include <linux/delay.h>
#include <linux/printk.h>
#include <linux/types.h>
#include <linux/syscalls.h>

static struct task_struct *demo_struct;

// 第一种:卡死
int demo_thread1(void *data) {
	pr_info("%s ===>\n", __func__);
	while(1) {
		pr_info("%s, I am alive\n", __func__);
	}
	pr_info("%s <===\n", __func__);
	return 0;
}

// 第二种:卡死
int demo_thread2(void *data) {
	pr_info("%s ===>\n", __func__);
	msleep(1000*1000*1000);
	pr_info("%s <===\n", __func__);
	return 0;
}

static int demo_init(void) {
	int result = 0;

	pr_info("%s ===>\n", __func__);

	demo_struct = kthread_run(demo_thread1, NULL, "demo_thread1");
	// demo_struct = kthread_run(demo_thread2, NULL, "demo_thread2");
	if (IS_ERR_OR_NULL(demo_struct)) {
		demo_struct = NULL;
		pr_err("%s, thread create failed\n", __func__);
		result = -EINVAL;
	} else {
		pr_info("%s, thread create success\n", __func__);
	}

	pr_info("%s <===\n", __func__);
	return result; 
}

static void demo_exit(void) {
	int ret;
	pr_info("%s ===>\n", __func__);
	if (!IS_ERR_OR_NULL(demo_struct)) {
		ret = kthread_stop(demo_struct);
		pr_info("%s, exit code %d\n", __func__, ret);
	} else {
		pr_err("%s, demo struct invalid\n", __func__);
	}
	demo_struct = NULL;
	pr_info("%s <===\n", __func__);
	return;
}

module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");

以上写法是错误写法,但是代码中基本都是这种格调

//=========================================================

针对第一种问题的解法

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <linux/uaccess.h>
#include <linux/kthread.h>
#include <linux/unistd.h>
#include <linux/delay.h>
#include <linux/printk.h>
#include <linux/types.h>
#include <linux/syscalls.h>

static struct task_struct *demo_struct;

// 第一种:OK
int demo_thread1(void *data) {
	pr_info("%s ===>\n", __func__);
	while(!kthread_should_stop()) {
		pr_info("%s, I am alive\n", __func__);
	}
	pr_info("%s <===\n", __func__);
	return 23;
}

static int demo_init(void) {
	int result = 0;

	pr_info("%s ===>\n", __func__);

	demo_struct = kthread_run(demo_thread1, NULL, "demo_thread1");
	if (IS_ERR_OR_NULL(demo_struct)) {
		demo_struct = NULL;
		pr_err("%s, thread create failed\n", __func__);
		result = -EINVAL;
	} else {
		pr_info("%s, thread create success\n", __func__);
	}

	pr_info("%s <===\n", __func__);
	return result; 
}

static void demo_exit(void) {
	int ret;
	pr_info("%s ===>\n", __func__);
	if (!IS_ERR_OR_NULL(demo_struct)) {
		ret = kthread_stop(demo_struct);
		pr_info("%s, exit code %d\n", __func__, ret);
	} else {
		pr_err("%s, demo struct invalid\n", __func__);
	}
	demo_struct = NULL;
	pr_info("%s <===\n", __func__);
	return;
}

module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");

针对第一种情况,不错,正确

[  733.690793] demo_thread1, I am alive
[  733.690795] demo_exit ===>
[  733.690796] demo_thread1, I am alive
[  733.690797] demo_thread1, I am alive
[  733.690797] demo_thread1, I am alive
[  733.690798] demo_thread1, I am alive
[  733.690799] demo_thread1, I am alive
[  733.690799] demo_thread1 <===
[  733.690906] demo_exit, exit code 23
[  733.690908] demo_exit <===

针对第二种情况

带while的场景 + 短睡眠可以,醒来后还能自我检查,就像第一种情况
长睡眠不行

int demo_thread2(void *data) {
	pr_info("%s ===>\n", __func__);
	msleep(1000*1000*1000);
	pr_info("%s <===\n", __func__);
	return 2;
}

一种解法就是:将长睡眠拆成短睡眠

针对长睡眠的情况

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <linux/uaccess.h>
#include <linux/kthread.h>
#include <linux/unistd.h>
#include <linux/delay.h>
#include <linux/printk.h>
#include <linux/types.h>
#include <linux/syscalls.h>
#include <linux/sched.h>
#include <linux/signal.h>

static struct task_struct *demo_struct;

int demo_thread2(void *data) {
	pr_info("%s ===>\n", __func__);
	while(1) {
		pr_info("%s, before schedule\n", __func__);

		// __set_current_state(TASK_INTERRUPTIBLE);
		// 可被中断的设置已经放到 schedule_timeout_interruptible 中了
		// MAX_SCHEDULE_TIMEOUT is MAX_LONG

		// 方式1
		schedule_timeout_interruptible(MAX_SCHEDULE_TIMEOUT);

		// 方式2
		// schedule_timeout_uninterruptible(MAX_SCHEDULE_TIMEOUT);

		pr_info("%s, after schedule\n", __func__);
		break;
	}

	pr_info("%s <===\n", __func__);
	return 2;
}

static int demo_init(void) {
	int result = 0;

	pr_info("%s ===>\n", __func__);

	demo_struct = kthread_run(demo_thread2, NULL, "demo_thread2");
	if (IS_ERR_OR_NULL(demo_struct)) {
		demo_struct = NULL;
		pr_err("%s, thread create failed\n", __func__);
		result = -EINVAL;
	} else {
		pr_info("%s, thread create success\n", __func__);
	}

	pr_info("%s <===\n", __func__);
	return result; 
}

static void demo_exit(void) {
	int ret;
	pr_info("%s ===>\n", __func__);
	if (!IS_ERR_OR_NULL(demo_struct)) {
		ret = kthread_stop(demo_struct);
		pr_info("%s, exit code %d\n", __func__, ret);
	} else {
		pr_err("%s, demo struct invalid\n", __func__);
	}
	demo_struct = NULL;
	pr_info("%s <===\n", __func__);
	return;
}

module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");

结果正常

[ 3020.000912] demo_init ===>
[ 3020.008014] demo_init, thread create success
[ 3020.008018] demo_init <===
[ 3020.009147] demo_thread2 ===>
[ 3020.009152] demo_thread2, before schedule

[ 3049.178367] demo_exit ===>
[ 3049.180738] demo_thread2, after schedule
[ 3049.180743] demo_thread2 <===
[ 3049.181166] demo_exit, exit code 2
[ 3049.181170] demo_exit <===

从目前我的分析来看,网上其他博主的分析都是有问题的
kthread_stop 中只有 wake_up_process 才能将睡眠的 demo_struct 重新放入到运行队列
然后等待 demo_thread2 运行结束,之后将 k->exit_code 返回给调用者
并不是大家分析的发信号,既然要发信号,那 demo_thread2 肯定要设置承接的信号句柄
可是我们并没有看到哪里有设它。
如果是用信号,那我们还用 kthread_stop 干嘛,直接信号执行进程间通信不香吗

不管我用 schedule_timeout_interruptible 还是 schedule_timeout_uninterruptible
最后的结果一样,是 kthread_stop 中的 wake_up_process 叫醒了 demo_struct
而不是信号

schedule_timeout_interruptible 和 schedule_timeout_uninterruptible 作用于信号
跟 wake_up_process 没半毛钱关系

不信你看 schedule_timeout 的注释

/**
 * schedule_timeout - sleep until timeout
 * @timeout: timeout value in jiffies
 *
 * Make the current task sleep until @timeout jiffies have elapsed.
 * The function behavior depends on the current task state
 * (see also set_current_state() description):
 *
 * %TASK_RUNNING - the scheduler is called, but the task does not sleep
 * at all. That happens because sched_submit_work() does nothing for
 * tasks in %TASK_RUNNING state.
 *
 * %TASK_UNINTERRUPTIBLE - at least @timeout jiffies are guaranteed to
 * pass before the routine returns unless the current task is explicitly
 * woken up, (e.g. by wake_up_process()).
 * 该 routine 返回时,至少能保证 timeout 的 pass,除非 current task 被显式唤醒
 * 比如被:wake_up_process() 唤醒
 *
 * %TASK_INTERRUPTIBLE - the routine may return early if a signal is
 * delivered to the current task or the current task is explicitly woken
 * up.
 *
 * 当该 current 收到信号或者被显式唤醒时,该 routine 可能会提前返回 
 *
 * The current task state is guaranteed to be %TASK_RUNNING when this
 * routine returns.
 *
 * Specifying a @timeout value of %MAX_SCHEDULE_TIMEOUT will schedule
 * the CPU away without a bound on the timeout. In this case the return
 * value will be %MAX_SCHEDULE_TIMEOUT.
 *
 * Returns 0 when the timer has expired otherwise the remaining time in
 * jiffies will be returned. In all cases the return value is guaranteed
 * to be non-negative.
 */
signed long __sched schedule_timeout(signed long timeout)
{
	struct process_timer timer;
	unsigned long expire;

	switch (timeout)
	{
	case MAX_SCHEDULE_TIMEOUT:
		/*
		 * These two special cases are useful to be comfortable
		 * in the caller. Nothing more. We could take
		 * MAX_SCHEDULE_TIMEOUT from one of the negative value
		 * but I' d like to return a valid offset (>=0) to allow
		 * the caller to do everything it want with the retval.
		 */
		schedule();
		goto out;
	default:
		/*
		 * Another bit of PARANOID. Note that the retval will be
		 * 0 since no piece of kernel is supposed to do a check
		 * for a negative retval of schedule_timeout() (since it
		 * should never happens anyway). You just have the printk()
		 * that will tell you if something is gone wrong and where.
		 */
		if (timeout < 0) {
			printk(KERN_ERR "schedule_timeout: wrong timeout "
				"value %lx\n", timeout);
			dump_stack();
			__set_current_state(TASK_RUNNING);
			goto out;
		}
	}

	expire = timeout + jiffies;

	timer.task = current;
	timer_setup_on_stack(&timer.timer, process_timeout, 0);
	__mod_timer(&timer.timer, expire, MOD_TIMER_NOTPENDING);
	schedule();
	del_singleshot_timer_sync(&timer.timer);

	/* Remove the timer from the object tracker */
	destroy_timer_on_stack(&timer.timer);

	timeout = expire - jiffies;

 out:
	return timeout < 0 ? 0 : timeout;
}
EXPORT_SYMBOL(schedule_timeout);

小发现:
如果给上述接口传递一个 MAX_SCHEDULE_TIMEOUT
则不就等价于:schedule()

//=========================================================

其他博客中的同步问题:

参考博客: https://www.it1352.com/1527141.html
如果kthread可能会退出而没有检查kthread_should_stop(),则应采取其他行动保证,
kthread_stop()不会看到kthread被销毁.

struct task_struct* kernel_test_task;

int module_init(void)
{
    // Create kthread, but don't start it.
    kernel_test_task = kthread_create(...);
    // Increments usage counter.
    get_task_struct(kernel_test_task);
    // Now it is safe to start kthread - exiting from it doesn't destroy its struct.
    wake_up_process(kernel_test_task);
}

void module_cleanup(void)
{
    // While thread may be finished now, its structure is garanteed to be alive.
    kthread_stop(kernel_test_task);
    // This will decrement usage counter, incremented in module_init.
    put_task_struct(kernel_test_task);
    // Now thread is garanteed to be finished, and its struct destroyed.
}

//=========================================================

参考代码

/**
 *
 * 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.
 */
// threadfn() 中可能会自灭(自己代码中调用 do_exit() )
// kthread_stop 的调用者必须确保 task_struct 不能 go away
// 应该是调 kthread_stop 时,threadfn中不能刚好执行 do_exit() 的代码

// 返回值是 threadfn 的结果,或者是错误值 (如果永远也不调用 wake_up_process 时)
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应该停止
	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;
}
EXPORT_SYMBOL(kthread_stop);

/**
 * wake_up_process - Wake up a specific process
 * @p: The process to be woken up.
 *
 * Attempt to wake up the nominated process and move it to the set of runnable
 * processes.
 *
 * Return: 1 if the process was woken up, 0 if it was already running.
 *
 * This function executes a full memory barrier before accessing the task state.
 */
int wake_up_process(struct task_struct *p)
{
	return try_to_wake_up(p, TASK_NORMAL, 0);
}
EXPORT_SYMBOL(wake_up_process);

#define TASK_NORMAL			(TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)



/**
 * kthread_should_stop - should this kthread return now?
 *
 * When someone calls kthread_stop() on your kthread, it will be woken
 * and this will return true.  You should then return, and your return
 * value will be passed through to kthread_stop().
 */
bool kthread_should_stop(void)
{
	return test_bit(KTHREAD_SHOULD_STOP, &to_kthread(current)->flags);
}
EXPORT_SYMBOL(kthread_should_stop);

Supongo que te gusta

Origin blog.csdn.net/wangkai6666/article/details/121724586
Recomendado
Clasificación