Linux wait返回及timer(5)

三、定时器超时唤醒线程的流程
定时器超时唤醒线程与发送信号唤醒线程流程不同,下面代码分析定时器唤醒时走的流程,调用路径如绿色标示(有关linux定时器的知识,可以搜索“linux定时器的实现”,可以找到很多介绍)。
kernel\kernel\Posix-timers.c

int posix_timer_event(struct k_itimer *timr, int si_private)  // posix定时器超时后调用到这里
{
    struct task_struct *task;
    int shared, ret = -1;
    /*
    * FIXME: if ->sigq is queued we can race with
    * dequeue_signal()->do_schedule_next_timer().
    *
    * If dequeue_signal() sees the "right" value of
    * si_sys_private it calls do_schedule_next_timer().
    * We re-queue ->sigq and drop ->it_lock().
    * do_schedule_next_timer() locks the timer
    * and re-schedules it while ->sigq is pending.
    * Not really bad, but not that we want.
    */
    timr->sigq->info.si_sys_private = si_private;

rcu_read_lock();
    task = pid_task(timr->it_pid, PIDTYPE_PID);
    if (task) {
        shared = !(timr->it_sigev_notify & SIGEV_THREAD_ID);
        ret = send_sigqueue(timr->sigq, task, shared);
    }
    rcu_read_unlock();
    /* If we failed to send the signal the timer stops. */
    return ret > 0;
}

kernel\kernel\Signal.c

int send_sigqueue(struct sigqueue *q, struct task_struct *t, int group)
{
    int sig = q->info.si_signo;
    int sival = q->info.si_value.sival_int;
    struct sigpending *pending;
    unsigned long flags;
    int ret, result;

BUG_ON(!(q->flags & SIGQUEUE_PREALLOC));

ret = -1;
    if (!likely(lock_task_sighand(t, &flags)))
        goto ret;

ret = 1; /* the signal is ignored */
    result = TRACE_SIGNAL_IGNORED;
    if (!prepare_signal(sig, t, false))
        goto out;

ret = 0;
    if (unlikely(!list_empty(&q->list))) {
        /*
        * If an SI_TIMER entry is already queue just increment
        * the overrun count.
        */
        BUG_ON(q->info.si_code != SI_TIMER);
        q->info.si_overrun++;
        result = TRACE_SIGNAL_ALREADY_PENDING;
        goto out;
    }
    q->info.si_overrun = 0;

signalfd_notify(t, sig);
    pending = group ? &t->signal->shared_pending : &t->pending;
    list_add_tail(&q->list, &pending->list);
    sigaddset(&pending->signal, sig);
    complete_signal(sig, t, group);
    result = TRACE_SIGNAL_DELIVERED;
out:
    trace_signal_generate(sig, &q->info, t, group, result);
    unlock_task_sighand(t, &flags);
ret:
    return ret;
}

kernel\kernel\Signal.c

static void complete_signal(int sig, struct task_struct *p, int group)
{
    struct signal_struct *signal = p->signal;
    struct task_struct *t;

/*
    * Now find a thread we can wake up to take the signal off the queue.
    *
    * If the main thread wants the signal, it gets first crack.
    * Probably the least surprising to the average bear.
    */
    if (wants_signal(sig, p))
        t = p;
    else if (!group || thread_group_empty(p))
        /*
        * There is just one thread and it does not need to be woken.
        * It will dequeue unblocked signals before it runs again.
        */
        return;
    else {
        /*
        * Otherwise try to find a suitable thread.
        */
        t = signal->curr_target;
        while (!wants_signal(sig, t)) {
            t = next_thread(t);
            if (t == signal->curr_target)
                /*
                * No thread needs to be woken.
                * Any eligible threads will see
                * the signal in the queue soon.
                */
                return;
        }
        signal->curr_target = t;
    }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/baa9f29ea8eb34fc042dbed89dcbdd91.html