概念
轮询API的主要函数是k_poll()
,它在概念上与POSIX函数非常相似,只是它对内核对象而不是文件描述符进行操作。
轮询API允许单个线程并发等待满足一个或多个条件,而无需主动单独查看每个条件。
目前支持的内核对象:
信号量
变为可用内核 FIFO
包含准备检索的数据- 发出
投票信号
想要等待多个条件的线程必须定义一个轮询事件数组,每个条件一个。
必须先初始化数组中的所有事件,然后才能轮询数组。
每个事件必须指定必须满足的条件类型,以便更改其状态以指示已满足请求的条件。
每个事件必须指定它希望满足条件的内核对象。
每个事件必须指定在满足条件时使用的操作模式。
每个事件都可以选择指定一个标记,以便将多个事件组合在一起,由用户自行决定。k_poll()
函数在满足它正在等待的条件之一后立即返回。当k_poll()
返回时,如果在调用k_poll()
之前满足了k_poll()
,或者由于内核的抢占多线程特性,则可能会满足多个。调用方必须查看数组中所有轮询事件的状态,以确定哪些事件已实现以及要执行的操作。
目前,只有一种操作模式可用:不获取对象。例如,当k_poll()
返回并且轮询事件声明信号量可用时,k_poll()
的调用方必须调用k_sem_take()
以获取信号量的所有权。如果信号量有争议,则无法保证在调用 k_sem_give()
时它仍然可用。
使用 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;
}
}
使用 k_poll_signal_raise
其中一种类型
的事件是K_POLL_TYPE_SIGNAL
:这是投票事件
的直接
信号。这可以看作是一个轻量级的二进制信号量
,只有一个线程可以等待。
轮询信号是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;
}
}
轮询接口Kconfig
Kconfig | 描述 |
---|---|
CONFIG_POLL | 启用轮询接口 |