gpt4 book ai didi

linux-kernel - 使用 msleep 唤醒处于 sleep 状态的内核线程

转载 作者:行者123 更新时间:2023-12-02 01:27:22 25 4
gpt4 key购买 nike

我已经使用“kthread_run”创建并启动了一个内核线程。
task1 = kthread_run(flash, NULL, "LED_thread");
我基本上必须以 4 种不同的模式以不同的开关时间闪烁 LED。我为此使用“msleep”。例如,如果在一个模式中,关闭时间为 10 秒,我关闭 LED,然后使用“msleep(10000)”。现在的问题是,如果模式是从用户空间更改的,那么模式只有在 10 秒的延迟完成后才会更改。

为了解决这个问题,我启动了第二个线程来监视 LED 的模式,一旦它看到模式的变化,它就会尝试使用“wake_up_process(task1)”唤醒第一个线程

但这不会唤醒第一个线程。

所以我的问题是:
1.wake_up_process 唤醒处于 sleep 状态的线程(远离运行队列)?
2. 如果没有,是否还有其他方法可以实现。

提前致谢
斯里克

在我的“flash”线程函数中,我有

更新:

@克雷格 ,这是您在单个线程中实现整个事情的一个很好的例子。在我看来,线程占用了大量的 cpu 周期,因此最好使用尽可能少的线程。你同意吗?
除了避免其他线程的示例之外,替代方法是:
1. 在驱动程序中使用 ioctl 并使用此 ioctl 而不是我正在使用的 sysfs 属性从用户空间设置模式,并在收到 ioctl 命令时将信号发送到工作线程以唤醒它。
2.在mode sysfs属性的store函数中发送信号唤醒工作线程(驱动中的函数,当在用户空间设置mode时会调用该函数)

最佳答案

一种方法是使用 msleep_interruptible而不是 msleep在线程 A 中。当然不知道,但您可能必须让线程 B 通过发送信号而不是 wake_up_process 来唤醒.试试看。

您可能需要一些互锁(例如自旋锁)来防止线程 B 在线程 A 已经唤醒 [并且已经看到闪烁类型更改] 之后发送信号的竞争条件。

或者,线程 A 可以记住旧类型,如果没有变化,则继续剩余的 sleep (这可以补偿无需锁定的竞争)

这是每个的内核代码:

/**
* msleep - sleep safely even with waitqueue interruptions
* @msecs: Time in milliseconds to sleep for
*/
void msleep(unsigned int msecs)
{
unsigned long timeout = msecs_to_jiffies(msecs) + 1;

while (timeout)
timeout = schedule_timeout_uninterruptible(timeout);
}

EXPORT_SYMBOL(msleep);

/**
* msleep_interruptible - sleep waiting for signals
* @msecs: Time in milliseconds to sleep for
*/
unsigned long msleep_interruptible(unsigned int msecs)
{
unsigned long timeout = msecs_to_jiffies(msecs) + 1;

while (timeout && !signal_pending(current))
timeout = schedule_timeout_interruptible(timeout);
return jiffies_to_msecs(timeout);
}

EXPORT_SYMBOL(msleep_interruptible);

更新:

Thanks for pointing to the possible race condition as well. I will try with semaphores/mutexes and see



如果线程 B 进行监视(例如读取 /dev/whatever/proc/whatever )并写入全局[可能在“私有(private)”数据结构内],则可能没有必要。您可能必须使用原子提取/存储或 CAS,但将其标记为 volatile可能就足够了。这是因为 B 是 [唯一] 作家,而 A 是 [唯一] 读者。

As far as sending signals in kernel space, can we do that? i thought signals are only for userspace. Could you give me an example if that is not the case.



由于 msleep_interruptible完全存在,它会寻找待处理的信号,这就是 QED。

但是,这里有一些代码可以证明这一点。 allow_signal中的评论自 2003 年的补丁以来一直存在:
/*
* Let kernel threads use this to say that they allow a certain signal.
* Must not be used if kthread was cloned with CLONE_SIGHAND.
*/
int allow_signal(int sig)
{
if (!valid_signal(sig) || sig < 1)
return -EINVAL;

spin_lock_irq(&current->sighand->siglock);
/* This is only needed for daemonize()'ed kthreads */
sigdelset(&current->blocked, sig);
/*
* Kernel threads handle their own signals. Let the signal code
* know it'll be handled, so that they don't get converted to
* SIGKILL or just silently dropped.
*/
current->sighand->action[(sig)-1].sa.sa_handler = (void __user *)2;
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
return 0;
}

EXPORT_SYMBOL(allow_signal);

int disallow_signal(int sig)
{
if (!valid_signal(sig) || sig < 1)
return -EINVAL;

spin_lock_irq(&current->sighand->siglock);
current->sighand->action[(sig)-1].sa.sa_handler = SIG_IGN;
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
return 0;
}

EXPORT_SYMBOL(disallow_signal);

只需使用内部调用。那可能是 do_send_sig_info .使用一些无害的东西,比如 SIGUSR1 (或者您可能需要使用“RT”信号,因为它们已排队)。

在内核中发送信号意味着该信号与任务的待处理信号掩码进行“或”运算,并且该任务被标记为可运行(例如,被唤醒)。

用户空间“跳转到处理程序”仅在给定任务即将重新进入[重新进入之前的最后一件事]用户空间时发生。如果设置了信号,内核将设置用户空间堆栈帧并将执行切换到处理程序。

在内核线程中,它不会导致跳转到“信号处理程序”,因为没有等效项。内核线程必须查看其挂起的信号掩码才能完全注意到它。没有跳跃。它不像中断。而且,内核线程必须手动清除挂起的掩码 [否则, msleep_interruptible此后将始终立即返回]。

为了检测和清除信号,这里有一些代码:
while (signal_pending(current)) {
siginfo_t info;
unsigned long signo;

signo = dequeue_signal_lock(current, &current->blocked, &info));

switch (signo) {
case SIGUSR1:
break;
}
}

更新#2:

The msleep_interruptible was not wakeable using wake_up_process, but only with send_sig_info. I think msleep_interruptible is nothing but : setstate(TASK_INTERRUPTIBLE) and schedule() with delay, so i had expected wake_up_process to wakeup the thread sleeping with msleep_interruptible.



[AFAIK] 你必须发送一个信号来提前终止 sleep ,因此我对使用信号的原始评论(和使用 do_send_sig_info )。有时这只是反复试验。我可能尝试过 wake_up_process , 也。但是,当这不起作用时,我会开始环顾四周 [通过查看 msleep*代码]。

But neverthless it would be interesting to understand if it is possible to implement that in a single thread.



是的。它需要一点重组和一两个额外的变量。

My blink thread should set LED on for a second



我们称之为 blink_interval_on

and depending on the mode it is in, has to set LED off after the 1 second for about 10 seconds or 1 second.



我们称之为 blink_interval_off

If we implement it in a single thread, polling will be delayed by those 10 seconds right?



[概念上] 的关键变化是赋予 msleep 的值。不必是整个间隔(例如 blink_interval_* ),但可以是较小的固定间隔。我们称之为 sleep_fixed .

我们希望选择它来提供您想要的响应能力。如果它非常小(例如一微秒),我们就会经常醒来。如果我们选择一个较大的值,如 1 秒或 10 秒,则响应会变得“迟缓”。

因此,一个好的值是 10-100 毫秒。足够慢,我们不会因为频繁的唤醒而占用资源,但足够快,如果闪烁模式发生变化,用户不会注意到差异。

现在,我们必须记录给定眨眼间隔的剩余时间 [bumping down by sleep_fixed] ],并在间隔用完时翻转 LED 状态。也就是说,我们手动跟踪完整的 msleep曾经为我们做的。

Can you give me an example for that.



好的,这是 [希望] 解释我的意思的粗略/伪代码版本:
// NOTE: all times are in milliseconds

int blink_interval_on; // LED "on" interval
int blink_interval_off; // LED "off" interval

int blink_interval_remaining; // time remaining in current interval
int curmode; // current blink mode

int blink_on_list[2] = { 1000, 1000 };
int blink_off_list[2] = { 1000, 10000 };

int sleep_fixed; // small sleep value

// set_led_on -- set LED on or off
void
set_led_on(int onflg)
{
}

// msleep -- sleep for specified number of milliseconds
void
msleep(int ms)
{
}

// getnewmode -- get desired blink mode
// RETURNS: 0=1 second, 1=10 seconds, etc (-1=stop)
int
getnewmode(void)
{
int newmode;

// do whatever is necessary, as you're doing now ...
newmode = 0;

return newmode;
}

// ledloop -- main thread for single thread case
void
ledloop(void)
{
int newmode;
int sleep_fixed;
int curstate;
int sleep_current;

// force an initial state change
curmode = -2;

// this is our "good enough" interval
sleep_fixed = 10;

while (1) {
// look for blink mode changes
newmode = getnewmode();
if (newmode < 0)
break;

// got a mode change
// force a change at the bottom
if (curmode != newmode) {
curstate = 0;
curmode = newmode;
blink_interval_remaining = 0;
}

// set up current sleep interval
sleep_current = sleep_fixed;
if (sleep_current > blink_interval_remaining)
sleep_current = blink_interval_remaining;

// do a sleep
// NOTE: because the sleep is so short, we can use the simple msleep
// this is the "good enough" way ...
if (sleep_current > 0) {
msleep(sleep_current);
blink_interval_remaining -= sleep_current;
}

// flip the LED state at interval end
if (blink_interval_remaining <= 0) {
curstate = ! curstate;
set_led_on(curstate);

// set new interval
if (curstate)
blink_interval_remaining = blink_on_list[curmode];
else
blink_interval_remaining = blink_off_list[curmode];
}
}
}

关于linux-kernel - 使用 msleep 唤醒处于 sleep 状态的内核线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36344295/

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