zephyr学习笔记---单向链表slist

看了下zephyr所支持的开发板,有一个TI公司的,CC3200。低功耗wifi芯片,淘宝了一下,200出头,可以接受。当即买了一块,先弄TCP/IP再学6LowPan会好些。现在等开发板到货。

开发环境已经装好,ubuntu下开发,请参考大牛写的:
http://iot-fans.xyz/zephyr/doc/v1.6.0/getting_started/installation_linux.html
最好对照原文:
https://www.zephyrproject.org/doc/getting_started/installation_linux.html

开发板没到,先搞些基础工作,从简单的入手吧。先分析几个基础数据结构。首先就是单向链表slist。

slist.h文件存在于zephyr / include / misc文件夹下,全部使用内联函数,无相应的.c文件。slist为单向非循环链表,具备头指针和尾指针,下面说说它的常用函数:

static  inline  void sys_slist_init(sys_slist_t *list)
初始化链表,其实就是将头指针和尾指针置空。

static  inline  bool sys_slist_is_empty(sys_slist_t *list)
判断list是否为空链表,返回true为空,false为非空。

static  inline sys_snode_t *sys_slist_peek_head(sys_slist_t *list)
返回list的头节点。

static  inline sys_snode_t *sys_slist_peek_tail(sys_slist_t *list)
返回list的尾节点

static  inline sys_snode_t *sys_slist_peek_next_no_check(sys_snode_t *node)
返回node的下一个节点,注意,必须已确定node不为空。此函数是不检查node是否为空的,所以速度会快些

static  inline sys_snode_t *sys_slist_peek_next(sys_snode_t *node)
和上一个函数功能相同,在无法确定node是否为空的情况下使用。如果node为空,则返回NULL。

static  inline  void sys_slist_append(sys_slist_t *list,
                    sys_snode_t *node)
将节点node追加至list链表的尾部

static  inline  void sys_slist_prepend(sys_slist_t *list,
                     sys_snode_t *node)
将节点node添加至list链表的头部,成为头节点。链表有这样的功能估计是为将来成为栈或队列作准备的。

static  inline  void sys_slist_append_list(sys_slist_t *list,
         void *head,  void *tail)
{
     if (!list->tail)
    {
        list->head = (sys_snode_t *)head;
        list->tail = (sys_snode_t *)tail;
    }
     else
    {
        list->tail->next = (sys_snode_t *)head;
        list->tail = (sys_snode_t *)tail;
    }
}
参数list:源链表
参数head:要追加链表的头节点
参数tail:要追加链表的尾节点
函数功能:将头节点为head、尾节点为tail的链表追加到源链表list后面。

static  inline  void sys_slist_merge_slist(sys_slist_t *list,
        sys_slist_t *list_to_append)
{
    sys_slist_append_list(list, list_to_append->head,
                          list_to_append->tail);
    sys_slist_init(list);
}
将链表list和链表list_to_append合并,list_to_append追加至list尾部。添加完后,list被清空。
这里有些看不懂,故将源码放上。list被清空,新链表就找不到了,因为list_to_append指向的还是追加前的链表,追加后的链表再也找不到。除非原本另有指针指向list的头尾节点。个人感觉,被清空的应该是list_to_append。

static  inline  void sys_slist_insert(sys_slist_t *list,
                    sys_snode_t *prev,
                    sys_snode_t *node)
在链表list的prev节点后插入新节点node。

static  inline sys_snode_t *sys_slist_get_not_empty(sys_slist_t *list)
删除链表的头节点,必须确定list不为空链表才能使用此函数。

static  inline sys_snode_t *sys_slist_get(sys_slist_t *list)
跟上个函数一样,删除链表头节点,list可以为空链表。

static  inline  void sys_slist_remove(sys_slist_t *list,
                    sys_snode_t *prev_node,
                    sys_snode_t *node)
在链表list中删除节点node。必须给出node的上一个节点prev_node作为参数。如果不想给出上一个节点,请使用下面这个函数。

static  inline  void sys_slist_find_and_remove(sys_slist_t *list,
                         sys_snode_t *node)
在链表list中删除节点node。

下面是几个遍历用的宏:
#ifndef __SLIST_H__
#define __SLIST_H__

/**
 * @brief 遍历整张链表
 * Note: 循环不安全,因此__sn不能被,简而言之就是不能用于删除操作。
 *
 * 用户必须自行添加大括号以指定循环体
 *
 *     SYS_SLIST_FOR_EACH_NODE(l, n) {
 *         
 *     }
 *
 * @param __sl 指向sys_slist_t类型的指针,表示将进行迭代的链表
 * @param __sn sys_snode_t类型指针,用于遍历链表中的每个节点
 */

#define SYS_SLIST_FOR_EACH_NODE(__sl, __sn)             \
     for (__sn = sys_slist_peek_head(__sl); __sn;            \
         __sn = sys_slist_peek_next(__sn))

/**
 * @brief 从链表指定节点处遍历到结尾
 * Note: 循环不安全,因此__sn不能被删除,简而言之就是不能用于删除操作。
 *
 * 用户自行加大括号
 *
 *     SYS_SLIST_ITERATE_FROM_NODE(l, n) {
 *         
 *     }
 *
 * 和SYS_SLIST_FOR_EACH_NODE()一样, 但如__sn指定为链表中的一个节点,
 * 则遍历从__sn的下一个节点开始。如果__sn为空,则从头节点开始遍历。
 *
 * @param __sl 指向sys_slist_t类型的指针,表示将进行迭代的链
 * @param __sn sys_snode_t类型指针,指定的开始节点,为空则从头开始
 */

#define SYS_SLIST_ITERATE_FROM_NODE(__sl, __sn)             \
     for (__sn = __sn ? sys_slist_peek_next_no_check(__sn)       \
             : sys_slist_peek_head(__sl);           \
         __sn;                          \
         __sn = sys_slist_peek_next(__sn))

/**
 * @brief 安全地遍历整个链表
 * Note: __sn可被删除, 它不会打断循环.
 *
 * 用户必须自行添加大括号:
 *
 *     SYS_SLIST_FOR_EACH_NODE_SAFE(l, n, s) {
 *         
 *     }
 *
 * @param __sl sys_slist_t类型指针,指向被遍历链表
 * @param __sn sys_snode_t类型指针,依次等于链表中的每个节点
 * @param __sns sys_snode_t类型指针,用于安全遍历
 */

#define SYS_SLIST_FOR_EACH_NODE_SAFE(__sl, __sn, __sns)         \
     for (__sn = sys_slist_peek_head(__sl),              \
             __sns = sys_slist_peek_next(__sn);         \
         __sn; __sn = __sns,                    \
             __sns = sys_slist_peek_next(__sn))


上面两个遍历一般用不到,只是为下两个遍历提供服务
/**
 * @brief 提供根据容器建立的链表的遍历原语
 * Note: 此循环不安全,因此不能被删除
 *
 * 用户必须自行加大括号:
 *
 *     SYS_SLIST_FOR_EACH_CONTAINER(l, c, n) {
 *         
 *     }
 *
 * @param __sl sys_slist_t指针,将遍历此链表
 * @param __cn 用于遍历链表元素的临时指针变量,为容器类型
 * @param __n sys_node_t在容器结构体中的类型名称
 */

#define SYS_SLIST_FOR_EACH_CONTAINER(__sl, __cn, __n)           \
     for (__cn = SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n); __cn; \
         __cn = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n))

下面这个是安全遍历
/**
 * @brief 提供根据容器建立的链表的安全遍历原语
 * Note: __cn 可以被删除,不会打断循环.
 *
 * User 用户必须自行添加大括号:
 *
 *     SYS_SLIST_FOR_EACH_NODE_SAFE(l, c, cn, n) {
 *         
 *     }
 *
 * @param __sl  sys_slist_t类型指针,将遍历此链表
 * @param __cn   用于遍历链表元素的临时指针变量,为容器类型
 * @param __cns  用于安全遍历的临时变量,和__cn同类型
 * @param __n  sys_node_t在容器结构体中的类型名称
 */

#define SYS_SLIST_FOR_EACH_CONTAINER_SAFE(__sl, __cn, __cns, __n)   \
     for (__cn = SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n), \
         __cns = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n); __cn;    \
         __cn = __cns, __cns = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n))

读完代码写程序,这时才发现使用CCS进行TI-RTOS是一件多么幸福的事情,用VS开发C#那简直是在天堂了。开发环境没搭太好,qemu又不懂如何关闭,总之各种折磨。总算写完,看来得花时间找找有什么办法心善下开发环境。

先上代码:

#include 
#include 
#include 

static sys_slist_t list;
struct container_node
{
    sys_snode_t node;
    int id;
};

void PrintList(sys_slist_t *list) //依次打印所有节点
{
    struct container_node *container;
    printk("print list node:\n");
    SYS_SLIST_FOR_EACH_CONTAINER(list, container, node)
    {
        printk("node%d   ", container->id);
    }
    printk("\n\n");
}

void main(void)
{   
    struct container_node node1, node2, node3, node4, node5;
    node1.id = 1;
    node2.id = 2;
    node3.id = 3;
    node4.id = 4;
    node5.id = 5;
    sys_slist_init(&list);
    //将5个节点加入链表
    sys_slist_append(&list, &node1.node);
    sys_slist_append(&list, &node2.node);
    sys_slist_append(&list, &node3.node);
    sys_slist_append(&list, &node4.node);
    sys_slist_append(&list, &node5.node);
    PrintList(&list);
    
    printk("move node3 to head\n");
    sys_slist_find_and_remove(&list, &node3.node);//删除节点3
    sys_slist_prepend(&list, &node3.node);//将节点3变为头节点
    PrintList(&list);
    
    printk("switch node4 and node2\n");
    sys_slist_find_and_remove(&list, &node4.node);//删除节点4
    sys_slist_insert(&list, &node1.node, &node4.node);//将节点4加到节点1后面
    PrintList(&list);
}
先记下环境变量设置命令,方便以后回来对照:
export ZEPHYR_GCC_VARIANT=zephyr
export ZEPHYR_SDK_INSTALL_DIR=~/zephyr-sdk
cd zephyr-project/
source zephyr-env.sh
由于关闭不了qemu,每编译一次都要打一轮。之后再解决这个问题
经tidyjiang大神指点,已解决qemu关闭问题:先按Ctrl+a,再按x退出。不要忘记他的博客,上篇日志提到过: http://blog.csdn.net/tidyjiang/article/details/51622186)

然后通过以下命令在qemu上执行程序:
make BOARD=qemu_x86 qemu
所有命令及运行结果如下图所示:
zephyr学习笔记---单向链表slist_第1张图片

本例演示了slist的添加、删除、插入及遍历操作。要使用链表,必须在链表所存储元素所使用结构体中添加一个sys_snode_t类型成员,这个结构体被为容器(container)。这样使用比较麻烦,在这点上contiki中的链表比zephyr更先进、好用些。另外我始终认为链表如果进出操作太多,会产生大量零碎空间,应当慎用。C#中基本没见哪个地方使用链表,有使用的地方基本都用静态链表代替。



你可能感兴趣的:(zephyr)