概念
轮询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 availablevoid 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 Avoid 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 Bvoid 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 | 启用轮询接口 |
