Zephyr线程调度锁

本文分析Zerphyr如何实现调度加解锁。

最近在看Zephyr内核代码的时候,深入的看了一下调度加解锁的实现,虽然代码比较简练,但实现原理上比较有意思,这里做一个简单的记录。

下面就是加解锁调度的主要代码,可以看到,就是在对sched_spinlock上锁的情况下对sched_locked字段进行加减完成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
static inline void z_sched_lock(void)
{
	--_current->base.sched_locked;

	compiler_barrier();
}

void k_sched_lock(void)
{
	LOCKED(&sched_spinlock) {
		z_sched_lock();
	}
}

void k_sched_unlock(void)
{
	LOCKED(&sched_spinlock) {
		__ASSERT(_current->base.sched_locked != 0U, "");
		__ASSERT(!arch_is_in_isr(), "");

		++_current->base.sched_locked;
		update_cache(0);
	}

	z_reschedule_unlocked();
}

LOCKED(&sched_spinlock)的宏定义在kernel/include/kernel_internal.h

1
2
3
4
#define LOCKED(lck) for (k_spinlock_key_t __i = {},			\
					  __key = k_spin_lock(lck);	\
			!__i.key;					\
			k_spin_unlock(lck, __key), __i.key = 1)

对于锁调度,展开来看就是

1
2
3
4
for (k_spinlock_key_t __i = {},	__key = k_spin_lock(&sched_spinlock); !__i.key;	k_spin_unlock(&sched_spinlock, __key), __i.key = 1)
{
	z_sched_lock();
}

这里非常巧妙的利用了for循环,初始化表达式来做中断lock,增量表达式做中断unlock,同时让退出条件i.key 满足,在下一次循环的时候判断i.key不满足就退出循环,功能相当于是。

1
2
3
k_spinlock_key_t __key = k_spin_lock(&sched_spinlock)
z_sched_lock();
k_spin_unlock(&sched_spinlock, __key)

通常的思维我们会想到用一个全局的变量来表示锁调度,而zephyr内核锁调度是对线程自己的sched_locked进行设置达成:
先看sched_locked的变化,创建线程时sched_locked被初始化为0

1
2
3
4
z_init_thread_base()
{
	thread_base->sched_locked = 0U;
}

在结构体中sched_locked是无符号的8bit整型uint8_t sched_locked
因此z_sched_lock对其减一,就变成了0xFF, k_sched_unlock加一又变回0x0,那么就是:

  • sched_locked = 0xFF 表示锁调度
  • sched_locked = 0x0 表示未锁调度
    但直接在代码中搜索sched_locked,不会发现调度器在对sched_locked进行判断,那么锁调度是如何生效的呢。我们再来看sched_locked所在的结构体
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    union {
    		struct {
    #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
    			uint8_t sched_locked;
    			int8_t prio;
    #else /* LITTLE and PDP */
    			int8_t prio;
    			uint8_t sched_locked;
    #endif
    		};
    		uint16_t preempt;
    	};
    

preempt和sched_locked共用内存,sched_locked刚好被放到preemt的高位,当调度被锁后,preempt的值就是0xFFXX

1
2
3
4
5
6
7
8
9
#define _NON_PREEMPT_THRESHOLD 0x0080U

/* highest value of _thread_base.preempt at which a thread is preemptible */
#define _PREEMPT_THRESHOLD (_NON_PREEMPT_THRESHOLD - 1U)
static inline int is_preempt(struct k_thread *thread)
{
	/* explanation in kernel_struct.h */
	return thread->base.preempt <= _PREEMPT_THRESHOLD;
}

preempt只有在小于_PREEMPT_THRESHOLD(也就是0x7F)时,该线程才能被抢占, 因此一旦sched_locked被设置为0xFF, preempt 0xFFXX必定大于_PREEMPT_THRESHOLD使得当前正在执行的线程不能被抢占而达到锁调度的目的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值