简介
events 是Zephyr 提供的一种多线程同步方式,其功能和 FreeRTOS 中的 eventGroup 一样,可以用于等待多个用户事件发生。 events 对象用于向一个或多个线程发出信号,指示发生了一组自定义事件。 线程等待 events 对象,直到另一个线程或 ISR 将所需的事件集发布到事件对象。 每次将事件发布到 events 对象时, events 对象会处理等待该事件对象的所有线程以确定是否存在匹配项。 等待条件与事件对象中的事件集匹配的所有线程都将被唤醒。
数据结构
k_event
struct k_event {
_wait_q_t wait_q;
uint32_t events;
struct k_spinlock lock;
SYS_PORT_TRACING_TRACKING_FIELD ( k_event)
} ;
该结构体中包含2个主要成员:
wait_q 等待队列,当线程调用等待函数时,如果条件不满足,那么将会被添加到等待队列中。 events 事件的状态,已发生为1,未发生为0。 spinlock 是为了多线程安全操作。
event_walk_data
struct event_walk_data {
struct k_thread * head;
uint32_t events;
} ;
其中 events 为事件发布后事件对象中 events 成员变量的值,代表当前已经发生的事件。 当线程或者中断向事件对象发布事件时,因为等待事件被挂起的线程可能需要被唤醒,但是这些线程等待的事件并不相同,需要从等待队列 wait_q 中查找出满足唤醒条件的线程,并将这些线程作为节点添加到被唤醒链表中,head即为链表头。
static int event_walk_op ( struct k_thread * thread, void * data)
{
unsigned int wait_condition;
struct event_walk_data * event_data = data;
wait_condition = thread-> event_options & K_EVENT_WAIT_MASK;
if ( are_wait_conditions_met ( thread-> events, event_data-> events,
wait_condition) ) {
thread-> no_wake_on_timeout = true;
thread-> next_event_link = event_data-> head;
event_data-> head = thread;
z_abort_timeout ( & thread-> base. timeout) ;
}
return 0 ;
}
程序依次遍历wait_q 中的线程,如果发现其等待的条件已满足,调用 event_walk_op 从链表头部插入线程。 当遍历完成后依次取出单链表中的线程,将其唤醒。
events 初始化
Z_EVENT_INITIALIZER
静态初始化使用于为 k_event 全局对象设置初始值
# define Z_EVENT_INITIALIZER ( obj) \
{ \
. wait_q = Z_WAIT_Q_INIT ( & obj. wait_q) , \
. events = 0 \
}
void k_event_init(struct k_event *event)
k_event_init 为运行时初始化函数,其具体的实现由z_impl_k_event_init 完成, 该函数功能与 Z_EVENT_INITIALIZER 一致。
void z_impl_k_event_init ( struct k_event * event)
{
event-> events = 0 ;
event-> lock = ( struct k_spinlock ) { } ;
SYS_PORT_TRACING_OBJ_INIT ( k_event, event) ;
z_waitq_init ( & event-> wait_q) ;
z_object_init ( event) ;
}
发布事件
void k_event_post(struct k_event *event, uint32_t events)
k_event_post 用于向事件对象发布指定的事件集,其实现由 z_impl_k_event_post 完成。
void z_impl_k_event_post ( struct k_event * event, uint32_t events)
{
k_event_post_internal ( event, events, events) ;
}
static void k_event_post_internal ( struct k_event * event, uint32_t events,
uint32_t events_mask)
{
k_spinlock_key_t key;
struct k_thread * thread;
struct event_walk_data data;
data. head = NULL ;
key = k_spin_lock ( & event-> lock) ;
SYS_PORT_TRACING_OBJ_FUNC_ENTER ( k_event, post, event, events,
events_mask) ;
events = ( event-> events & ~ events_mask) |
( events & events_mask) ;
event-> events = events;
data. events = events;
z_sched_waitq_walk ( & event-> wait_q, event_walk_op, & data) ;
if ( data. head != NULL ) {
thread = data. head;
struct k_thread * next;
do {
arch_thread_return_value_set ( thread, 0 ) ;
thread-> events = events;
next = thread-> next_event_link;
z_sched_wake_thread ( thread, false) ;
thread = next;
} while ( thread != NULL ) ;
}
z_reschedule ( & event-> lock, key) ;
SYS_PORT_TRACING_OBJ_FUNC_EXIT ( k_event, post, event, events,
events_mask) ;
}
设置事件
void k_event_set(struct k_event *event, uint32_t events)
k_event_set 和 k_event_post 的区别类似于C语言中 = 与 |= 之间的差别,k_event_set 会将事件对象中的事件赋值为 events,而 k_event_post 只会影响指定的位,未指定的位则保持不变。
void z_impl_k_event_set ( struct k_event * event, uint32_t events)
{
k_event_post_internal ( event, events, ~ 0 ) ;
}
设置或清除事件
void k_event_set_masked(struct k_event *event, uint32_t events, uint32_t events_mask)
k_event_set_masked 会将 events_mask 中指定的事件清除,并设置 events 中指定的事件。
void z_impl_k_event_set_masked ( struct k_event * event, uint32_t events,
uint32_t events_mask)
{
k_event_post_internal ( event, events, events_mask) ;
}
清除事件
k_event_clear(struct k_event *event, uint32_t events)
k_event_clear 用于将 events 指定的事件清除
void z_impl_k_event_clear ( struct k_event * event, uint32_t events)
{
k_event_post_internal ( event, 0 , events) ;
}
等待事件
等待选项
# define K_EVENT_WAIT_ANY 0x00
# define K_EVENT_WAIT_ALL 0x01
# define K_EVENT_WAIT_MASK 0x01
# define K_EVENT_WAIT_RESET 0x02
选项说明:
bit0 为 1,等待所有事件,bit0 为 0,等待其中任一事件 bit1 为 1,在调用 k_event_wait 时先清除所有事件,bit1 为 0,不清除。
uint32_t k_event_wait(struct k_event *event, uint32_t events, bool reset, k_timeout_t timeout)
k_event_wait 中包含4个参数:
events 等待的事件 reset 是否在调用 k_event_wait 时先清除所有事件 timeout 超时时间 k_event_wait 由 z_impl_k_event_wait 实现相应功能。
uint32_t z_impl_k_event_wait ( struct k_event * event, uint32_t events,
bool reset, k_timeout_t timeout)
{
uint32_t options = reset ? K_EVENT_WAIT_RESET : 0 ;
return k_event_wait_internal ( event, events, options, timeout) ;
}
该函数中 options 的 bit0 为 0,代表任一事件发生即会返回 options 的 bit1 由 reset 决定,当 reset 为 true 时会先将所有事件清除,再等待事件。
static uint32_t k_event_wait_internal ( struct k_event * event, uint32_t events,
unsigned int options, k_timeout_t timeout)
{
uint32_t rv = 0 ;
unsigned int wait_condition;
struct k_thread * thread;
__ASSERT ( ( ( arch_is_in_isr ( ) == false) ||
K_TIMEOUT_EQ ( timeout, K_NO_WAIT) ) , "" ) ;
SYS_PORT_TRACING_OBJ_FUNC_ENTER ( k_event, wait, event, events,
options, timeout) ;
if ( events == 0 ) {
SYS_PORT_TRACING_OBJ_FUNC_EXIT ( k_event, wait, event, events, 0 ) ;
return 0 ;
}
wait_condition = options & K_EVENT_WAIT_MASK;
thread = z_current_get ( ) ;
k_spinlock_key_t key = k_spin_lock ( & event-> lock) ;
if ( options & K_EVENT_WAIT_RESET) {
event-> events = 0 ;
}
if ( are_wait_conditions_met ( events, event-> events, wait_condition) ) {
rv = event-> events;
k_spin_unlock ( & event-> lock, key) ;
goto out;
}
if ( K_TIMEOUT_EQ ( timeout, K_NO_WAIT) ) {
k_spin_unlock ( & event-> lock, key) ;
goto out;
}
thread-> events = events;
thread-> event_options = options;
SYS_PORT_TRACING_OBJ_FUNC_BLOCKING ( k_event, wait, event, events,
options, timeout) ;
if ( z_pend_curr ( & event-> lock, key, & event-> wait_q, timeout) == 0 ) {
rv = thread-> events;
}
out:
SYS_PORT_TRACING_OBJ_FUNC_EXIT ( k_event, wait, event,
events, rv & events) ;
return rv & events;
}
uint32_t k_event_wait_all(struct k_event *event, uint32_t events, bool reset, k_timeout_t timeout)
k_event_wait_all 是 k_event_wait 的另一版本,两者差别在于等待条件的不同,前者是等待所有事件,后者等待任一事件。
uint32_t z_impl_k_event_wait_all ( struct k_event * event, uint32_t events,
bool reset, k_timeout_t timeout)
{
uint32_t options = reset ? ( K_EVENT_WAIT_RESET | K_EVENT_WAIT_ALL)
: K_EVENT_WAIT_ALL;
return k_event_wait_internal ( event, events, options, timeout) ;
}