内核抢占和中断(区别)

一 综述

抢占是内核对进程的管理:当高优先级的任务因中断而成为就绪,特定的低优先级进程将让出CPU,而那个高优先级的进程得到CPU。 可以这样(简单地)说,中断是因为硬件,而抢占是因为中断带来特定事件的发生。

二 先导知识

2.1 上下文

一般来说,CPU在任何时刻都处于以下三种情况之一:

(1) 运行于用户空间,执行用户进程;
(2) 运行于内核空间,处于进程上下文;
(3) 运行于内核空间,处于中断上下文。

应用程序通过系统调用陷入内核,此时处于进程上下文。现代几乎所有的CPU体系结构都支持中断。当外部设备产生中断,向CPU发送一个异步信号,CPU调用相应的中断处理程序来处理该中断,此时CPU处于中断上下文

进程上下文中,可以通过current关联相应的任务。进程以进程上下文的形式运行在内核空间,可以发生睡眠,所以在进程上下文中,可以使用信号量(semaphore)。实际上,内核经常在进程上下文中使用信号量来完成任务之间的同步,当然也可以使用锁。

中断上下文不属于任何进程,它与current没有任何关系(尽管此时current指向被中断的进程)。由于没有进程背景,在中断上下文中不能发生睡眠,否则又如何对它进行调度。所以在中断上下文中只能使用锁进行同步,正是因为这个原因,中断上下文也叫做原子上下文(atomic context)(关于同步可以参考同步)。在中断处理程序中,通常会禁止同一中断,甚至会禁止整个本地中断,所以中断处理程序应该尽可能迅速,所以又把中断处理分成上部和下部(关于中断)。

2.2 上下文切换

上下文切换,也就是从一个可执行进程切换到另一个可执行进程。上下文切换由函数context_switch()函数完成,该函数位于kernel/sched.c中,它由进程调度函数schedule()调用。

static inline
task_t * context_switch(runqueue_t *rq, task_t *prev, task_t *next)
{
    struct mm_struct *mm = next->mm;
    struct mm_struct *oldmm = prev->active_mm;

    if (unlikely(!mm)) {
        next->active_mm = oldmm;
        atomic_inc(&oldmm->mm_count);
        enter_lazy_tlb(oldmm, next);
    } else
        switch_mm(oldmm, mm, next);

    if (unlikely(!prev->mm)) {
        prev->active_mm = NULL;
        WARN_ON(rq->prev_mm);
        rq->prev_mm = oldmm;
    }

    /* Here we just switch the register state and the stack. */
    switch_to(prev, next, prev);

    return prev;
}

其中,switch_mm()将虚拟内存映射到新的进程;switch_to完成最终的进程切换,它保存原进程的所有寄存器信息,恢复新进程的所有寄存器信息,并执行新的进程。无论何时,内核想要进行任务切换,都通过调用schedule()完成任务切换。

三 内核可抢占以及可抢占和可中断的区别

首先被中断不是被抢占,中断和抢占是两个概念。
抢占必须涉及进程上下文的切换,而中断是在中断上下文。
所谓可抢占抢的是进程上下文,人人都争取上台。
可中断指的是是否可以中断当前CPU而进入我的中断处理函数。

如果内核是不可抢占的(比如说2.4的内核),一旦切进内核态,只要代码不是主动释放CPU它就可以一直占着CPU。虽不可抢占,但若此时发生中断,代码还是要交出CPU,但是中断返回之后,代码又能霸占CPU了,此为可中断但不可抢占
如果内核是可抢占的(比如2.6或之后的内核),上述情况就不会发生了。

四 抢占

抢占也分用户抢占和内核抢占。详解请参考抢占

4.1 用户抢占

当内核即将返回用户空间时,内核会检查need_resched是否设置,如果设置,则调用schedule(),此时,发生用户抢占。一般来说,用户抢占发生几下情况:
(1) 从系统调用返回用户空间
(2) 从中断(异常)处理程序返回用户空间

4.2 内核抢占

内核从2.6开始就支持内核抢占,对于非内核抢占系统,内核代码可以一直执行,直到完成,也就是说当进程处于内核态时,是不能被抢占的(当然,运行于内核态的进程可以主动放弃CPU,比如,在系统调用服务例程中,由于内核代码由于等待资源而放弃CPU,这种情况叫做计划性进程切换(planned process switch))。但是,对于由异步事件(比如中断)引起的进程切换,抢占式内核与非抢占式是有区别的,对于前者叫做强制性进程切换(forced process switch)。

为了支持内核抢占,内核引入了preempt_count字段,该计数初始值为0,每当使用锁时加1,释放锁时减1。当preempt_count为0时,表示内核可以被安全的抢占,大于0时,则禁止内核抢占。

当从中断返回内核空间时,内核会检preempt_count和need_resched的值(返回用户空间时只需要检查need_resched),如查preempt_count为0且need_resched设置,则调用schedule(),完成任务抢占。

一般来说,内核抢占发生以下情况:

  1. 当一个中断处理例程退出,在返回到内核态时。preempt_count为0且need_resched置位。这是隐式的调用schedule()函数。

  2. 当内核代码再一次具有可抢占性的时候,如解锁(spin_unlock_bh)及使能软中断(local_bh_enable)等,此时当preempt_count从正整数变为0时。这也是隐式的调用schedule()函数

  3. 如果内核中的任务显式的调用schedule(), 任务主动放弃CPU使用权。

  4. 如果内核中的任务阻塞(这同样也会导致调用schedule())。任务主动放弃CPU使用权。

五 中断

详细可参考中断

5.1 中断方式分为同步中断和异步中断

中断方式分为同步中断和异步中断。同步中断是由CPU控制单元产生的,“同步”是指只有在一条指令执行完毕后,CPU才会发出中断,而不是发生在代码指令执行期间,比如系统调用。而异步中断是由其他硬件设备依照CPU时钟信号产生的,即意味着中断能够在指令之间发生,例如键盘中断。有一个知识点值得了解:内核态能够触发的唯一异常就是缺页异常,其他的都是用户态触发的。

5.2 中断过程分为上下两个部分

中断过程分为上下两个部分:中断上半部、下半部。上半部处理紧要事情。下半部处理不那么要紧的任务。比如:网卡上来的数据推进协议栈,然后推给上层应用程序。

当前的内核有三种下半部的实现方式:softirq、tasklet、working queue。

名称上下文特点概述
Softirq中断上下文可中断不可睡眠速度最快。同一个Softirq可能会同时运行在多个核上,必须非常小心的处理数据同步
Tasklet中断上下文可中断不可睡眠基于Softirq实现,同一类的Tasklet不会被同时运行,编程代价小
Working quere进程上下文可中断可睡眠基于内核线程实现

参考链接:https://www.cnblogs.com/hustcat/archive/2009/08/31/1557507.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值