preempt_count
Posted Loopers
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了preempt_count相关的知识,希望对你有一定的参考价值。
定义
/*
* low level task data that entry.S needs immediate access to.
* __switch_to() assumes cpu_context follows immediately after cpu_domain.
*/
struct thread_info
unsigned long flags; /* low level flags */
mm_segment_t addr_limit; /* address limit */
struct task_struct *task; /* main task structure */
struct exec_domain *exec_domain; /* execution domain */
struct restart_block restart_block;
int preempt_count; /* 0 => preemptable, <0 => bug */
int cpu; /* cpu */
;
在支持可抢占的系统中,一个进程的therad_info信息定义如上。其中preempt_count代表的是该进程是否可以被抢占,根据注释的说明当peermpt_count等于0的时候当前进程就可以被抢占,当小于0存在bug,当不等于0也就是大于0说明当前进程不可以被抢占。不可抢占的原因很多,比如当前进程在中断上下文中或者使用了锁(spin_lock的过程中会disable掉抢占的)。至于当前是什么原因不能被抢占,就需要看peermpt_count每个字段的含义。
字段含义
先来看如下图所示的各个字段的含义再来看看代码中关于preempt_count的各个字段的定义:
<linux/include/preempt_mask.h>
------------------------------------------
/*
* We put the hardirq and softirq counter into the preemption
* counter. The bitmask has the following meaning:
*
* - bits 0-7 are the preemption count (max preemption depth: 256)
* - bits 8-15 are the softirq count (max # of softirqs: 256)
*
* The hardirq count could in theory be the same as the number of
* interrupts in the system, but we run all interrupt handlers with
* interrupts disabled, so we cannot have nesting interrupts. Though
* there are a few palaeontologic drivers which reenable interrupts in
* the handler, so we need more than one bit here.
*
* PREEMPT_MASK: 0x000000ff
* SOFTIRQ_MASK: 0x0000ff00
* HARDIRQ_MASK: 0x000f0000
* NMI_MASK: 0x00100000
* PREEMPT_ACTIVE: 0x00200000
*/
#define PREEMPT_BITS 8
#define SOFTIRQ_BITS 8
#define HARDIRQ_BITS 4
#define NMI_BITS 1
结合上述的示图和代码的定义可知,bit0-7代表的是抢占的次数,最大抢占深度为256次; bit8-15代表的是软中断的次数,最大也是256次; bit16-19表示硬件中断的次数,注释的大概意思是避免中断嵌套,但是也不能防止某些驱动中嵌套使用中断,所以嵌套16层也是最大次数了。bit20代表NMI中断,bit21代表当前抢占是否active。
相关宏定义
linux系统为了方便得出各个字段的值,提供了一系列宏定义如下:#define PREEMPT_SHIFT 0
#define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) //0+8=8
#define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) //8+8=16
#define NMI_SHIFT (HARDIRQ_SHIFT + HARDIRQ_BITS) //16+4=20
#define __IRQ_MASK(x) ((1UL << (x))-1)
#define PREEMPT_MASK (__IRQ_MASK(PREEMPT_BITS) << PREEMPT_SHIFT)
#define SOFTIRQ_MASK (__IRQ_MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT)
#define HARDIRQ_MASK (__IRQ_MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT)
#define NMI_MASK (__IRQ_MASK(NMI_BITS) << NMI_SHIFT)
#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) //1<<0
#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) //1<<8
#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) //1<<16
#define NMI_OFFSET (1UL << NMI_SHIFT) //1<<20
#define SOFTIRQ_DISABLE_OFFSET (2 * SOFTIRQ_OFFSET) //16
#define PREEMPT_ACTIVE_BITS 1
#define PREEMPT_ACTIVE_SHIFT (NMI_SHIFT + NMI_BITS)
#define PREEMPT_ACTIVE (__IRQ_MASK(PREEMPT_ACTIVE_BITS) << PREEMPT_ACTIVE_SHIFT)
#define hardirq_count() (preempt_count() & HARDIRQ_MASK) //硬中断count
#define softirq_count() (preempt_count() & SOFTIRQ_MASK) //软中断count
#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK \\
| NMI_MASK)) //所有中断=硬+软+NMI
从上述的定义可以得出,如果想知道硬中断的次数就使用hardirq_count, 如果想知道软中断次数就使用softirq_count, 如果想知道所有中断的次数就使用irq_count。
各种上下文
/*
* Are we doing bottom half or hardware interrupt processing?
* Are we in a softirq context? Interrupt context?
* in_softirq - Are we currently processing softirq or have bh disabled?
* in_serving_softirq - Are we currently processing softirq?
*/
#define in_irq() (hardirq_count())
#define in_softirq() (softirq_count())
#define in_interrupt() (irq_count())
#define in_serving_softirq() (softirq_count() & SOFTIRQ_OFFSET)
其中in_irq用于判断当前进程是否在硬中断中; in_softirq用于判断是否当前进程在软件中断或者有别的进程disable了软中断
in_interrupt用于判断当前进程是否在中断中; 而in_serving_softirq用于判断当前进程是否在软件中断中,通过bit8这一位来判断。
#define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != 0)
使用上述的宏判断当前进程是否处于原子操作中。
开发者涨薪指南 48位大咖的思考法则、工作方式、逻辑体系
以上是关于preempt_count的主要内容,如果未能解决你的问题,请参考以下文章
LInux中ThreadInfo中的preempt_count字段