概念
事件对象是一种线程同步对象,允许一个或者多个线程等待同一个事件对象,当事件被传递到事件对象时,满足条件的线程都变为就绪。通常使用事件来通知一系列条件已满足。在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