analysis of wake_up_interruptible()

来源:互联网 发布:淘宝详情页文字排版 编辑:程序博客网 时间:2024/05/10 04:51
#define wake_up_interruptible(x)    __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)

void fastcall __wake_up(wait_queue_head_t     *q,
                        unsigned int         mode,
                        int                 nr_exclusive,
                        void                 *key)
{
    unsigned long flags;
    spin_lock_irqsave(&q->lock, flags);
    __wake_up_common(q, mode, nr_exclusive, 0, key);
    spin_unlock_irqrestore(&q->lock, flags);
}

static void __wake_up_common(   wait_queue_head_t *q,
                                unsigned int        mode,
                                int                 nr_exclusive,
                                int                 sync,
                                void                *key)
{
    struct list_head *tmp, *next;
    list_for_each_safe(tmp, next, &q->task_list) {
        wait_queue_t *curr;
        unsigned flags;
        curr = list_entry(tmp, wait_queue_t, task_list);
        flags = curr->flags;
        if (curr->func(curr, mode, sync, key) &&
            (flags & WQ_FLAG_EXCLUSIVE) &&
            !--nr_exclusive)
            break;
    }
}

#define list_for_each_safe(pos, n, head) /
    for (pos = (head)->next, n = pos->next; pos != (head);  pos = n, n = pos->next)

_
_wake_up_common() --> default_wake_function()


wait_queue_t.func = default_wake_function
int default_wake_function(wait_queue_t *curr, unsigned mode, int sync, void *key)
{
    task_t *p = curr->task;
    return try_to_wake_up(p, mode, sync);
}


try_to_wake_up() -- 尝试唤醒线程p
which is used to wake up any sleeping processes given the wait_queue.

linux-2.6.21.5/kernel/sched.c

    唤醒操作通过函数wake_up进行,它会唤醒指定的等待队列上的所有进程。它调用函数try_to_wake_up,该函数负责将进程设置为TASK_RUNNING状态,调用activate_task将此进程放入可执行队列,如果被唤醒的进程优先级比当前正在运行的进程的优先级高,还有设置need_resched标志。通常哪段代码促使等待条件达成,它就负责随后调用wake_up()函数。
    The try_to_wake_up( ) function awakes a sleeping or stopped processby setting its state to TASK_RUNNING and inserting it into the runqueueof the local CPU.
    The function receives as its parameters:
1. The descriptor pointer (p) of the process to be awakened
2. A mask of the process states (state) that can be awakened(要唤醒的进程状态集)
3. A flag (sync) that forbids the awakened process to preempt the process currently running on the local CPU
(如果sync为1则表示禁止唤醒进程p抢占当前进程)



static int try_to_wake_up(task_t * p, unsigned int state, int sync)
{
    int cpu, this_cpu, success = 0;
    unsigned long flags;
    long old_state;
    runqueue_t *rq;
#ifdef CONFIG_SMP
    unsigned long load, this_load;
    struct sched_domain *sd;
    int new_cpu;
#endif

1. 关闭本地中断并给本地可执行队列rq加锁
Invokes the task_rq_lock( ) function to disable local interrupts and toacquire the lock of the runqueue rq owned by the CPU that was lastexecuting the process (it could be different from the local CPU). Thelogical number of that CPU is stored in the p->thread_info->cpufield.
|----------------------------------|
|   rq = task_rq_lock(p, &flags); -|
|----------------------------------|

2. 如果当前进程状态p->state不在要唤醒的进程状态集中,则不能唤醒该进程
Checks if the state of the process p->state belongs to the mask ofstates state passed as argument to the function; if this is not thecase, it jumps to step 9 to terminate the function.
|----------------------------------|
|   old_state = p->state;          |
|   if (!(old_state & state))      |
|       goto out;                  |
|----------------------------------|

3. 如果当前进程本身就在可执行队列中,则无需唤醒本进程
If the p->array field is not NULL, the process already belongs to a runqueue; therefore, it jumps to step 8.
|----------------------------------|
|   if (p->array)                  |
|       goto out_running;          |
|----------------------------------|

task_cpu(p)返回当前进程p所使用的CPU编号(p所归属的runqueue所在的CPU编号)
|----------------------------------|
|   cpu = task_cpu(p);             |
|   this_cpu = smp_processor_id(); |
|----------------------------------|

4. 多处理器系统:

#ifdef CONFIG_SMP
    if (unlikely(task_running(rq, p)))
        goto out_activate;
#ifdef CONFIG_SCHEDSTATS
    schedstat_inc(rq, ttwu_cnt);
    if (cpu == this_cpu) {
        schedstat_inc(rq, ttwu_local);
    } else {
        for_each_domain(this_cpu, sd) {
            if (cpu_isset(cpu, sd->span)) {
                schedstat_inc(sd, ttwu_wake_remote);
                break;
            }
        }
    }
#endif

    new_cpu = cpu;
    if (cpu == this_cpu || unlikely(!cpu_isset(this_cpu, p->cpus_allowed)))
        goto out_set_cpu;

    load = source_load(cpu);
    this_load = target_load(this_cpu);

    if (sync)
        this_load -= SCHED_LOAD_SCALE;

    if (load < SCHED_LOAD_SCALE/2 && this_load > SCHED_LOAD_SCALE/2)
        goto out_set_cpu;

    new_cpu = this_cpu;

    for_each_domain(this_cpu, sd) {
        unsigned int imbalance;
        imbalance = sd->imbalance_pct + (sd->imbalance_pct - 100) / 2;

        if ((sd->flags & SD_WAKE_AFFINE) &&
                !task_hot(p, rq->timestamp_last_tick, sd)) {
            if (cpu_isset(cpu, sd->span)) {
                schedstat_inc(sd, ttwu_move_affine);
                goto out_set_cpu;
            }
        } else if ((sd->flags & SD_WAKE_BALANCE) &&
                imbalance*this_load <= 100*load) {
             if (cpu_isset(cpu, sd->span)) {
                schedstat_inc(sd, ttwu_move_balance);
                goto out_set_cpu;
            }
        }
    }

    new_cpu = cpu;
out_set_cpu:
    new_cpu = wake_idle(new_cpu, p);
    if (new_cpu != cpu) {
        set_task_cpu(p, new_cpu);
        task_rq_unlock(rq, &flags);
        rq = task_rq_lock(p, &flags);
        old_state = p->state;
        if (!(old_state & state))
            goto out;
        if (p->array)
            goto out_running;

        this_cpu = smp_processor_id();
        cpu = task_cpu(p);
    }

out_activate:
#endif


5.If the process is in theTASK_UNINTERRUPTIBLE state, it decreases the nr_uninterruptible fieldof the target runqueue, and sets the p->activated field of theprocess descriptor to -1.
|----------------------------------------------|
|   if (old_state == TASK_UNINTERRUPTIBLE) {   |
|       rq->nr_uninterruptible--;              |
|       p->activated = -1;                     |
|   }                                          |
|----------------------------------------------|

6.更新唤醒进程p的平均睡眠时间sleep_avg和动态优先级prio;记录该进程唤醒前的睡眠状态;将该进程插入活跃优先级数组
|----------------------------------------------|
|   activate_task(p, rq, cpu == this_cpu);     |
|----------------------------------------------|

7. 如果唤醒进程p的动态优先级prio比当前进程current的动态优先级高则当前进程的TIF_NEED_RESCHED就需要设置
If either the target CPU is not the local CPU or if the sync flag isnot set, it checks whether the new runnable process has a dynamicpriority higher than that of the current process of the rq runqueue(p->prio < rq->curr->prio); if so, invokes resched_task( )to preempt rq->curr.
|----------------------------------------------|
|   if (!sync || cpu != this_cpu) {            |
|       if (TASK_PREEMPTS_CURR(p, rq))         |
|           resched_task(rq->curr);            |
|   }                                          |
|   success = 1;                               |
|----------------------------------------------|

8. Sets the p->state field of the process to TASK_RUNNING.
|---------------------------------|
|out_running:                     |
|    p->state = TASK_RUNNING;     |
|---------------------------------|

9. Invokes task_rq_unlock( ) to unlock the rq runqueue and reenable the local interrupts.
|---------------------------------|
|out:                             |
|    task_rq_unlock(rq, &flags);
-|
|---------------------------------|
    return success;
}

原创粉丝点击