网站如何建立数据库,东莞专业网站建设推广,网站搭建工作室加盟,网站logo的颜色与网页的颜色谈论进程上下文 、中断上下文 、 原子上下文之前#xff0c;有必要讨论下两个概念#xff1a; a -- 上下文 上下文是从英文context翻译过来#xff0c;指的是一种环境。相对于进程而言#xff0c;就是进程执行时的环境#xff1b; 具体来说就是各个变量和数据#xff0c;… 谈论进程上下文 、中断上下文 、 原子上下文之前有必要讨论下两个概念 a -- 上下文 上下文是从英文context翻译过来指的是一种环境。相对于进程而言就是进程执行时的环境 具体来说就是各个变量和数据包括所有的寄存器变量、进程打开的文件、内存信息等。 b -- 原子 原子atom本意是“不能被进一步分割的最小粒子”而原子操作atomic operation意为不可被中断的一个或一系列操作 一、为什么会有上下文这种概念 内核空间和用户空间是现代操作系统的两种工作模式内核模块运行在内核空间而用户态应用程序运行在用户空间。它们代表不同的级别而对系统资源具有不同的访问权限。内核模块运行在最高级别内核态这个级下所有的操作都受系统信任而应用程序运行在较低级别用户态。在这个级别处理器控制着对硬件的直接访问以及对内存的非授权访问。内核态和用户态有自己的内存映射即自己的地址空间。 其中处理器总处于以下状态中的一种 内核态运行于进程上下文内核代表进程运行于内核空间 内核态运行于中断上下文内核代表硬件运行于内核空间 用户态运行于用户空间。 系统的两种不同运行状态才有了上下文的概念。用户空间的应用程序如果想请求系统服务比如操作某个物理设备映射设备的地址到用户空间必须通过系统调用来实现。系统调用是操作系统提供给用户空间的接口函数。 通过系统调用用户空间的应用程序就会进入内核空间由内核代表该进程运行于内核空间这就涉及到上下文的切换用户空间和内核空间具有不同的 地址映射通用或专用的寄存器组而用户空间的进程要传递很多变量、参数给内核内核也要保存用户进程的一些寄存器、变量等以便系统调用结束后回到用户 空间继续执行 二、进程上下文 所谓的进程上下文就是一个进程在执行的时候CPU的所有寄存器中的值、进程的状态以及堆栈上的内容当内核需要切换到另一个进程时它 需要保存当前进程的所有状态即保存当前进程的进程上下文以便再次执行该进程时能够恢复切换时的状态继续执行。 一个进程的上下文可以分为三个部分:用户级上下文、寄存器上下文以及系统级上下文。 用户级上下文: 正文、数据、用户堆栈以及共享存储区 寄存器上下文: 通用寄存器、程序寄存器(IP)、处理器状态寄存器(EFLAGS)、栈指针(ESP) 系统级上下文: 进程控制块task_struct、内存管理信息(mm_struct、vm_area_struct、pgd、pte)、内核栈。 当发生进程调度时进行进程切换就是上下文切换(context switch)。 操作系统必须对上面提到的全部信息进行切换新调度的进程才能运行。而系统调用进行的是模式切换(mode switch)。模式切换与进程切换比较起来容易很多而且节省时间因为模式切换最主要的任务只是切换进程寄存器上下文的切换。 进程上下文主要是异常处理程序和内核线程。内核之所以进入进程上下文是因为进程自身的一些工作需要在内核中做。例如系统调用是为当前进程服务的异常通常是处理进程导致的错误状态等。所以在进程上下文中引用current是有意义的。 三、中断上下文 硬件通过触发信号向CPU发送中断信号导致内核调用中断处理程序进入内核空间。这个过程中硬件的一些变量和参数也要传递给内核 内核通过这些参数进行中断处理。 所以“中断上下文”就可以理解为硬件传递过来的这些参数和内核需要保存的一些环境主要是被中断的进程的环境。 内核进入中断上下文是因为中断信号而导致的中断处理或软中断。而中断信号的发生是随机的中断处理程序及软中断并不能事先预测发生中断时当前运行的是哪个进程所以在中断上下文中引用current是可以的但没有意义。 事实上对于A进程希望等待的中断信号可能在B进程执行期间发生。例如A进程启动写磁盘操作A进程睡眠后B进程在运行当磁盘写完后磁盘中断信号打断的是B进程在中断处理时会唤醒A进程。 四、进程上下文 VS 中断上下文 内核可以处于两种上下文进程上下文和中断上下文。 在系统调用之后用户应用程序进入内核空间此后内核空间针对用户空间相应进程的代表就运行于进程上下文。 异步发生的中断会引发中断处理程序被调用中断处理程序就运行于中断上下文。 中断上下文和进程上下文不可能同时发生。 运行于进程上下文的内核代码是可抢占的但中断上下文则会一直运行至结束不会被抢占。因此内核会限制中断上下文的工作不允许其执行如下操作 a -- 进入睡眠状态或主动放弃CPU 由于中断上下文不属于任何进程它与current没有任何关系尽管此时current指向被中断的进程所以中断上下文一旦睡眠或者放弃CPU将无法被唤醒。所以也叫原子上下文atomic context。 b -- 占用互斥体 为了保护中断句柄临界区资源不能使用mutexes。如果获得不到信号量代码就会睡眠会产生和上面相同的情况如果必须使用锁则使用spinlock。 c -- 执行耗时的任务 中断处理应该尽可能快因为内核要响应大量服务和请求中断上下文占用CPU时间太长会严重影响系统功能。在中断处理例程中执行耗时任务时应该交由中断处理例程底半部来处理。 d -- 访问用户空间虚拟内存 因为中断上下文是和特定进程无关的它是内核代表硬件运行在内核空间所以在中断上下文无法访问用户空间的虚拟地址 e -- 中断处理例程不应该设置成reentrant可被并行或递归调用的例程 因为中断发生时preempt和irq都被disable直到中断返回。所以中断上下文和进程上下文不一样中断处理例程的不同实例是不允许在SMP上并发运行的。 f -- 中断处理例程可以被更高级别的IRQ中断 如果想禁止这种中断可以将中断处理例程定义成快速处理例程相当于告诉CPU该例程运行时禁止本地CPU上所有中断请求。这直接导致的结果是由于其他中断被延迟响应系统性能下降。 五、原子上下文 内核的一个基本原则就是在中断或者说原子上下文中内核不能访问用户空间而且内核是不能睡眠的。也就是说在这种情况下内核是不能调用有可能引起睡眠的任何函数。一般来讲原子上下文指的是在中断或软中断中以及在持有自旋锁的时候。内核提供 了四个宏来判断是否处于这几种情况里 [cpp] view plaincopy #define in_irq() (hardirq_count()) //在处理硬中断中 #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。 815位是软中断计数通常由local_bh_disable/local_bh_enable修改同时表明内核容许的最大软中断深度是256。 1627位是硬中断计数通常由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()来判断是否在原子上下文的代码在禁抢占的情况下都是有问题的。