轮询 API 用于并发等待满足多个条件中的任何一个。

概念

轮询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。

  1. struct k_poll_event events[2] = {
  2. K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_SEM_AVAILABLE,
  3. K_POLL_MODE_NOTIFY_ONLY,
  4. &my_sem, 0),
  5. K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_FIFO_DATA_AVAILABLE,
  6. K_POLL_MODE_NOTIFY_ONLY,
  7. &my_fifo, 0),
  8. };

或在运行时

  1. struct k_poll_event events[2];
  2. void some_init(void)
  3. {
  4. k_poll_event_init(&events[0],
  5. K_POLL_TYPE_SEM_AVAILABLE,
  6. K_POLL_MODE_NOTIFY_ONLY,
  7. &my_sem);
  8. k_poll_event_init(&events[1],
  9. K_POLL_TYPE_FIFO_DATA_AVAILABLE,
  10. K_POLL_MODE_NOTIFY_ONLY,
  11. &my_fifo);
  12. // tags are left uninitialized if unused
  13. }

初始化事件后,可以将数组传递给k_poll()。可以将超时指定为:

  • 仅等待指定的时间量
  • K_NO_WAIT不等待
  • K_FOREVER直到满足事件条件

每个信号量FIFO上都提供了轮询器列表,并且应用程序可以根据需要在其中等待尽可能多的事件。

请注意,服务员将按先到先得的顺序服务,而不是按优先顺序。

如果成功,k_poll()返回 0。如果它超时,它将返回 -EAGAIN

  1. // assume there is no contention on this semaphore and FIFO
  2. // -EADDRINUSE will not occur; the semaphore and/or data will be available
  3. void do_stuff(void)
  4. {
  5. rc = k_poll(events, 2, 1000);
  6. if (rc == 0) {
  7. if (events[0].state == K_POLL_STATE_SEM_AVAILABLE) {
  8. k_sem_take(events[0].sem, 0);
  9. } else if (events[1].state == K_POLL_STATE_FIFO_DATA_AVAILABLE) {
  10. data = k_fifo_get(events[1].fifo, 0);
  11. // handle data
  12. }
  13. } else {
  14. // handle timeout
  15. }
  16. }

在循环中调用k_poll()时,事件状态必须由用户重置为K_POLL_STATE_NOT_READY

  1. void do_stuff(void)
  2. {
  3. for(;;) {
  4. rc = k_poll(events, 2, K_FOREVER);
  5. if (events[0].state == K_POLL_STATE_SEM_AVAILABLE) {
  6. k_sem_take(events[0].sem, 0);
  7. } else if (events[1].state == K_POLL_STATE_FIFO_DATA_AVAILABLE) {
  8. data = k_fifo_get(events[1].fifo, 0);
  9. // handle data
  10. }
  11. events[0].state = K_POLL_STATE_NOT_READY;
  12. events[1].state = K_POLL_STATE_NOT_READY;
  13. }
  14. }

使用 k_poll_signal_raise

其中一种类型的事件是K_POLL_TYPE_SIGNAL:这是投票事件直接信号。这可以看作是一个轻量级的二进制信号量,只有一个线程可以等待。
轮询信号是k_poll_signal类型的单独对象,必须连接到k_poll_event,类似于信号量FIFO。必须首先通过K_POLL_SIGNAL_INITIALIZER()k_poll_signal_init()对其进行初始化。

  1. struct k_poll_signal signal;
  2. void do_stuff(void)
  3. {
  4. k_poll_signal_init(&signal);
  5. }

它通过k_poll_signal_raise()函数发出信号。此函数采用对API不透明的用户结果参数,可用于将额外信息传递给等待事件的线程。

  1. struct k_poll_signal signal;
  2. // thread A
  3. void do_stuff(void)
  4. {
  5. k_poll_signal_init(&signal);
  6. struct k_poll_event events[1] = {
  7. K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL,
  8. K_POLL_MODE_NOTIFY_ONLY,
  9. &signal),
  10. };
  11. k_poll(events, 1, K_FOREVER);
  12. if (events.signal->result == 0x1337) {
  13. // A-OK!
  14. } else {
  15. // weird error
  16. }
  17. }
  18. // thread B
  19. void signal_do_stuff(void)
  20. {
  21. k_poll_signal_raise(&signal, 0x1337);
  22. }

如果要在循环中轮询信号,则其事件状态和已发出信号的字段必须在每次迭代时重置。

  1. struct k_poll_signal signal;
  2. void do_stuff(void)
  3. {
  4. k_poll_signal_init(&signal);
  5. struct k_poll_event events[1] = {
  6. K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL,
  7. K_POLL_MODE_NOTIFY_ONLY,
  8. &signal),
  9. };
  10. for (;;) {
  11. k_poll(events, 1, K_FOREVER);
  12. if (events[0].signal->result == 0x1337) {
  13. // A-OK!
  14. } else {
  15. // weird error
  16. }
  17. events[0].signal->signaled = 0;
  18. events[0].state = K_POLL_STATE_NOT_READY;
  19. }
  20. }

轮询接口Kconfig

Kconfig 描述
CONFIG_POLL 启用轮询接口