本文简要介绍Zephyr内核提供的同步手段,说明主要属性和特点。
因为操作系统同步手段的共性,因此本文中有一大部分内容都是操作系统的基本概念。
概述
在嵌入式操作系统中,多线程被调度器同时调度,从宏观上看是线程是并行执行的,对于并行的线程当有执行步骤有先后顺序要求,执行线程任务间有配合需求以及线程间有源共享的情况时,就需要各线程能够同步。
所有的嵌入式操作系统都会提供线程同步手段,Zephyr也不例外,Zephyr提供了信号量,互斥锁,轮询三种内核对象作为同步手段。
信号量(k_sem)
信号量是用于控制多个线程对一组资源的访问,使用信号量在生产者(ISR/Thread)和消费者(thread)之间同步。
Zephyr的互斥量没有比较特殊的地方,标准的互斥接口实现
接口定义在include/kernel.h中,代码实现在kernel/sem.c中,可查看k_sem相关代码。有以下特性:
- Zephyr的信号量在初始化时可以指定初始化计数值和最大计数值,生产者give时计数值+1,但不会超过最大值,消费者take时计数值-1,直到为0。一些其它的操作系统不会设置最大计数值。
- 每次信号量give都会引发调度。
- 如果多个线程都在等待信号量,新产生的信号量会被等待时间最长的最高优先级线程接收。
互斥量(k_mutex)
Mutex主要是用于解决多线程之间资源保护的问题,当线程打算访问一个共享资源时,需要先拿到该资源的互斥锁。 当线程拥有锁后正在访问共享资源,其它线程尝试获取锁时会被阻塞,直到访问资源的线程释放锁。
Zephyr的互斥量没有比较特殊的地方,标准的互斥接口实现
接口定义在include/kernel.h中,代码实现在kernel/mutex.c中,可查看k_mutex相关代码。有以下特性:
- Mutex只能用于线程之间,不能用于ISR中。
- Mutex unlock时会引发调度。
- 低优先级线程获取锁后有高优先级线程等锁时会引起优先级倒置。
轮询(k_poll)
poll算是一个比较特殊的内核对象,和一般操作系统中的事件对象相似,也有点类似于posix中的poll().
polling API对内核对象进行轮询,允许单个线程等待一个或者多个条件满足。条件类型只能是内核对象,目前支持的是Semaphore, FIFO, poll signal三种。
例如一个线程使用polling API同时等待3个semaphore,只要有1个semaphore发生时polling API就会得到通知,之后可以由用户根据需求决定要做什么样的操作。
接口定义在include/kernel.h中,代码实现在kernel/mutex.c中,可查看k_poll相关代码。有以下特性:
- 等待多个条件时,有一个条件满足k_poll就会返回,因此如果要组合条件,还需要使用代码配合
- Sem/FIFO满足条件后, k_poll只是接到通知返回,线程并未获取到Sem/FIFO, 还需要使用代码主动获取
参考
https://docs.zephyrproject.org/latest/reference/kernel/index.html