
文章目录
- 简介
- 数据结构
-
- events 初始化
-
- Z_EVENT_INITIALIZER
- void k_event_init(struct k_event *event)
- 发布事件
-
- void k_event_post(struct k_event *event, uint32_t events)
- 设置事件
-
- void k_event_set(struct k_event *event, uint32_t events)
- 设置或清除事件
-
- void k_event_set_masked(struct k_event *event, uint32_t events, uint32_t events_mask)
- 清除事件
-
- k_event_clear(struct k_event *event, uint32_t events)
- 等待事件
-
- 等待选项
- uint32_t k_event_wait(struct k_event *event, uint32_t events, bool reset, k_timeout_t timeout)
- uint32_t k_event_wait_all(struct k_event *event, uint32_t events, bool reset, k_timeout_t timeout)
简介
- 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 \\}
- 该宏会将等待队列初始化为空
- 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);
}