使用 msleep 唤醒处于睡眠状态的内核线程
wakeup a kernel thread that is in sleep using msleep
我已经使用 "kthread_run" 创建并启动了一个内核线程。
task1 = kthread_run(flash, NULL, "LED_thread");
我基本上必须以 4 种不同的模式闪烁 LED,并具有不同的开关时间。我为此使用 "msleep"。例如,如果在一个模式中,关闭时间为 10 秒,我会关闭 LED,然后使用 "msleep(10000)"。现在的问题是,如果模式从用户 space 更改,模式仅在 10 秒延迟完成后才会更改。
为了解决这个问题,我启动了第二个线程来监视 LED 的模式,一旦发现模式发生变化,它就会尝试使用 "wake_up_process(task1)"[=10 唤醒第一个线程=]
但这不会唤醒第一个线程。
所以我的问题是:
1. wake_up_process 唤醒睡眠中的线程(从 运行 队列中调度)?
2.如果不行,有没有其他的实现方式
提前致谢
斯里克
在我的 "flash" 线程函数中,我有
更新:
@Craig,这是您在单个线程中实现整个事情的一个很好的例子。在我看来,线程占据了很多 cpu 周期,所以线程越少越好。你同意吗?
除了你避免其他线程的例子之外的替代方法是:
1. 在驱动程序中使用 ioctl 并使用此 ioctl 而不是我正在使用的 sysfs 属性从用户 space 设置模式,并在收到 ioctl 命令时将信号发送到工作线程以将其唤醒.
2. 发送信号唤醒worker线程在mode sysfs属性的store函数中(驱动中的函数,当mode在user中设置时会被调用space)
一种方法是在线程 A 中使用 msleep_interruptible
而不是 msleep
。当然不知道,但您可能必须让线程 B 通过发送信号而不是 [=16] 来唤醒=].试试看。
您可能需要一些互锁(例如自旋锁)来防止线程 B 在线程 A 已经唤醒[并且已经看到闪烁类型更改]后发送信号的竞争条件。
或者,线程 A 可以记住旧的类型,如果没有变化就继续剩余的睡眠(这可以补偿不需要锁定的竞争)
这是每个的内核代码:
/**
* 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" 数据结构中],则可能没有必要。您可能必须使用 atomic fetch/store 或 CAS,但将其标记为 volatile
可能就足够了。这是因为 B 是 [唯一的] 作者,而 A 是 [唯一的] reader.
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
存在于 all,并且它寻找挂起的信号,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(¤t->sighand->siglock);
/* This is only needed for daemonize()'ed kthreads */
sigdelset(¤t->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(¤t->sighand->siglock);
return 0;
}
EXPORT_SYMBOL(allow_signal);
int disallow_signal(int sig)
{
if (!valid_signal(sig) || sig < 1)
return -EINVAL;
spin_lock_irq(¤t->sighand->siglock);
current->sighand->action[(sig)-1].sa.sa_handler = SIG_IGN;
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
return 0;
}
EXPORT_SYMBOL(disallow_signal);
就用内线吧。那可能是 do_send_sig_info
。使用像 SIGUSR1
这样无伤大雅的东西(或者你可能需要使用 "RT" 信号,因为它们在排队)。
在内核中发送一个信号意味着该信号被或运算到任务的挂起信号掩码中,并且该任务被标记为可运行(例如被唤醒)。
用户空间 "jump to handler" 仅在给定任务即将重新进入 [重新进入之前的最后一件事] 用户空间时 出现。如果设置了信号,内核将设置用户空间堆栈帧并将执行切换到处理程序。
在内核线程中,它 不会 导致跳转到 "signal handler",因为没有等效项。内核线程必须查看其挂起的信号掩码才能注意到它。有没有跳转。它不像那样是中断。并且,内核线程必须手动清除挂起的掩码[否则,msleep_interruptible
此后将总是 return立即
。
要检测并清除信号,请使用以下代码:
while (signal_pending(current)) {
siginfo_t info;
unsigned long signo;
signo = dequeue_signal_lock(current, ¤t->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] 你必须发送一个信号来提前终止睡眠,因此我最初的评论是关于使用信号(并使用 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_*
),但可以是更小的 fixed 区间。我们称之为 sleep_fixed
.
我们希望选择此选项以提供您想要的响应能力。如果它 非常 小(例如一微秒),我们就醒得太频繁了。如果我们选择 large 值,例如 1 秒或 10 秒,则响应变为 "sluggish".
因此,一个合适的值是 10-100 毫秒。速度足够慢,我们不会因频繁唤醒而占用资源,但速度足够快,如果闪烁模式发生变化,用户不会注意到差异。
现在,我们必须跟踪给定闪烁间隔的剩余时间 [=sleep_fixed
],并在间隔耗尽时翻转 LED 状态。也就是说,我们手动跟踪完整的 msleep
曾经为我们做什么。
Can you give me an example for that.
好的,这里有一个 crude/pseudo-code 版本来 [希望] 解释我的意思:
// 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];
}
}
}
我已经使用 "kthread_run" 创建并启动了一个内核线程。 task1 = kthread_run(flash, NULL, "LED_thread"); 我基本上必须以 4 种不同的模式闪烁 LED,并具有不同的开关时间。我为此使用 "msleep"。例如,如果在一个模式中,关闭时间为 10 秒,我会关闭 LED,然后使用 "msleep(10000)"。现在的问题是,如果模式从用户 space 更改,模式仅在 10 秒延迟完成后才会更改。
为了解决这个问题,我启动了第二个线程来监视 LED 的模式,一旦发现模式发生变化,它就会尝试使用 "wake_up_process(task1)"[=10 唤醒第一个线程=]
但这不会唤醒第一个线程。
所以我的问题是: 1. wake_up_process 唤醒睡眠中的线程(从 运行 队列中调度)? 2.如果不行,有没有其他的实现方式
提前致谢 斯里克
在我的 "flash" 线程函数中,我有
更新:
@Craig,这是您在单个线程中实现整个事情的一个很好的例子。在我看来,线程占据了很多 cpu 周期,所以线程越少越好。你同意吗?
除了你避免其他线程的例子之外的替代方法是:
1. 在驱动程序中使用 ioctl 并使用此 ioctl 而不是我正在使用的 sysfs 属性从用户 space 设置模式,并在收到 ioctl 命令时将信号发送到工作线程以将其唤醒.
2. 发送信号唤醒worker线程在mode sysfs属性的store函数中(驱动中的函数,当mode在user中设置时会被调用space)
一种方法是在线程 A 中使用 msleep_interruptible
而不是 msleep
。当然不知道,但您可能必须让线程 B 通过发送信号而不是 [=16] 来唤醒=].试试看。
您可能需要一些互锁(例如自旋锁)来防止线程 B 在线程 A 已经唤醒[并且已经看到闪烁类型更改]后发送信号的竞争条件。
或者,线程 A 可以记住旧的类型,如果没有变化就继续剩余的睡眠(这可以补偿不需要锁定的竞争)
这是每个的内核代码:
/**
* 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" 数据结构中],则可能没有必要。您可能必须使用 atomic fetch/store 或 CAS,但将其标记为 volatile
可能就足够了。这是因为 B 是 [唯一的] 作者,而 A 是 [唯一的] reader.
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
存在于 all,并且它寻找挂起的信号,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(¤t->sighand->siglock);
/* This is only needed for daemonize()'ed kthreads */
sigdelset(¤t->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(¤t->sighand->siglock);
return 0;
}
EXPORT_SYMBOL(allow_signal);
int disallow_signal(int sig)
{
if (!valid_signal(sig) || sig < 1)
return -EINVAL;
spin_lock_irq(¤t->sighand->siglock);
current->sighand->action[(sig)-1].sa.sa_handler = SIG_IGN;
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
return 0;
}
EXPORT_SYMBOL(disallow_signal);
就用内线吧。那可能是 do_send_sig_info
。使用像 SIGUSR1
这样无伤大雅的东西(或者你可能需要使用 "RT" 信号,因为它们在排队)。
在内核中发送一个信号意味着该信号被或运算到任务的挂起信号掩码中,并且该任务被标记为可运行(例如被唤醒)。
用户空间 "jump to handler" 仅在给定任务即将重新进入 [重新进入之前的最后一件事] 用户空间时 出现。如果设置了信号,内核将设置用户空间堆栈帧并将执行切换到处理程序。
在内核线程中,它 不会 导致跳转到 "signal handler",因为没有等效项。内核线程必须查看其挂起的信号掩码才能注意到它。有没有跳转。它不像那样是中断。并且,内核线程必须手动清除挂起的掩码[否则,msleep_interruptible
此后将总是 return立即
要检测并清除信号,请使用以下代码:
while (signal_pending(current)) {
siginfo_t info;
unsigned long signo;
signo = dequeue_signal_lock(current, ¤t->blocked, &info));
switch (signo) {
case SIGUSR1:
break;
}
}
更新#2:
The msleep_interruptible was not wakeable using
wake_up_process
, but only withsend_sig_info
. I thinkmsleep_interruptible
is nothing but :setstate(TASK_INTERRUPTIBLE)
andschedule()
with delay, so i had expectedwake_up_process
to wakeup the thread sleeping withmsleep_interruptible
.
[AFAIK] 你必须发送一个信号来提前终止睡眠,因此我最初的评论是关于使用信号(并使用 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_*
),但可以是更小的 fixed 区间。我们称之为 sleep_fixed
.
我们希望选择此选项以提供您想要的响应能力。如果它 非常 小(例如一微秒),我们就醒得太频繁了。如果我们选择 large 值,例如 1 秒或 10 秒,则响应变为 "sluggish".
因此,一个合适的值是 10-100 毫秒。速度足够慢,我们不会因频繁唤醒而占用资源,但速度足够快,如果闪烁模式发生变化,用户不会注意到差异。
现在,我们必须跟踪给定闪烁间隔的剩余时间 [=sleep_fixed
],并在间隔耗尽时翻转 LED 状态。也就是说,我们手动跟踪完整的 msleep
曾经为我们做什么。
Can you give me an example for that.
好的,这里有一个 crude/pseudo-code 版本来 [希望] 解释我的意思:
// 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];
}
}
}