概念
事件对象是一种线程同步对象,允许一个或者多个线程等待同一个事件对象,当事件被传递到事件对象时,满足条件的线程都变为就绪。通常使用事件来通知一系列条件已满足。在Zephyr中每个事件对象使用一个32bit数来跟踪传递的事件集,每个bit表示一个事件。事件可以由ISR或者线程发送,发送事件有两种方法:
- 设置:覆盖现有的事件集,重写这个32bit数
- 发布:以bit方式添加事件到事件基,注意这里只能添加,不能删除
线程可以等待一个或多个事件。在等待多个事件时可以指定等待其中部分或者全部事件。线程在提出等待事件对象时,可以选择在等待前清空等待事件对象上的所有事件。
zephyr的事件内核对象是2021年10月引入https://github.com/zephyrproject-rtos/zephyr/commit/ae394bff7c54202adddbd20eee8a9dcba75d8da5。
API
宏定义
K_EVENT_DEFINE(name)
静态的定义和初始化事件对象。
参数
- name – 事件对象名
函数
void k_event_init(struct k_event *event)
初始化事件对象,在使用事件对象前首先使用该函数对其进行初始化。
参数
- event – 事件对象的地址
void k_event_post(struct k_event *event, uint32_t events)
发布一个或多个事件到事件对象。如果等待event
的线程因为发布的事件满足等待条件,该线程被转为就绪,与设置事件不同,发布的事件是增加到事件对象中。
参数
- event – 事件对象的地址
- events – 发布的事件集合
void k_event_set(struct k_event *event, uint32_t events)
设置事件集合到事件对象,将事件对象中的事件设置为events
。如果等待event
的线程因为设置的事件满足等待条件,该线程被转为就绪。与发布事件不同,设置事件会取代事件对象中的事件集。
参数
- event – 事件对象的地址
- events – 设置的事件集合
uint32_t k_event_wait(struct k_event *event, uint32_t events, bool reset, k_timeout_t timeout)
等待任意指定事件,在event
上等待,有任意指定的事件被发送,或者是等待时间超过timeout
。一个线程最多可以等待32个事件,这些事件由events
中的bit位置标识。
注意 reset = true
将在等待事件前,将event
中已发生的事件,使用的时候请特别注意。
参数
- event – 事件对象的地址
- events – 等待的事件集
- reset – 如果为
true
,等待前清空event
中已发生的事件,如果为false则不清空 - timeout – 指定等待事件的最长事件,可以指定为
K_NO_WAIT
和K_FOREVER
返回值
- 事件集 – 等待成功后返回收到匹配的事件集
- 0 – 指定时间内未收到匹配事件
uint32_t k_event_wait_all(struct k_event *event, uint32_t events, bool reset, k_timeout_t timeout)
等待所有指定事件,在event
上等待,指定的所有事件被发送,或者是等待时间超过timeout
。一个线程最多可以等待32个事件,这些事件由events
中的bit位置标识。
注意 reset = true
将在等待事件前,将event
中已发生的事件,使用的时候请特别注意。
参数
- event – 事件对象的地址
- events – 等待的事件集
- reset – 如果为
true
,等待前清空event
中已发生的事件,如果为false则不清空 - timeout – 指定等待事件的最长事件,可以指定为
K_NO_WAIT
和K_FOREVER
返回值
- 事件集 – 等待成功后返回收到匹配的事件集
- 0 – 指定时间内未收到匹配事件
使用
配置
事件对象的配置选项是CONFIG_EVENTS
,zephyr内核没有给事件对象配置选项设置默认值,编译时将被识别为未配置,因此要使用事件对象需要增加配置CONFIG_EVENTS=y
。
示例
初始化事件对象
使用函数
1 | struct k_event my_event; |
等效于
1 | K_EVENT_DEFINE(my_event); |
设置事件
下列示例在中断服务程序中设置事件为0x001
1 | void input_available_interrupt_handler(void *arg) |
发布事件
下面示例在中断服务程序中发布事件0x120,如果前面的设置事件未被清除,此时my_event
中的事件为0x121
1 | void input_available_interrupt_handler(void *arg) |
等待事件
下面示例等待事件50毫秒,在50毫秒内只要前面的设置和发布示例中任意一个发生都会等待成功
1 | void consumer_thread(void) |
下面示例等待事件50毫秒,在50毫秒没只有前面的设置和发布示例都发生了才会等待成功
1 | void consumer_thread(void) |
代码分析
事件对象的代码实现在kernel\events.c
,事件对象是由struct k_event
进行管理
1 | struct k_event { |
wait_q
用于管理等待该事件对象的线程events
用于保存当前事件对象收到的事件lock
用于保护内核对事件对象的操作的原子性
事件对象的所有操作都是围绕着struct k_event
进行的。
初始化
函数实现代码如下,就是对struct k_event
定义事件的各个成员进行初始化
1 | void z_impl_k_event_init(struct k_event *event) |
用宏可以达到同时定义和初始化的目的,实现如下
1 |
|
等待事件
等待事件可以等待任意指定事件和全部事件,在events.c
中都是由同一个内部函数k_event_wait_internal
实现,只是指定的参数不一样
1 | uint32_t z_impl_k_event_wait(struct k_event *event, uint32_t events, |
内部函数k_event_wait_internal
的第三个参数opetions
用于指定等待的选项,选项定义在events.c
中
1 |
1 | static uint32_t k_event_wait_internal(struct k_event *event, uint32_t events, |
发送事件
发送事件分为设置和发布,在events.c
中都是由同一个内部函数k_event_post_internal
实现,只是指定的参数不一样
1 | void z_impl_k_event_post(struct k_event *event, uint32_t events) |
内部函数k_event_post_internal
的第三个参数accumulat
为true
时表示发送的events
是添加到event
内,为false
时表示是覆盖event
的已有事件
1 | static void k_event_post_internal(struct k_event *event, uint32_t events, |
参考
https://docs.zephyrproject.org/latest/reference/kernel/synchronization/events.html