linux 配置 内核抢占,Linux内核抢占实现机制分析

另外一种可抢占内核实现方案是在内核代码段中插入抢占点(preemption point)的方案。在这一方案中,首先要找出内核中产生长延迟的代码段,然后在这一内核代码段的适当位置插入抢占点,使得系统不必等到这段代码执行完就可重新调度。这样对于需要快速响应的事件,系统就可以尽快地将服务进程调度到CPU运行。抢占点实际上是对进程调度函数的调用,代码如下:

if(current->need_resched)schedule();

通常这样的代码段是一个循环体,插入抢占点的方案就是在这一循环体中不断检测need_ resched的值,在必要的时候调用schedule()令当前进程强行放弃CPU

8、何时需要重新调度

内核必须知道在什么时候调用schedule()。如果仅靠用户程序代码显式地调用schedule(),它们可能就会永远地执行下去。相反,内核提供了一个need_resched标志来表明是否需要重新执行一次调度。当某个进程耗尽它的时间片时,scheduler tick()就会设置这个标志;当一个优先级高的进程进入可执行状态的时候,try_to_wake_up也会设置这个标志。

set_ tsk_need_resched:设置指定进程中的need_ resched标志

clear tsk need_resched:清除指定进程中的need_ resched标志

need_resched():检查need_ resched标志的值;如果被设置就返回真,否则返回假

信号量、等到队列、completion等机制唤醒时都是基于waitqueue的,而waitqueue的唤醒函数为default_wake_function,其调用try_to_wake_up将进程更改为可运行状态并置待调度标志。

在返回用户空间以及从中断返回的时候,内核也会检查need_resched标志。如果已被设置,内核会在继续执行之前调用调度程序。

每个进程都包含一个need_resched标志,这是因为访问进程描述符内的数值要比访问一个全局变量快(因为current宏速度很快并且描述符通常都在高速缓存中)。在2.2以前的内核版本中,该标志曾经是一个全局变量。2.2到2.4版内核中它在task_struct中。而在2.6版中,它被移到thread_info结构体里,用一个特别的标志变量中的一位来表示。可见,内核开发者总是在不断改进。

/linux+v2.6.19/include/linux/sched.h

1503static inline void set_tsk_need_resched(struct task_struct *tsk)

1504{

1505        set_tsk_thread_flag(tsk,TIF_NEED_RESCHED);

1506}

1507

1508static inline void clear_tsk_need_resched(struct task_struct *tsk)

1509{

1510        clear_tsk_thread_flag(tsk,TIF_NEED_RESCHED);

1511}

1512

1513static inline int signal_pending(struct task_struct *p)

1514{

1515        return unlikely(test_tsk_thread_flag(p,TIF_SIGPENDING));

1516}

1517

1518static inline int need_resched(void)

1519{

1520        return unlikely(test_thread_flag(TIF_NEED_RESCHED));

1521}

///

/linux+v2.6.19/kernel/sched.c

991/*

992 * resched_task - mark a task 'to be rescheduled now'.

993 *

994 * On UP this means the setting of the need_resched flag, on SMP it

995 * might also involve a cross-CPU call to trigger the scheduler on

996 * the target CPU.

997 */

998#ifdef CONFIG_SMP

999

1000#ifndef tsk_is_polling

1001#define tsk_is_polling(t) test_tsk_thread_flag(t, TIF_POLLING_NRFLAG)

1002#endif

1003

1004static void resched_task(struct task_struct *p)

1005{

1006        int cpu;

1007

1008        assert_spin_locked(&task_rq(p)->lock);

1009

1010        if (unlikely(test_tsk_thread_flag(p, TIF_NEED_RESCHED)))

1011                return;

1012

1013        set_tsk_thread_flag(p, TIF_NEED_RESCHED);

1014

1015        cpu = task_cpu(p);

1016        if (cpu == smp_processor_id())

1017                return;

1018

1019        /* NEED_RESCHED must be visible before we test polling */

1020        smp_mb();

1021        if (!tsk_is_polling(p))

1022                smp_send_reschedule(cpu);

1023}

1024#else

1025static inline void resched_task(struct task_struct *p)

1026{

1027        assert_spin_locked(&task_rq(p)->lock);

1028        set_tsk_need_resched(p);

1029}

1030#endif

///

///

1366/***

1367 * try_to_wake_up - wake up a thread

1368 * @p: the to-be-woken-up thread

1369 * @state: the mask of task states that can be woken

1370 * @sync: do a synchronous wakeup?

1371 *

1372 * Put it on the run-queue if it's not already there. The "current"

1373 * thread is always on the run-queue (except when the actual

1374 * re-schedule is in progress), and as such you're allowed to do

1375 * the simpler "current->state = TASK_RUNNING" to mark yourself

1376 * runnable without the overhead of this.

1377 *

1378 * returns failure only if the task is already active.

1379 */

1380static int try_to_wake_up(struct task_struct *p, unsigned int state, int sync)

///

///

1538int fastcall wake_up_process(struct task_struct *p)

1539{

1540        return try_to_wake_up(p, TASK_STOPPED | TASK_TRACED |

1541                                 TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE, 0);

1542}

1543EXPORT_SYMBOL(wake_up_process);

1545int fastcall wake_up_state(struct task_struct *p, unsigned int state)

1546{

1547        return try_to_wake_up(p, state, 0);

1548}

1616/*

1617 * wake_up_new_task - wake up a newly created task for the first time.

1618 *

1619 * This function will do some initial scheduler statistics housekeeping

1620 * that must be done for every newly created context, then puts the task

1621 * on the runqueue and wakes it.

1622 */

1623void fastcall wake_up_new_task(struct task_struct *p, unsigned long clone_flags)

3571/*

3572 * The core wakeup function.  Non-exclusive wakeups (nr_exclusive == 0) just

3573 * wake everything up.  If it's an exclusive wakeup (nr_exclusive == small +ve

3574 * number) then we wake all the non-exclusive tasks and one exclusive task.

3575 *

3576 * There are circumstances in which we can try to wake a task which has already

3577 * started to run but is not in state TASK_RUNNING.  try_to_wake_up() returns

3578 * zero in this (rare) case, and we handle it by continuing to scan the queue.

3579 */

3580static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,

3581                             int nr_exclusive, int sync, void *key)

///

///

3595/**

3596 * __wake_up - wake up threads blocked on a waitqueue.

3597 * @q: the waitqueue

3598 * @mode: which threads

3599 * @nr_exclusive: how many wake-one or wake-many threads to wake up

3600 * @key: is directly passed to the wakeup function

3601 */

3602void fastcall __wake_up(wait_queue_head_t *q, unsigned int mode,

3603                        int nr_exclusive, void *key)

3604{

3605        unsigned long flags;

3606

3607        spin_lock_irqsave(&q->lock, flags);

3608        __wake_up_common(q, mode, nr_exclusive, 0, key);

3609        spin_unlock_irqrestore(&q->lock, flags);

3610}

3611EXPORT_SYMBOL(__wake_up);

3564int default_wake_function(wait_queue_t *curr, unsigned mode, int sync,

3565                          void *key)

3566{

3567        return try_to_wake_up(curr->private, mode, sync);

3568}

3569EXPORT_SYMBOL(default_wake_function);

3652void fastcall complete(struct completion *x)

3653{

3654        unsigned long flags;

3655

3656        spin_lock_irqsave(&x->wait.lock, flags);

3657        x->done++;

3658        __wake_up_common(&x->wait, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE,

3659                         1, 0, NULL);

3660        spin_unlock_irqrestore(&x->wait.lock, flags);

3661}

3662EXPORT_SYMBOL(complete);

33/3<123

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值