通用做法的问题
#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);