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

xiaoxiao2021-02-27  282

看了下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) {  *         <user code>  *     }  *  * @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) {  *         <user code>  *     }  *  * 和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) {  *         <user code>  *     }  *  * @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) {  *         <user code>  *     }  *  * @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) {  *         <user code>  *     }  *  * @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又不懂如何关闭,总之各种折磨。总算写完,看来得花时间找找有什么办法心善下开发环境。

先上代码:

[cpp]  view plain  copy   #include <zephyr.h>   #include <misc/printk.h>   #include <misc/slist.h>      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 所有命令及运行结果如下图所示:

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

http://blog.csdn.net/abatei/article/details/66970546
转载请注明原文地址: https://www.6miu.com/read-2314.html

最新回复(0)