preempt_count说明

preempt_count:等于0表示可抢占,大于0表示不可抢占,小于0表示bug。

源代码文件:

include/linux/preempt.h

include/asm-generic/preempt.h

include/linux/preempt_mask.h

linux系统在运行时,总会处于某一种特定的上下文中,例如,进程上下文,中断上下文等。为了判断系统当前运行的上下文状态,内核提供了preempt_count变量来记录当前系统运行的上下文信息。

注意,对于preempt_count的操作一般都是在最外层包了一层皮来关闭中断的情况下进行的,操作完成后开启中断。

1、数据结构

抢占计数器preempt_count变量实现和体系结构相关的,比如x86的preempt_count实现为percpu变量,而arm的preempt_count记录在当前进程的进程描述符中。

arch/x86/include/asm/preempt.h
DECLARE_PER_CPU(int, __preempt_count);

arch/arm/include/asm/thread_info.h
struct thread_info {
	/*底层使用的标志位*/
	unsigned long		flags;		/* low level flags */
	/*等于0表示可抢占,大于0表示不可抢占,小于0表示为bug*/
	int			preempt_count;	/* 0 => preemptable, <0 => bug */
	/*地址大小的范围*/
	mm_segment_t		addr_limit;	/* address limit */
	/*进程核心结构*/
	struct task_struct	*task;		/* main task structure */
	/*可执行域*/
	struct exec_domain	*exec_domain;	/* execution domain */
	__u32			cpu;		/* cpu */
	__u32			cpu_domain;	/* cpu domain */
	/*硬件上下文*/
	struct cpu_context_save	cpu_context;	/* cpu context */
	/*系统调用号*/
	__u32			syscall;	/* syscall number */
	__u8			used_cp[16];	/* thread used copro */
	unsigned long		tp_value[2];	/* TLS registers */
#ifdef CONFIG_CRUNCH
	struct crunch_state	crunchstate;
#endif
	union fp_state		fpstate __attribute__((aligned(8)));
	union vfp_state		vfpstate;
#ifdef CONFIG_ARM_THUMBEE
	unsigned long		thumbee_state;	/* ThumbEE Handler Base register */
#endif
};

2、preempt_count字段含义

/*
 * 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

#define PREEMPT_SHIFT	0
#define SOFTIRQ_SHIFT	(PREEMPT_SHIFT + PREEMPT_BITS)  //8
#define HARDIRQ_SHIFT	(SOFTIRQ_SHIFT + SOFTIRQ_BITS)  //16
#define NMI_SHIFT		(HARDIRQ_SHIFT + HARDIRQ_BITS)  //20

#define __IRQ_MASK(x)	((1UL << (x))-1)

/*各个域mask值*/
#define PREEMPT_MASK	(__IRQ_MASK(PREEMPT_BITS) << PREEMPT_SHIFT) //(1<<8)-1=0xff
#define SOFTIRQ_MASK	(__IRQ_MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) //((1<<8)-1)<<8 = 0xff<<8 = 0xff00
#define HARDIRQ_MASK		(__IRQ_MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT)  //((1<<4)-1)<<16 = 0xf<<16 = 0xf0000
#define NMI_MASK		(__IRQ_MASK(NMI_BITS)     << NMI_SHIFT)     	      //((1<<1)-1)<<20 = 0x1<<20 = 0x100000

#define PREEMPT_OFFSET	(1UL << PREEMPT_SHIFT)  //1UL<<0=0x1
/*由于软中断在单个cpu上不会嵌套执行,因此仅第8位就可以用来判断当前是否处于软中断上下文中,而其它的9~15位用于表示是否禁用了中断下半部*/
#define SOFTIRQ_OFFSET	(1UL << SOFTIRQ_SHIFT)  //1UL<<8 = 0x100
#define HARDIRQ_OFFSET	(1UL << HARDIRQ_SHIFT)  //1UL<<16 = 0x10000
#define NMI_OFFSET		(1UL << NMI_SHIFT)         //1UL<<20 = 0x100000

/*用于中断下半部的开启/关闭*/
#define SOFTIRQ_DISABLE_OFFSET	 (2 * SOFTIRQ_OFFSET )  //0x200

#define PREEMPT_ACTIVE_BITS	1
#define PREEMPT_ACTIVE_SHIFT	(NMI_SHIFT + NMI_BITS) //21

/*所有的有效位,NMI_MASK|HARDIRQ_MASK|SOFTIRQ_MASK|PREEMPT_MASK (0x1fffff);~0x200000=0xffcfffff*/
#define PREEMPT_ACTIVE	(__IRQ_MASK(PREEMPT_ACTIVE_BITS) << PREEMPT_ACTIVE_SHIFT) //((1<<1)-1)<<21 = 0x1<<21 = 0x200000

#define hardirq_count()		(preempt_count() & HARDIRQ_MASK)
#define softirq_count()		(preempt_count() & SOFTIRQ_MASK)
#define irq_count()		(preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK  | NMI_MASK))
/*
 * 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())

/*是否位于BH(下半部)临界区或正在处理软中断*/
#define in_softirq()		(softirq_count())

/*是否位于NMI、硬中断上下文、软中断上下文,或位于BH(下半部)临界区*/
#define in_interrupt()		(irq_count())

/*是否位于软中断上下文*/
#define in_serving_softirq()	(softirq_count() & SOFTIRQ_OFFSET)

/*
 * Are we in NMI context?
 */
/*NMI(不可屏蔽)中断上下文*/
#define in_nmi()	(preempt_count() & NMI_MASK)


/*
 * Are we running in atomic context?  WARNING: this macro cannot
 * always detect atomic context; in particular, it cannot know about
 * held spinlocks in non-preemptible kernels.  Thus it should not be
 * used in the general case to determine whether sleeping is possible.
 * Do not use in_atomic() in driver code.
 */
/*当前状态是否处于中断上下文或禁止抢占状态,如果是,说明系统运行在原子上下文中(atomic context)*/
#define in_atomic()	((preempt_count() & ~PREEMPT_ACTIVE) != 0)

3、核心API说明

#define PREEMPT_ENABLED	(0)

static __always_inline int preempt_count(void)
{
	return current_thread_info()->preempt_count;
}

static __always_inline int *preempt_count_ptr(void)
{
	return &current_thread_info()->preempt_count;
}

static __always_inline void preempt_count_set(int pc)
{
	*preempt_count_ptr() = pc;
}

static __always_inline void __preempt_count_add(int val)
{
	*preempt_count_ptr() += val;
}

static __always_inline void __preempt_count_sub(int val)
{
	*preempt_count_ptr() -= val;
}

#define preempt_count_add(val)	__preempt_count_add(val)
#define preempt_count_sub(val)	__preempt_count_sub(val)

#define preempt_count_dec_and_test() __preempt_count_dec_and_test()


#define __preempt_count_inc() 	__preempt_count_add(1)
#define __preempt_count_dec()	 __preempt_count_sub(1)

#define preempt_count_inc() 	preempt_count_add(1)
#define preempt_count_dec() 	preempt_count_sub(1)


include/linux/preempt.h

常用API说明
preempt_enable()		decrement the preempt counter
preempt_disable()		increment the preempt counter
preempt_enable_no_resched()	decrement, but do not immediately preempt
preempt_check_resched()	if needed, reschedule
preempt_count()		return the preempt counter


/*preempt_diable:preempt_count的计数 +1,(preempt_count:0,表示可以抢占,>0:不可抢占,<0:bug)*/
#define preempt_disable() \
do { \
	preempt_count_inc(); \
	barrier(); \
} while (0)

/*preempt_enable,不进行抢占调度*/
#define sched_preempt_enable_no_resched() \
do { \
	barrier(); \
	preempt_count_dec(); \
} while (0)

#define preempt_enable_no_resched() sched_preempt_enable_no_resched()

#ifdef CONFIG_PREEMPT
/*preempt_enable:preempt_count的计数 -1*/
#define preempt_enable() \
do { \
	barrier(); \
	if (unlikely(preempt_count_dec_and_test())) \
		__preempt_schedule();/*抢占调度*/ \
} while (0)

#define preempt_check_resched() \
do { \
	if (should_resched()) \
		__preempt_schedule(); \
} while (0)

#else
#define preempt_enable() \
do { \
	barrier(); \
	preempt_count_dec(); \
} while (0)
#define preempt_check_resched() do { } while (0)
#endif

中断下半部开启和关闭
#define SOFTIRQ_DISABLE_OFFSET	 (2 * SOFTIRQ_OFFSET )  //0x200
static inline void local_bh_enable(void)
{
	__local_bh_enable_ip(_THIS_IP_, SOFTIRQ_DISABLE_OFFSET);
}

void __local_bh_enable_ip(unsigned long ip, unsigned int cnt)
{
	WARN_ON_ONCE(in_irq() || irqs_disabled());
#ifdef CONFIG_TRACE_IRQFLAGS
	local_irq_disable();
#endif
	/*
	 * Are softirqs going to be turned on now:
	 */
	if (softirq_count() == SOFTIRQ_DISABLE_OFFSET)
		trace_softirqs_on(ip);
	/*
	 * Keep preemption disabled until we are done with softirq processing:
	 * 保持禁止抢占,直到完成软件中断(softirq)处理完成。所以先减去(cnt-1),最后在调用preempt_count_dec再减一。
	 */
	preempt_count_sub(cnt - 1);

	if (unlikely(!in_interrupt() && local_softirq_pending())) {
		/*
		 * Run softirq if any pending. And do it in its own stack
		 * as we may be calling this deep in a task call stack already.
		 */
		do_softirq(); /*执行软中断*/
	}

	preempt_count_dec();
#ifdef CONFIG_TRACE_IRQFLAGS
	local_irq_enable();
#endif
	preempt_check_resched();
}


static inline void local_bh_disable(void)
{
	__local_bh_disable_ip(_THIS_IP_, SOFTIRQ_DISABLE_OFFSET);
}

void __local_bh_disable_ip(unsigned long ip, unsigned int cnt)
{
	unsigned long flags;

	WARN_ON_ONCE(in_irq());

	raw_local_irq_save(flags);
	/*
	 * The preempt tracer hooks into preempt_count_add and will break
	 * lockdep because it calls back into lockdep after SOFTIRQ_OFFSET
	 * is set and before current->softirq_enabled is cleared.
	 * We must manually increment preempt_count here and manually
	 * call the trace_preempt_off later.
	 */
	__preempt_count_add(cnt);
	/*
	 * Were softirqs turned off above:
	 */
	if (softirq_count() == (cnt & SOFTIRQ_MASK))
		trace_softirqs_off(ip);
	raw_local_irq_restore(flags);

	if (preempt_count() == cnt) {
#ifdef CONFIG_DEBUG_PREEMPT
		current->preempt_disable_ip = get_parent_ip(CALLER_ADDR1);
#endif
		trace_preempt_off(CALLER_ADDR0, get_parent_ip(CALLER_ADDR1));
	}
}

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值