Zephyr poll

Posted 咕咚.萌西

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Zephyr poll相关的知识,希望对你有一定的参考价值。

文章目录

简介

  • 在Zephyr中,polling是一种等待事件的机制,它可以等待多个事件同时发生。
  • 在Zephyr中,可以使用k_poll()函数来等待事件。k_poll()函数接受一个k_poll_event数组,数组中的每个元素都描述了一个等待的事件。
  • 在等待期间,线程会被阻塞,直到所有事件都发生或等待超时。
  • 在等待结束后,k_poll()函数会返回一个整数,表示发生的事件数。
  • poll 中除了提供等待机制,还存在一种轻量级的同步对象 k_poll_signal,与队列,fifo,这些同步通信方式类型,但是它比较简单,只能向等待线程发送一个信号被触发的通知,再加上一个返回值,除此之外没有其他的功能。

struct k_poll_event

struct k_poll_event 
    /* _node 第一个成员,确保内存 k_poll_event 与双链表节点对齐,
     * 可以将 k_poll_event 插入到双链表中,当等待的事件还未发生时,
     * 调用 k_poll 的线程会将 k_poll_event 插入到目标对象中的 poll_events 双链表中,
     * 当目标对象发生指定事件时,IPC 对象会从 poll_events 取出节点并唤醒线程
     */
    sys_dnode_t _node;

    /* poller 不需要创建,它指向需要等待的线程中的 poller,
     * poller 代表的是线程的等待方式,调用 k_poll 函数时会自动设置对应的模式,
     * 同时将 k_poll_event 中的 poller 进行初始化(指向当前线程),
     * 除此之外 poller 还有一个额外的功能,此处 poller 记录的是线程中 poller 变量的地址,
     * 线程中 poller 变量相对于其 tcb 首地址的偏移是固定的,
     * 通过 poller 地址减去偏移可以获得对应线程 tcb
     */
    struct z_poller *poller;

    /* optional user-specified tag, opaque, untouched by the API */
    uint32_t tag:8;

    /* 等待的事件类型,此处类型需要与下方union中传入的指针类型相对应,
     * 否则会出现无法预料的错误
     */
    uint32_t type:_POLL_NUM_TYPES;

    /* 等待事件的状态,在调用轮询api之前应设置为 K_POLL_STATE_NOT_READY
     * 函数调用返回之后通过 state 的值判断对应的事件是否发生,
     * 如需再次使用需要重新初始化 state 为 K_POLL_STATE_NOT_READY
     */
    uint32_t state:_POLL_NUM_STATES;

    /* 轮询线程在可用时不获取对象的所有权 */
    uint32_t mode:1;

    /* unused bits in 32-bit word */
    uint32_t unused:_POLL_EVENT_NUM_UNUSED_BITS;

    /* 保存具体对象的地址,用于访问对象中的 poll_events 双链表 */
    union 
        void *obj;
        struct k_poll_signal *signal;
        struct k_sem *sem;
        struct k_fifo *fifo;
        struct k_queue *queue;
        struct k_msgq *msgq;
#ifdef CONFIG_PIPES
        struct k_pipe *pipe;
#endif
    ;
;

struct k_poll_event 功能

  • struct k_poll_event 可以当作一个双链表节点,里面除了双链表的pre和next指针外,还包含等待的信息,将线程与IPC对象之间联系起来。

  • 当事件未发生,线程需要挂起时可以访问 IPC 对象中的 poll_events 链表,将 event 放入链表。

static inline void register_event(struct k_poll_event *event,
				 struct z_poller *poller)

    switch (event->type) 
    case K_POLL_TYPE_SEM_AVAILABLE:
        __ASSERT(event->sem != NULL, "invalid semaphore\\n");
        add_event(&event->sem->poll_events, event, poller);
        break;

    /* ... */
    

    event->poller = poller;

  • 当线程调用 k_poll 进行等待时,线程可以获取 IPC 状态判断对应事件是否发生,以决定是否需要继续等待。
static inline bool is_condition_met(struct k_poll_event *event, uint32_t *state)

    switch (event->type) 
    case K_POLL_TYPE_SEM_AVAILABLE:
        if (k_sem_count_get(event->sem) > 0U) 
            *state = K_POLL_STATE_SEM_AVAILABLE;
            return true;
        
        break;

	/* ... */
    

    return false;

  • 当 IPC 对象中发生指定事件时,会从 poll_events 链表中取出 event 节点,设置 event 的状态,然后把等待线程唤醒。
void z_handle_obj_poll_events(sys_dlist_t *events, uint32_t state)

    struct k_poll_event *poll_event;

    poll_event = (struct k_poll_event *)sys_dlist_get(events);
    if (poll_event != NULL) 
        (void) signal_poll_event(poll_event, state);
	


static int signal_poll_event(struct k_poll_event *event, uint32_t state)

    struct z_poller *poller = event->poller;
    int retcode = 0;

    if (poller != NULL) 
        if (poller->mode == MODE_POLL) 
            /* 将等待的线程设置为就绪态 */
            retcode = signal_poller(event, state);
         else if (poller->mode == MODE_TRIGGERED) 
            retcode = signal_triggered_work(event, state);
         else 
            /* Poller is not poll or triggered mode. No action needed.*/
            ;
        

        poller->is_polling = false;

        if (retcode < 0) 
            return retcode;
        
    

    /* 设置state */
    set_event_ready(event, state);
    return retcode;


轮询类型

  • 下面是Zephyr中支持的等待类型:
#define K_POLL_TYPE_IGNORE                0
#define K_POLL_TYPE_SIGNAL                Z_POLL_TYPE_BIT(_POLL_TYPE_SIGNAL)              // 1
#define K_POLL_TYPE_SEM_AVAILABLE         Z_POLL_TYPE_BIT(_POLL_TYPE_SEM_AVAILABLE)       // 2
#define K_POLL_TYPE_DATA_AVAILABLE        Z_POLL_TYPE_BIT(_POLL_TYPE_DATA_AVAILABLE)      // 4
#define K_POLL_TYPE_FIFO_DATA_AVAILABLE   K_POLL_TYPE_DATA_AVAILABLE                      // 4
#define K_POLL_TYPE_MSGQ_DATA_AVAILABLE   Z_POLL_TYPE_BIT(_POLL_TYPE_MSGQ_DATA_AVAILABLE) // 8
#define K_POLL_TYPE_PIPE_DATA_AVAILABLE   Z_POLL_TYPE_BIT(_POLL_TYPE_PIPE_DATA_AVAILABLE) // 16
  • Zephyr内核支持等待信号,信号量,队列,FIFO,msgq,管道这几种类型的事件。
  • 一般的IPC通信方式虽然会提供等待机制,但是其只能针对一种事件进行等待,而不能等待多个事件(此处的事件特指 IPC 事件),例如我们无法在等待信号量的同时等待队列,或者同时等待多个队列,Zephyr中为我们提供了Polling API,它可以同时等待多个事件到来。

轮询状态

#define K_POLL_STATE_NOT_READY           0
#define K_POLL_STATE_SIGNALED            Z_POLL_STATE_BIT(_POLL_STATE_SIGNALED)           // 1
#define K_POLL_STATE_SEM_AVAILABLE       Z_POLL_STATE_BIT(_POLL_STATE_SEM_AVAILABLE)      // 2
#define K_POLL_STATE_DATA_AVAILABLE      Z_POLL_STATE_BIT(_POLL_STATE_DATA_AVAILABLE)     // 4
#define K_POLL_STATE_FIFO_DATA_AVAILABLE K_POLL_STATE_DATA_AVAILABLE                      // 4
#define K_POLL_STATE_MSGQ_DATA_AVAILABLE Z_POLL_STATE_BIT(_POLL_STATE_MSGQ_DATA_AVAILABLE)// 16
#define K_POLL_STATE_PIPE_DATA_AVAILABLE Z_POLL_STATE_BIT(_POLL_STATE_PIPE_DATA_AVAILABLE)// 32
#define K_POLL_STATE_CANCELLED           Z_POLL_STATE_BIT(_POLL_STATE_CANCELLED)          // 8
  • 在调用k_poll之前需将状态设置为 K_POLL_STATE_NOT_READY,避免调用返回后读取到错误的状态。
  • 每个状态与轮询的类型一一对应,除此之外,如果在等待过程中其他线程调用了取消等待的函数,被等待的线程会被唤醒并返回一错误码。

初始化

#define K_POLL_EVENT_INITIALIZER(_event_type, _event_mode, _event_obj) \\
	 \\
	.poller = NULL, \\
	.type = _event_type, \\
	.state = K_POLL_STATE_NOT_READY, \\
	.mode = _event_mode, \\
	.unused = 0, \\
	 \\
		.obj = _event_obj, \\
	, \\
	

#define K_POLL_EVENT_STATIC_INITIALIZER(_event_type, _event_mode, _event_obj, \\
					event_tag) \\
	 \\
	.tag = event_tag, \\
	.type = _event_type, \\
	.state = K_POLL_STATE_NOT_READY, \\
	.mode = _event_mode, \\
	.unused = 0, \\
	 \\
		.obj = _event_obj, \\
	, \\
	
  • 在Zephyr中提供了两个宏用于静态初始化。
    • _event_type 代表等待的事件类型。
    • _event_mode 固定为 K_POLL_MODE_NOTIFY_ONLY。
    • _event_obj 代表目标对象地址,可以是struct k_poll_signal、struct k_sem、struct k_fifo、struct k_queue、struct k_msgq、struct k_pipe类型的变量,传入的对象需与等待的类型一致。
void k_poll_event_init(struct k_poll_event *event, uint32_t type, int mode, void *obj);
  • 除了使用宏在定义变量时进行初始化之外,还可以使用 k_poll_event_init 进行初始化,它的功能和上面的两个宏功能一样,函数可以直接传入变量地址后即可初始化,而上面两个宏只能在变量定义时初始化,当执行完一次 k_poll 之后需要再次使用时,需要将 k_poll_event 中的 state 重设为 K_POLL_STATE_NOT_READY。
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);


事件等待

  • k_poll 在 poll 整个过程中最重要的函数之一,用于等待事件发生。
int z_impl_k_poll(struct k_poll_event *events, int num_events,
		  k_timeout_t timeout)

	int events_registered;
	k_spinlock_key_t key;
	struct z_poller *poller = &_current->poller;

	poller->is_polling = true;
	poller->mode = MODE_POLL;

	__ASSERT(!arch_is_in_isr(), "");
	__ASSERT(events != NULL, "NULL events\\n");
	__ASSERT(num_events >= 0, "<0 events\\n");

	SYS_PORT_TRACING_FUNC_ENTER(k_poll_api, poll, events);
	
	/* register_events 首先会轮询 events 中的IPC对象,等待的事件是否已经发生,如果已经发生会将 poller->is_polling 设为 false,
	 * 如果等待的事件没有发生则会将 event 添加到对应的 IPC 对象中的 poll_events 链表中, 等待事件来临并唤醒被挂起的线程
	 */
	events_registered = register_events(events, num_events, poller,
					    K_TIMEOUT_EQ(timeout, K_NO_WAIT));

	key = k_spin_lock(&lock);

	/* 如果poller->is_polling为false,说明已经有事件发生,就不需要再等待了 */
	if (!poller->is_polling) 
		/* 从queue、sem、msgq、pipe等中移除事件 */
		clear_event_registrations(events, events_registered, key);
		k_spin_unlock(&lock, key);

		SYS_PORT_TRACING_FUNC_EXIT(k_poll_api, poll, events, 0);

		return 0;
	

	poller->is_polling = false;

	/* 如果等待的事件没有发生,timeout等于K_NO_WAIT,就不需要等待了,返回-EAGAIN (超时)*/
	if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) 
		k_spin_unlock(&lock, key);

		SYS_PORT_TRACING_FUNC_EXIT(k_poll_api, poll, events, -EAGAIN);

		return -EAGAIN;
	

	static _wait_q_t wait_q = Z_WAIT_Q_INIT(&wait_q);

	// 如果等待的事件没有发生,timeout不等于K_NO_WAIT,就需要等待,调用z_pend_curr()等待
	int swap_rc = z_pend_curr(&lock, key, &wait_q, timeout);

	// 从queue、sem、msgq、pipe等中移除事件
	key = k_spin_lock(&lock);
	clear_event_registrations(events, events_registered, key);
	k_spin_unlock(&lock, key);

	SYS_PORT_TRACING_FUNC_EXIT(k_poll_api, poll, events, swap_rc);

	// swap_rc 为0,说明等待的事件发生了,否则等待超时或者等待被中断
	return swap_rc;


static inline int register_events(struct k_poll_event *events,
				  int num_events,
				  struct z_poller *poller,
				  bool just_check)

	int events_registered = 0;

	for (int ii = 0; ii < num_events; ii++) 
		k_spinlock_key_t key;
		uint32_t state;

		key = k_spin_lock(&lock);
		/* K_POLL_EVENT_INITIALIZER 会将 obj 进行初始化,而obj等价于queue、sem、msgq、pipe,
		   当queue、sem、msgq、pipe中不为空时,代表等待的事件已经发生,此时就会调用is_condition_met(),
		*/
		if (is_condition_met(&events[ii], &state)) 
			/* set_event_ready 会设置初始状态并将 k_poll_event 中的poller设置为NULL */
			set_event_ready(&events[ii], state);
			poller->is_polling = false;
		 
		/* 如果等待的事件没有发生,就会调用register_event(),将事件添加到queue、sem、msgq、pipe等中,
		   当等待的事件发生时,IPC 对象调用 z_handle_obj_poll_events(),将事件从queue、sem、msgq、pipe等中移除,
		   从而唤醒等待的线程
		*/
		else if (!just_check && poller->is_polling) 
			register_event(&events[ii], poller);
			events_registered += 1;
		 else 
			/* Event is not one of those identified in is_condition_met()
			 * catching non-polling events, or is marked for just check,
			 * or not marked for polling. No action needed.
			 */
			;
		
		k_spin_unlock(&lock, key);
	

	return events_registered;

事件处理

  • 在Zephyr中,IPC 对象发生指定事件时,会将 poll_events 链表中等待的线程进行唤醒,以 queue 为例子,向 queue 插入数据时,会发生 K_POLL_TYPE_DATA_AVAILABLE 类型的事件,而在 queue 的 poll_events 链表中,存放的都是等待该事件而被挂起的线程,当发生该事件时会调用 z_handle_obj_poll_events 从其中取出一个节点,唤醒对应线程。
void z_handle_obj_poll_events(sys_dlist_t *events, uint32_t state)

	struct k_poll_event *poll_event;

	/* 等待事件的线程会被放入到IPC对象的 poll_events 链表中,这里从链表中取出事件,
	 * 然后调用signal_poll_event()函数处理事件
	 */
	poll_event = (struct k_poll_event *)sys_dlist_get(events);
	if (poll_event != NULL) 
		(void) signal_poll_event(poll_event, state);
	

  • signal_poll_event 中会调用 signal_poller,最终将唤醒等待线程。
static int signal_poll_event(struct k_poll_event *event, uint32_t state)

	struct z_poller *poller = event->poller;
	int retcode = 0;


	/* k_poll_event 中的pooller可以根据mode的不同,进行不同处理 */
	if (poller != NULL) 
		if (poller->mode == MODE_POLL) 
			retcode = signal_poller(event, state);
		 else if (poller->mode == MODE_TRIGGERED) 
			retcode = signal_triggered_work(event, state);
		 else 
			/* Poller is not poll or triggered mode. No action needed.*/
			;
		
		
		poller->is_polling = false;

		if (retcode < 0) 
			return retcode;
		
	
	
	/* 修改对应事件的状态为就绪态 */
	set_event_ready(event, state);
	return retcode;


static int signal_poller(struct k_poll_event *event, uint32_t state)

	struct k_thread *thread = poller_thread(event->poller);

	__ASSERT(thread != NULL, "poller should have a thread\\n");

	// 如果线程不再等待,直接返回0
	if (!z_is_thread_pending(thread)) 
		return 0;
	

	// 如果线程处于等待超时状态,直接返回-EAGAIN
	if (z_is_thread_timeout_expired(thread)) 
		return -EAGAIN;
	

	// 将线程从等待队列中移除
	z_unpend_thread(thread);
	// 设置线程的返回值,如果state == K_POLL_STATE_CANCELLED,返回-EINTR,否则返回0
	arch_thread_return_value_set(thread,
		state == K_POLL_STATE_CANCELLED ? -EINTR : 0);

	if (!z_is_thread_ready(thread)) 
		return 0;
	

	// 将线程添加到就绪队列中
	z_ready_thread(thread);

	return 0;

struct k_poll_signal

  • 在上述轮询类型中其中一种是 K_POLL_TYPE_SIGNAL,是一个“直接”由信号通知的轮询事件,可以看作是一个轻量级的二进制信号量,只有一个线程可以等待。

初始化

  • 轮询信号是一个类型为 struct k_poll_signal 的单独对象,类似于信号量或 FIFO,必须附加到 k_poll_event 上,它必须首先通过 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 发出信号,这个函数可以携带一个 result 参数,可以用来向等待的线程传递额外的信息,该参数对于API而言是不透明的,如果是在一个循环中使用k_poll,在每次使用之后应将 k_poll_signal 中的 signaled 设置为0, 同时将 k_poll_event 中的 state 重设为 K_POLL_STATE_NOT_READY。
struct k_poll_signal signal;

void thread_a(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 == 0xffffffff) 
		
			// A-OK!
		 
		else 
		
            // weird error
        

        events[0].signal->signaled = 0;
        events[0].state 

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_WAITK_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 poll的主要内容,如果未能解决你的问题,请参考以下文章

物联网工程,往嵌入式方向走。求大神给个学习路线

Zephyr RTOS -- Polling API

Zephyr RTOS -- Polling API

终于!Linaro 加盟 Zephyr 项目

STM32+ESP8266(ESP-12F)物联网温度计-腾讯云物联网

STM32+ESP8266(ESP-12F)物联网温度计-腾讯云物联网