Zephyr RTOS -- Polling API
Posted 搬砖-工人
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Zephyr RTOS -- Polling API相关的知识,希望对你有一定的参考价值。
文章目录
本笔记基于 Zephyr 版本 2.6.0-rc2
前言
本人正在学习 Zephyr,一个可移植性较强,可以兼容多种开发板及物联网设备的操作系统,如果你感兴趣,可以点此查看我的 学习笔记总述 进行了解!
Polling API - (轮询 API)
轮询 API 用于并发地等待多个条件中的任何一个被满足。
1. Concepts - (概念)
轮询 API 的主要函数是 k_poll()
,它在概念上与 POSIX poll()
函数非常相似,不同之处在于它是对内核对象操作,而不是文件描述符进行操作。
轮询 API 允许单个线程并发地等待一个或多个条件被满足,而不需要单独地主动查看每个条件。
这里有一组有限的条件:
- 信号量可用
- 内核 FIFO 包含准备检索的数据
- 一个轮询信号被发出
想要等待多个条件的线程必须定义一个 轮询事件(poll events) 数组, 每个条件一个。
必须先初始化数组中的所有事件,然后才能轮询数组。
每个事件必须指定必须满足哪种 类型(type) 的条件,以便将其状态更改为表示已满足所请求的条件。
每个事件必须指定它希望满足条件的 内核对象(kernel object)。
每个事件必须指定在满足条件时使用哪种操作 模式(mode)。
每个事件可以选择指定一个 标记(tag) 来将多个事件组合在一起,这是由用户决定的。
除了内核对象之外,还有一个 轮询信号(poll signal) 伪对象类型可以直接发出信号。
k_poll()
函数一旦满足它所等待的条件之一就返回。如果在调用 k_poll()
之前完成,或者由于内核的优先多线程特性,在 k_poll()
返回时可能会有多个完成。调用者必须查看数组中所有轮询事件的状态,以确定完成了哪些事件以及采取哪些操作。
目前,只有一种操作模式可用:不获取对象。例如,这意味着当 k_poll()
返回并且 poll 事件表明信号量可用时, k_poll()
的调用者必须调用 k_sem_take()
以获取信号量的所有权。如果信号量被争用,则不能保证它在 k_sem_give()
被调用时仍然可用。
2. Implementation - (实现)
2.1 Using k_poll()
主要 API 是 k_poll()
,它对类型为 k_poll_event
的轮询事件数组进行操作 。数组中的每个条目都代表一个事件,调用 k_poll()
将等待其条件得到满足。
它们可以使用运行时初始化器 K_POLL_EVENT_INITIALIZER()
或 k_poll_event_init()
或静态初始化器 K_POLL_EVENT_STATIC_INITIALIZER()
进行初始化。必须将与指定 类型 匹配的对象传递给初始值设定项。该 模式 必须设置为 K_POLL_MODE_NOTIFY_ONLY
。状态必须设置为 K_POLL_STATE_NOT_READY
(由初始化者负责)。
用户 标记 是可选的,并且对 API 完全不透明:它的存在是为了帮助用户将类似的事件分组在一起。由于是可选的,它被传递给静态初始化器,而不是运行时初始化器,这是出于性能原因。如果使用运行时初始化器,用户必须在k_poll_event
数据结构中单独设置它。如果要忽略数组中的事件(很可能是暂时的),可以将其类型设置为 K_POLL_TYPE_IGNORE
。
struct k_poll_event events[2] = {
K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_SEM_AVAILABLE,
K_POLL_MODE_NOTIFY_ONLY,
&my_sem, 0),
K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_FIFO_DATA_AVAILABLE,
K_POLL_MODE_NOTIFY_ONLY,
&my_fifo, 0),
};
或在运行时
struct k_poll_event events[2];
void some_init(void)
{
k_poll_event_init(&events[0],
K_POLL_TYPE_SEM_AVAILABLE,
K_POLL_MODE_NOTIFY_ONLY,
&my_sem);
k_poll_event_init(&events[1],
K_POLL_TYPE_FIFO_DATA_AVAILABLE,
K_POLL_MODE_NOTIFY_ONLY,
&my_fifo);
// tags are left uninitialized if unused
}
初始化事件后,可以将数组传递给 k_poll()
。可以将超时指定为只等待指定数量的时间,或者将特殊值 K_NO_WAIT
和 K_FOREVER
指定为不等待或等待事件条件得到满足。
每个信号量或 FIFO 上都提供了一个轮询器列表,并且可以根据应用程序的需要在其中等待尽可能多的事件。请注意,等待者将按照 先到先得的顺序 服务,而不是按优先顺序。
如果成功,则 k_poll()
返回 0。如果超时,则返回 -EAGAIN
。
// assume there is no contention on this semaphore and FIFO
// -EADDRINUSE will not occur; the semaphore and/or data will be available
void do_stuff(void)
{
rc = k_poll(events, 2, 1000);
if (rc == 0) {
if (events[0].state == K_POLL_STATE_SEM_AVAILABLE) {
k_sem_take(events[0].sem, 0);
} else if (events[1].state == K_POLL_STATE_FIFO_DATA_AVAILABLE) {
data = k_fifo_get(events[1].fifo, 0);
// handle data
}
} else {
// handle timeout
}
}
当 k_poll()
在循环中调用时,事件状态必须由用户重置为 K_POLL_STATE_NOT_READY
。
void do_stuff(void)
{
for(;;) {
rc = k_poll(events, 2, K_FOREVER);
if (events[0].state == K_POLL_STATE_SEM_AVAILABLE) {
k_sem_take(events[0].sem, 0);
} else if (events[1].state == K_POLL_STATE_FIFO_DATA_AVAILABLE) {
data = k_fifo_get(events[1].fifo, 0);
// handle data
}
events[0].state = K_POLL_STATE_NOT_READY;
events[1].state = K_POLL_STATE_NOT_READY;
}
}
2.2 Using k_poll_signal_raise()
事件类型之一是 K_POLL_TYPE_SIGNAL
:这是轮询事件的 “direct” 信号。这可以看作是一个只有一个线程可以等待的轻量级二进制信号量。
轮询信号是 k_poll_signal
类型的单独对象,必须附加到 k_poll_event,类似于信号量或 FIFO。它必须首先通过 K_POLL_SIGNAL_INITIALIZER()
或 k_poll_signal_init()
进行初始化。
struct k_poll_signal signal;
void do_stuff(void)
{
k_poll_signal_init(&signal);
}
它是通过 k_poll_signal_raise()
函数来通知的。该函数接受一个用户结果参数,该参数对 API 是不透明的,可用于将额外的信息传递给等待事件的线程。
struct k_poll_signal signal;
// thread A
void do_stuff(void)
{
k_poll_signal_init(&signal);
struct k_poll_event events[1] = {
K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL,
K_POLL_MODE_NOTIFY_ONLY,
&signal),
};
k_poll(events, 1, K_FOREVER);
if (events.signal->result == 0x1337) {
// A-OK!
} else {
// weird error
}
}
// thread B
void signal_do_stuff(void)
{
k_poll_signal_raise(&signal, 0x1337);
}
如果信号要在循环中轮询,如果信号已经被轮询,则必须在每次迭代中重置它的事件状态和它的有信号字段。
struct k_poll_signal signal;
void do_stuff(void)
{
k_poll_signal_init(&signal);
struct k_poll_event events[1] = {
K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL,
K_POLL_MODE_NOTIFY_ONLY,
&signal),
};
for (;;) {
k_poll(events, 1, K_FOREVER);
if (events[0].signal->result == 0x1337) {
// A-OK!
} else {
// weird error
}
events[0].signal->signaled = 0;
events[0].state = K_POLL_STATE_NOT_READY;
}
}
3. Suggested Uses - (建议用途)
使用 k_poll()
合并将在一个对象上暂挂的多个线程,从而节省大量堆栈空间。
因为对象只有在没有其他线程等待它们可用时才会发出信号,而且只有一个线程可以轮询特定的对象,所以轮询最好是在对象不是多个线程争用,基本上,当单个线程作为多个对象的主“服务器”或“分派器”运行,并且是唯一试图获取这些对象的线程时的情况下使用。
4. Configuration Options - (配置选项)
相关配置选项:
参考链接
https://docs.zephyrproject.org/latest/reference/kernel/other/polling.html#
以上是关于Zephyr RTOS -- Polling API的主要内容,如果未能解决你的问题,请参考以下文章