原子上下文
内核的一个基本原则就是:在中断或者说原子上下文中,内核不能访问用户空间,而且内核是不能睡眠的。也就是说在这种情况下,内核是不能调用有可能引起睡眠的任何函数。一般来讲原子上下文指的是在中断或软中断中,以及在持有自旋锁的时候。内核提供 了四个宏来判断是否处于这几种情况里:
这四个宏所访问的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
12345
凡是上面4个宏返回1得到地方都是原子上下文,是不容许内核访问用户空间,不容许内核睡眠的,不容许调用任何可能引起睡眠的函数。而且代表thread_info->preempt_count不是0,这就告诉内核,在这里面抢占被禁用。
但是,对于in_atomic()来说,在启用抢占的情况下,它工作的很好,可以告诉内核目前是否持有自旋锁,是否禁用抢占等。但是,在没有启用抢占的情况 下,spin_lock根本不修改preempt_count,所以即使内核调用了spin_lock,持有了自旋锁,in_atomic()仍然会返回 0,错误的告诉内核目前在非原子上下文中。所以凡是依赖in_atomic()来判断是否在原子上下文的代码,在禁抢占的情况下都是有问题的。
中断上下文
中断上下文,发生中断时,CPU进入中断处理程序,又会产生当前堆栈和CPU寄存器等值。这些内核态堆栈状态和寄存器的值,称为中断上下文。中断上下文保存的是当前中断处理程序的堆栈、CPU寄存器,进程上下文保存的才是进程状态、堆栈信息。
进程运行是由内核通过调度算法决定的,然后进行(进程)上下文切换,这是由操作系统支持。
而中断跟进程没有必然联系,可以没有任何进程,甚至没有操作系统,但仍有中断,中断是由硬件支持的,有操作系统时被接管。中断分为同步中断、异步中断。同样地,没有中断产生,也一样会产生进程上下文切换。
同步中断,又称为异常,是指当指令执行时,由CPU控制单元产生的,如除数0,总线错误,都会产生同步中断;异步中断,由其他硬件设备依照CPU时钟随机产生的,如定时器中断、按键中断、串口数据中断,CPU无法知道异步中断什么时候会来。