使用msleep唤醒处于休眠状态的内核线程

时间:2016-03-31 21:37:15

标签: linux-kernel kernel-module

我使用" 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属性的存储功能中的工作线程(在用户空间中设置模式时将调用的驱动程序中的函数)

1 个答案:

答案 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(&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这样无害的东西(或者您可能需要使用&#34; RT&#34;信号,因为它们已排队)。

在内核中发送信号意味着信号被“或”到任务的未决信号掩码中,并且任务被标记为可运行(例如被唤醒)。

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

在内核线程中,导致跳转到&#34;信号处理程序&#34;因为没有相应的东西。内核线程必须查看其挂起的信号掩码才能注意到它。有没有跳转。 就像中断一样。并且,内核线程必须手动清除挂起的掩码[否则,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:

  

使用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];
        }
    }
}