我使用" kthread_run"创建并启动了一个内核线程。 task1 = kthread_run(flash,NULL," LED_thread"); 我必须基本上使用不同的关闭时间以4种不同的模式闪烁LED。我正在使用" msleep"为了这。例如,如果在模式中,关闭时间是10秒,我关闭LED然后使用" msleep(10000)"。现在问题是如果模式从用户空间改变,模式只有在10秒的延迟完成后才会改变。
为了解决我已经开始监视LED模式的第二个线程,一旦看到模式发生变化,就会尝试使用" wake_up_process(task1)&#唤醒第一个线程34;
但这并没有唤醒第一个主题。
所以我的问题是: 1. wake_up_process唤醒一个处于睡眠状态的线程(远离运行队列)? 2.如果没有,还有其他任何完成此事。
提前致谢 Srik
在我的" flash"线程功能,我有
更新
@Craig ,这是你在一个线程中实现整个事情的一个很好的例子。在我看来,线程占用了很多cpu周期,因此最好尽量减少线程。你同意吗?
除了避免其他线程的例子之外的其他选择是:
1.在驱动程序中使用ioctl并使用此ioctl而不是我正在使用的sysfs属性从用户空间设置模式,并在收到ioctl命令时将信号发送到工作线程以将其唤醒。
2.发送信号以唤醒模式sysfs属性的存储功能中的工作线程(在用户空间中设置模式时将调用的驱动程序中的函数)
答案 0 :(得分:2)
一种方法是在线程A. Dunno中使用msleep_interruptible
而不是msleep
,但是你可能必须让线程B通过发送信号而不是wake_up_process
来进行唤醒。试试两个并看看。
您可能需要一些互锁(例如自旋锁)来防止线程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);
<强>更新强>
感谢您指出可能的竞争条件。我将尝试使用信号量/互斥量并查看
如果线程B进行监控(例如,阅读/dev/whatever
或/proc/whatever
)并写入全局[可能在&#34;私有&#34;之内],则可能没有必要。数据结构]。您可能必须使用原子获取/存储或CAS,但将其标记为volatile
可能就足够了。这是因为B是[唯一]作者,A是[唯一]读者。
就在内核空间发送信号而言,我们可以这样做吗?我认为信号仅适用于用户空间。如果不是这样的话,你能给我一个例子。
由于 all 中存在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(¤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
这样无害的东西(或者您可能需要使用&#34; RT&#34;信号,因为它们已排队)。
在内核中发送信号意味着信号被“或”到任务的未决信号掩码中,并且任务被标记为可运行(例如被唤醒)。
用户空间&#34;跳转到处理程序&#34;当给定任务即将重新输入[重新输入之前的最后一件事]用户空间时,仅发生 。如果设置了信号,则内核会设置用户空间堆栈帧并将执行切换到处理程序。
在内核线程中,不导致跳转到&#34;信号处理程序&#34;因为没有相应的东西。内核线程必须查看其挂起的信号掩码才能注意到它。有没有跳转。 不就像中断一样。并且,内核线程必须手动清除挂起的掩码[否则,msleep_interruptible
此后总是立即返回。“
要检测并清除信号,请参阅以下代码:
while (signal_pending(current)) {
siginfo_t info;
unsigned long signo;
signo = dequeue_signal_lock(current, ¤t->blocked, &info));
switch (signo) {
case SIGUSR1:
break;
}
}
更新#2:
使用
wake_up_process
无法唤醒msleep_interruptible,但只能使用send_sig_info
。我认为msleep_interruptible
只是:setstate(TASK_INTERRUPTIBLE)
和schedule()
有延迟,所以我原本希望wake_up_process
唤醒与msleep_interruptible
一起睡觉的线程。
[AFAIK]你必须发出一个信号以提前终止睡眠,因此我最初评论使用信号(并使用do_send_sig_info
)。有时它只是反复试验。我也许曾尝试wake_up_process
。但是,当这不起作用时,我会[通过查看msleep*
代码]开始四处寻找。
但是,无论如何理解是否有可能在单个线程中实现它将会很有趣。
是。它需要一点点的重组和一两个额外的变量。
我的闪烁线应该将LED设置为开启
我们称之为blink_interval_on
并且根据其所处的模式,必须在1秒后将LED设置为关闭约10秒或1秒。
我们称之为blink_interval_off
如果我们在一个线程中实现它,那么轮询会延迟10秒钟吗?
[概念上]关键变化是msleep
给出的值不必是整个区间(例如blink_interval_*
),但可以是更小的< em>固定间隔。我们称之为sleep_fixed
。
我们希望选择此项来提供您喜欢的响应能力。如果它非常小(例如一微秒),我们就会经常醒来。如果我们选择大值(如1秒或10秒),则响应会变得缓慢&#34;。
因此,一个好的值将是10-100毫秒。足够慢,以至于我们不会通过频繁的唤醒来占用资源,但是如果眨眼模式改变,用户就不会注意到差异。
现在,我们必须跟踪给定闪烁间隔剩余的时间[按sleep_fixed
降低],并在间隔耗尽时翻转LED状态。也就是说,我们手动跟踪我们过去常用的msleep
内容。
你能举个例子。
好的,这是一个原始/伪代码版本,希望能解释我的意思:
// 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];
}
}
}