zephyr提供的数据数据传递对象之一便是,k_fifo、k_lifo。以下以FIFO为例,简单分析其源码。
zephyr版本:2.5.99
k_fifo的定义,在文件/zephyr/include/kernel.h中
/* k_fifo的定义,仅仅是对k_queue的再次封装*/
struct k_fifo {
struct k_queue _queue;
};
/* k_queue定义*/
struct k_queue {
sys_sflist_t data_q; /*带flag操作的单向链表*/
struct k_spinlock lock; /* 同步用自旋锁*/
_wait_q_t wait_q; /* 记录队列的等待线程*/
_POLL_EVENT;
_OBJECT_TRACING_NEXT_PTR(k_queue)
_OBJECT_TRACING_LINKED_FLAG
};
/* 链表相关定义*/
struct _sfnode {
/* 节点是WORD对齐的,因此可以忽略低2位,将其用于flag使用*/
unative_t next_and_flags;
};
typedef struct _sfnode sys_sfnode_t;
/* 链表结构包含两个节点,一个首节点、一个尾节点*/
struct _sflist {
sys_sfnode_t *head;
sys_sfnode_t *tail;
};
typedef struct _sflist sys_sflist_t; /*链表结构定义*/
k_fifo提供的API与k_queue的API基本一致,包括:初始化、put、get等操作。应用(application)可以通过系统调用来操作相应的内核对象。
#define k_fifo_init(fifo) \
k_queue_init(&(fifo)->_queue)
#define k_fifo_cancel_wait(fifo) \
k_queue_cancel_wait(&(fifo)->_queue)
#define k_fifo_put(fifo, data) \
k_queue_append(&(fifo)->_queue, data)
#define k_fifo_alloc_put(fifo, data) \
k_queue_alloc_append(&(fifo)->_queue, data)
#define k_fifo_put_list(fifo, head, tail) \
k_queue_append_list(&(fifo)->_queue, head, tail)
#define k_fifo_put_slist(fifo, list) \
k_queue_merge_slist(&(fifo)->_queue, list)
#define k_fifo_get(fifo, timeout) \
k_queue_get(&(fifo)->_queue, timeout)
#define k_fifo_is_empty(fifo) \
k_queue_is_empty(&(fifo)->_queue)
#define k_fifo_peek_head(fifo) \
k_queue_peek_head(&(fifo)->_queue)
#define k_fifo_peek_tail(fifo) \
k_queue_peek_tail(&(fifo)->_queue)
#define K_FIFO_DEFINE(name) \
Z_STRUCT_SECTION_ITERABLE_ALTERNATE(k_queue, k_fifo, name) = \
Z_FIFO_INITIALIZER(name)
上述系统调用的实现,在/zephyr/kernel/queue.c中。
初始化接口:
/*初始化的实现主要是对成员的初始化*/
void z_impl_k_queue_init(struct k_queue *queue)
{
sys_sflist_init(&queue->data_q);
queue->lock = (struct k_spinlock) {};
z_waitq_init(&queue->wait_q);
#if defined(CONFIG_POLL)
sys_dlist_init(&queue->poll_events);
#endif
SYS_TRACING_OBJ_INIT(k_queue, queue);
z_object_init(queue);
}
put(append)接口的实现
void k_queue_append(struct k_queue *queue, void *data)
{
(void)queue_insert(queue, NULL, data, false, true);
}
static int32_t queue_insert(struct k_queue *queue, void *prev, void *data,
bool alloc, bool is_append)
{
struct k_thread *first_pending_thread;
k_spinlock_key_t key = k_spin_lock(&queue->lock);
if (is_append) {
/* 尾部扩展一个节点,需首先获取尾结点信息*/
prev = sys_sflist_peek_tail(&queue->data_q);
}
/* 无等待线程时,才需要把数据插入到队列中*/
first_pending_thread = z_unpend_first_thread(&queue->wait_q);
if (first_pending_thread != NULL) {
prepare_thread_to_run(first_pending_thread, data);
z_reschedule(&queue->lock, key);
return 0;
}
/* Only need to actually allocate if no threads are pending */
if (alloc) {
/* 当数据中未包含节点信息时,需要重新对数据进行封装,节点所需内存,从线程
的资源池中获得,在信息被get后,资源会自动释放。*/
struct alloc_node *anode;
anode = z_thread_malloc(sizeof(*anode));
if (anode == NULL) {
k_spin_unlock(&queue->lock, key);
return -ENOMEM;
}
anode->data = data;
/* 由于是采用malloc方式生成的节点,因此标志位置1*/
sys_sfnode_init(&anode->node, 0x1);
data = anode;
} else {
/* 数据中包含节点信息,标志位置0*/
sys_sfnode_init(data, 0x0);
}
sys_sflist_insert(&queue->data_q, prev, data); /* 插入节点*/
handle_poll_events(queue, K_POLL_STATE_DATA_AVAILABLE); /*发送PULL事件*/
z_reschedule(&queue->lock, key); /*触发调度*/
return 0;
}
get接口的实现
void *z_queue_node_peek(sys_sfnode_t *node, bool needs_free)
{
void *ret;
/* 入参检查以及节点标志检查*/
if ((node != NULL) && (sys_sfnode_flags_get(node) != (uint8_t)0)) {
/* If the flag is set, then the enqueue operation for this item
* did a behind-the scenes memory allocation of an alloc_node
* struct, which is what got put in the queue. Free it and pass
* back the data pointer.
*/
struct alloc_node *anode;
/*如果是采用alloc_append方式的节点,则使用CONTAINER_OF方式获取节点地址,从而获取数据*/
anode = CONTAINER_OF(node, struct alloc_node, node);
ret = anode->data;
if (needs_free) {
k_free(anode);
}
} else {
/* Data was directly placed in the queue, the first word
* reserved for the linked list. User mode isn't allowed to
* do this, although it can get data sent this way.
*/
/*如果put时的data数据中包含有节点信息,则直接返回。因为节点信息保存在data的首地址*/
ret = (void *)node;
}
return ret;
}
void *z_impl_k_queue_get(struct k_queue *queue, k_timeout_t timeout)
{
k_spinlock_key_t key = k_spin_lock(&queue->lock);
void *data;
if (likely(!sys_sflist_is_empty(&queue->data_q))) {
/*链表节点非空处理*/
sys_sfnode_t *node;
node = sys_sflist_get_not_empty(&queue->data_q); /*获取非空节点*/
data = z_queue_node_peek(node, true); /* 由节点获取数据*/
k_spin_unlock(&queue->lock, key); /*解锁*/
return data;
}
/* 当链表为空时,说明队列中已无有效数据,判断线程是否愿意等待*/
if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
k_spin_unlock(&queue->lock, key);
return NULL;
}
int ret = z_pend_curr(&queue->lock, key, &queue->wait_q, timeout);
return (ret != 0) ? NULL : _current->base.swap_data;
}
k_fifo的应用示例
struct data_element
{
void *fifo_reserved; /*保留,用于queue中data_q链表的节点*/
void *data;
... /* 存放有效数据的成员*/
}
struct data_element * fifo_tx_data1;
char fifo_tx_data2[32] = {0};
struct k_queue my_queue
void producer_thread(...)
{
....
/* use append to input data*/
fifo_tx_data1 = maoolc(sizeof(struct data_element);
fifo_tx_data1->data = ... /*为data成员装载数据*/
k_queue_append(&my_queue, fifo_tx_data1)
/* use alloc_append to input data*/
k_queue_alloc_append(&my_queue, data2);
....
}
void consumer_thread(...)
{
void *rx_data1,rx_data2;
rx_data1 = (struct data_element*)k_queue_get(&my_queue, K_NO_WAIT);
display(rx_data1->data);
rx_data2 = k_queue_get(&my_queue, K_NO_WAIT);
display(rx_data2);
}