进程上下文、中断上下文及原子上下文***

 谈论进程上下文 、中断上下文 、 原子上下文以前,有必要讨论下两个概念:程序员

a -- 上下文并发

       上下文是从英文context翻译过来,指的是一种环境。相对于进程而言,就是进程执行时的环境;异步

       具体来讲就是各个变量和数据,包括全部的寄存器变量、进程打开的文件、内存信息等。函数

b -- 原子性能

       原子(atom)本意是“不能被进一步分割的最小粒子”,而原子操做(atomic operation)意为"不可被中断的一个或一系列操做" ;atom

 

1、为何会有上下文这种概念spa

         内核空间和用户空间是现代操做系统的两种工做模式,内核模块运行在内核空间,而用户态应用程序运行在用户空间。它们表明不一样的级别,而对系统资源具备不一样的访问权限。内核模块运行在最高级别(内核态),这个级下全部的操做都受系统信任,而应用程序运行在较低级别(用户态)。在这个级别,处理器控制着对硬件的直接访问以及对内存的非受权访问。内核态和用户态有本身的内存映射,即本身的地址空间。操作系统

         其中处理器总处于如下状态中的一种:.net

         内核态,运行于进程上下文,内核表明进程运行于内核空间;线程

         内核态,运行于中断上下文,内核表明硬件运行于内核空间;

         用户态,运行于用户空间。

  上下文的切换,用户空间和内核空间具备不一样的 地址映射,通用或专用的寄存器组,而用户空间的进程要传递不少变量、参数给内核,内核也要保存用户进程的一些寄存器、变量等,以便系统调用结束后回到用户 空间继续执行,

 

2、进程上下文

        所谓的进程上下文,就是一个进程在执行的时候,CPU的全部寄存器中的值、进程的状态以及堆栈上的内容,当内核须要切换到另外一个进程时,它 须要保存当前进程的全部状态,即保存当前进程的进程上下文,以便再次执行该进程时,可以恢复切换时的状态,继续执行。

       一个进程的上下文能够分为三个部分:用户级上下文寄存器上下文以及系统级上下文

       用户级上下文: 正文、数据、用户堆栈以及共享存储区;

       寄存器上下文: 通用寄存器、程序寄存器(IP)、处理器状态寄存器(EFLAGS)、栈指针(ESP);

       系统级上下文: 进程控制块task_struct、内存管理信息(mm_struct、vm_area_struct、pgd、pte)、内核栈。

       当发生进程调度时,进行进程切换就是上下文切换(context switch)。

       操做系统必须对上面提到的所有信息进行切换,新调度的进程才能运行。而系统调用进行的是模式切换(mode switch)。模式切换与进程切换比较起来,容易不少,并且节省时间,由于模式切换最主要的任务只是切换进程寄存器上下文的切换

       进程上下文主要是异常处理程序和内核线程。内核之因此进入进程上下文是由于进程自身的一些工做须要在内核中作。例如,系统调用是为当前进程服务的,异常一般是处理进程致使的错误状态等。因此在进程上下文中引用current是有意义的。

 

3、中断上下文

       硬件经过触发信号,向CPU发送中断信号,致使内核调用中断处理程序,进入内核空间。这个过程当中,硬件的一些变量和参数也要传递给内核, 内核经过这些参数进行中断处理。

       因此,“中断上下文”就能够理解为硬件传递过来的这些参数和内核须要保存的一些环境,主要是被中断的进程的环境。

       内核进入中断上下文是由于中断信号而致使的中断处理或软中断。而中断信号的发生是随机的,中断处理程序及软中断并不能事先预测发生中断时当前运行的是哪一个进程,因此在中断上下文中引用current是能够的,但没有意义。

       事实上,对于A进程但愿等待的中断信号,可能在B进程执行期间发生。例如,A进程启动写磁盘操做,A进程睡眠后B进程在运行,当磁盘写完后磁盘中断信号打断的是B进程,在中断处理时会唤醒A进程。

 

4、进程上下文 VS 中断上下文

       内核能够处于两种上下文:进程上下文和中断上下文。

      在系统调用以后,用户应用程序进入内核空间,此后内核空间针对用户空间相应进程的表明就运行于进程上下文。

      异步发生的中断会引起中断处理程序被调用,中断处理程序就运行于中断上下文。

      内核会限制中断上下文的工做,不容许其执行以下操做:

 a -- 进入睡眠状态或主动放弃CPU

        因为中断上下文不属于任何进程,它与current没有任何关系(尽管此时current指向被中断的进程),因此中断上下文一旦睡眠或者放弃CPU,将没法被唤醒。因此也叫原子上下文(atomic context)

b -- 占用互斥体

        为了保护中断句柄临界区资源,不能使用mutexes。若是得到不到信号量,代码就会睡眠,会产生和上面相同的状况,若是必须使用锁,则使用spinlock。

c --  执行耗时的任务

       中断处理应该尽量快,由于内核要响应大量服务和请求,中断上下文占用CPU时间太长会严重影响系统功能。在中断处理例程中执行耗时任务时,应该交由中断处理例程底半部来处理。

d -- 访问用户空间虚拟内存

       由于中断上下文是和特定进程无关的,它是内核表明硬件运行在内核空间,因此在中断上下文没法访问用户空间的虚拟地址

e -- 中断处理例程不该该设置成reentrant(可被并行或递归调用的例程)

       由于中断发生时,preempt和irq都被disable,直到中断返回。因此中断上下文和进程上下文不同,中断处理例程的不一样实例,是不容许在SMP上并发运行的。

f -- 中断处理例程能够被更高级别的IRQ中断

      若是想禁止这种中断,能够将中断处理例程定义成快速处理例程,至关于告诉CPU,该例程运行时,禁止本地CPU上全部中断请求。这直接致使的结果是,因为其余中断被延迟响应,系统性能降低。

 

5、原子上下文

       内核的一个基本原则就是:在中断或者说原子上下文中,内核不能访问用户空间,并且内核是不能睡眠的。也就是说在这种状况下,内核是不能调用有可能引发睡眠的任何函数。

  • #define in_softirq()     (softirq_count()) //在处理软中断中  
  • #define in_interrupt()   (irq_count()) //在处理硬中断或软中断中  
  • #define in_atomic()     ((preempt_count() & ~PREEMPT_ACTIVE) != 0) //包含以上全部状况  

      这四个宏所访问的count都是thread_info->preempt_count。这个变量实际上是一个位掩码。最低8位表示抢占计数,一般由spin_lock/spin_unlock修改,或程序员强制修改,同时代表内核允许的最大抢占深度是256。

  8-15位是软中断计数,一般由local_bh_disable/local_bh_enable修改,同时代表内核允许的最大软中断深度是256。

16-27位是硬中断计数,一般由enter_irq/exit_irq修改,同时代表内核允许的最大硬中断深度是4096。

第28位是PREEMPT_ACTIVE标志。用代码表示就是:

PREEMPT_MASK: 0x000000ff

SOFTIRQ_MASK: 0x0000ff00

HARDIRQ_MASK: 0x0fff0000

       凡是上面4个宏返回1获得地方都是原子上下文,是不允许内核访问用户空间,不允许内核睡眠的,不允许调用任何可能引发睡眠的函数。并且表明thread_info->preempt_count不是0,这就告诉内核,在这里面抢占被禁用。

      但 是,对于in_atomic()来讲,在启用抢占的状况下,它工做的很好,能够告诉内核目前是否持有自旋锁,是否禁用抢占等。可是,在没有启用抢占的状况 下,spin_lock根本不修改preempt_count,因此即便内核调用了spin_lock,持有了自旋锁,in_atomic()仍然会返回 0,错误的告诉内核目前在非原子上下文中。因此凡是依赖in_atomic()来判断是否在原子上下文的代码,在禁抢占的状况下都是有问题的。

相关文章
相关标签/搜索