zephyr k_queue

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);

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值