vxworks源码剖析- 数据结构篇一

来源:互联网 发布:网络摄影机三星 编辑:程序博客网 时间:2024/05/20 18:17

http://commoncosmos.blog.163.com/blog/static/186295315201281185449565/


vxworks中使用了多种基本数据结构,例如双向链表,队列,树等等,本文将介绍这些基本数据结构在vxworks中的实现。

1. 双向链表

双向链表是最简单的数据结构,其实现也非常简单;而且,双向链表往往是实现其他数据结构的基础,因此本文最先介绍双向链表。双向链表的定义在dllLib.h文件中,函数实现在dllLib.c文件中。

在介绍双向链表之前,有必要先介绍一下vxworks中双向链表的实现样式:

 

List为一个指针,该指针链表结构体,包含两个域,分别指向链表头节点和尾节点:

typedef struct   
    {
    DL_NODE *head; 
    DL_NODE *tail; 
    } DL_LIST;

head和tail指针分别指向链表的头节点和尾节点,节点结构体包含两个域,分别为前一个节点和后一个节点,节点结构体的定义为:

typedef struct dlnode  
    {
    struct dlnode *next; 
    struct dlnode *previous; 
    } DL_NODE;

1.1 双向链表创建

在创建链表时,链表还没有任何子节点,因此此时的头节点和尾节点指针均为NULL;但是链表结构体需要创建,因此需要为链表结构体分配内存,创建链表代码如下:

DL_LIST *dllCreate (void)
    {
    FAST DL_LIST *pList = (DL_LIST *) malloc ((unsigned) sizeof (DL_LIST));

    dllInit (pList);

    return (pList);
    }

STATUS dllInit
    (
    FAST DL_LIST *pList     /* #define FAST register */
    )
    {
    pList->head  = NULL;
    pList->tail  = NULL;

    return (OK);
    }

此时创建的链表结构只有一个链表头,但是还没有任何节点:

1.2 添加节点

vxworks中的双向链表的节点都是添加在链表的尾部,通过的dllAdd函数实现,而dllAdd函数通过调用dllInsert函数来实现,dllInsert函数实现具体的添加过程;链表节点的添加主要通过一些指针的迁移来完成,在添加过程中需要注意中间节点的存储,并注意指针迁移顺序,最重要的是千万要保证链表不能断链,说了这么多,不如看代码实在:

void dllAdd
    (
    DL_LIST *pList,     /* pointer to list descriptor */
    DL_NODE *pNode      /* pointer to node to be added */
    )
    {
    dllInsert (pList, pList->tail, pNode);
    }

 

void dllInsert
    (
    FAST DL_LIST *pList,        /* pointer to list descriptor */
    FAST DL_NODE *pPrev,        /* pointer to node after which to insert */
    FAST DL_NODE *pNode         /* pointer to node to be inserted */
    )
    {
    FAST DL_NODE *pNext;

    if (pPrev == NULL)
 {    /* new node is to be first in list */
 pNext = pList->head;                                  
 pList->head = pNode;                                       /*1.设置链表结构体的头指针*/
 }
    else
 {    /* make prev node point fwd to new */
 pNext = pPrev->next;
 pPrev->next = pNode;                                     /*2.将节点添加到添加处节点的后面*/ 

 }

    if (pNext == NULL)
 pList->tail = pNode;                                    /*3.设置链表结构体的尾指针*/ 
    else
 pNext->previous = pNode;                          /*4.将节点添加到添加处节点的后面节点的前面*/


    /* set pointers in new node */

    pNode->next  = pNext;                                            /*5.设置节点的后向指针*/
    pNode->previous = pPrev;                                           /*6.设置节点的前向指针*/
    }

双向链表的添加,需要分三种情况:

1.2.1 链表中无节点

链表刚创建时就是类似的情况,此时链表结构体的头指针和尾指针均指向NULL;这种情况下添加节点主要是执行代码中的1、3、5、6语句,可以用下图表示:

1.2.2 链表中有1个节点

当链表中有1个节点时,链表结构体的头指针和尾指针均指向该节点,但是该节点的前向和后向指针则为NULL,此时添加节点主要执行代码中的2、3、5、6语句,具体操作可以用下图演示:

1.2.3 链表中节点个数大于1个

如果链表中的节点个数大于1个的时候,就可以在这两个节点中间插入节点了,不过采用dllAdd函数是无法实现的,因为这个函数每次都是在链表的最后一个节点处添加节点的,因此执行的语句仍然是2、3、5、6,和前面一种情况是一样的;要实现往两个节点中间插入节点,只能通过dllInsert函数来实现,这种方式插入节点不需要改变链表结构体的头指针和尾指针,只需要改变节点的前向和后向指针就可以了,执行的语句为2、4、5、6,可以用下图来演示:

1.2.4 在链表头部添加节点

当链表中没有节点时,在链表头部添加节点和第一种情况是完全一样的,执行1、3、5、6语句;如果链表中有节点时,需要改变链表头指针的位置,执行的语句为1、4、5、6,具体情况和上面大同小异:

1.3 删除节点

节点的删除相对于节点的添加来说,要相对容易一些,因为节点的删除只需要改变两个指针即可;不过和添加节点相同的是,删除节点也需要考虑四种情况,具体代码如下:

void dllRemove
    (
    DL_LIST *pList,             /* pointer to list descriptor */
    DL_NODE *pNode              /* pointer to node to be deleted */
    )
    {
    if (pNode->previous == NULL)
 pList->head = pNode->next;                                        /*1.改变链表的头指针*/
    else
 pNode->previous->next = pNode->next;                     /*2.改变待删除节点的前向节点的后向指针*/

    if (pNode->next == NULL)
 pList->tail = pNode->previous;                                   /*3.改变链表的尾指针*/
    else
 pNode->next->previous = pNode->previous;              /*4.改变待删除节点的后向节点的前向指针*/
    }

1.3.1 删除节点为链表中唯一的一个节点

此时链表中的节点既是链表的第一个节点,也是最后一个节点,因此,删除该节点后,链表中没有节点,链表的头指针和尾指针均为NULL;此种情况下,执行的语句为1、3,具体演示如下:

1.3.2 链表中节点数大于1个,删除节点为链表的第一个节点

删除节点为链表的第一个节点,链表的尾指针将不会发生变化,头指针指向待删除节点的下一个节点,而待删除节点的后向节点的前向指针将为NULL,执行语句为1、4,具体演示如下:

1.3.3 链表中节点数大于1个,删除节点为链表的最后一个节点

删除节点为链表的最后一个节点,链表的头指针不会发生变化,尾指针指向待删除节点的前向节点,而待删除节点的前向节点的后向指针将为NULL,执行语句为2、3,具体演示如下:

1.3.4 链表中节点数大于2个,删除节点为链表中间的一个节点

此种情况最简单,只需要改变待删除节点的前向节点的后向指针和后向节点的前向指针,将待删除节点的前向节点和后向节点连接起来即可,执行语句为2、4,具体演示如下:

1.4 获取首节点

获取首节点通过dllGet函数实现,主要完成两个功能:删除首节点,返回首节点;删除首节点和删除节点功能基本相似,主要是前面描述的1.3.1和1.3.2描述的情况,至于返回首节点就更加简单了:

DL_NODE *dllGet
    (
    FAST DL_LIST *pList         /* pointer to list from which to get node */
    )
    {
    FAST DL_NODE *pNode = pList->head;

    if (pNode != NULL)                      /* is list empty? */
 {
        pList->head = pNode->next;          /* make next node be 1st */

        if (pNode->next == NULL)            /* is there any next node? */
            pList->tail = NULL;             /*   no - list is empty */
        else
            pNode->next->previous = NULL;   /*   yes - make it 1st node */
 }

    return (pNode);
    }

1.5 获取节点数目

获取节点数目通过dllCount函数实现,并结合DLL_NEXT宏实现,DLL_NEXT定义如下:

#define DLL_NEXT(pNode)   \
    (     \
    (((DL_NODE *)(pNode))->next) \
    )
dllCount函数实现如下:

int dllCount
    (
    DL_LIST *pList      /* pointer to list descriptor */
    )
    {
    FAST DL_NODE *pNode = DLL_FIRST (pList);
    FAST int count = 0;

    while (pNode != NULL)
 {
 count++;
 pNode = DLL_NEXT (pNode);
 }

    return (count);
    }

1.6 遍历节点

节点遍历和返回节点数目函数类似,不再多述。

1.7 删除链表

删除链表和创建链表相对应,主要是释放内存:

STATUS dllDelete
    (
    DL_LIST *pList     /* pointer to list head to be initialized */
    )
    {
    free ((char *) pList);   /* free list */
    return OK;
    }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

:   基本数据结构 
typedef   struct   node /*   Node   of   a   linked   list.   */ 

struct   node   *next; /*   Points   at   the   next   node   in   the   list   */ 
struct   node   *previous; /*   Points   at   the   previous   node   in   the   list   */ 
}   NODE; 

typedef   struct /*   Header   for   a   linked   list.   */ 

NODE   node; /*   Header   list   node   */ 
int   count; /*   Number   of   nodes   in   list   */ 
}   LIST; 

typedef   struct   dlnode /*   Node   of   a   linked   list.   */ 

        struct   dlnode   *next; /*   Points   at   the   next   node   in   the   list   */ 
        struct   dlnode   *previous; /*   Points   at   the   previous   node   in   the   list   */ 
}   DL_NODE; 

typedef   struct /*   Header   for   a   linked   list.   */ 
        { 
        DL_NODE   *head; /*   header   of   list   */ 
        DL_NODE   *tail; /*   tail   of   list   */ 
}   DL_LIST; 

  
:   网络相关 
1. LIST   hostList     
整个系统的host,   hostAdd()产生.   hostShow()产看.其中的表项为:   
typedef   struct 
        { 
        NODE   node;   /*节点链*/ 

        HOSTNAME   hostName;/*名称*/ 

        struct   in_addr   netAddr;/*网络地址*/ 

        }   HOSTENTRY; 
char   targetName   [HOSTNAME_LEN]; /*   name   for   this   target   machine   */ 

2. LIST   endList 
整个系统已加载的end,   muxDevLoad()产生,endFindByName()查看.其中表项为
typedef   struct   end_tbl_row 
        { 
        NODE   node;                                     /*   Needed   by   our   list   processing   library.   */ 
        char   name[END_NAME_MAX]; /*   Name   of   the   devices   in   row.   (ln,   etc.)   */ 
        LIST   units;                                   /*   List   of   devices,   i.e.   0,   1,   etc..     */ 
        }   END_TBL_ROW; 
units链表中包含的是真正的END_OBJ. 

3. END_TBL_ENTRY   endDevTbl[] 
整个系统要加载的end,加载后便进入endList.其中表项为
typedef   struct   end_tbl_entry 
        { 
        int   unit;                                                                 /*   This   device 's   unit   #   */ 
        END_OBJ*   (*endLoadFunc)   (char*,   void*);                   /*   The   Load   function.   */ 
        char*   endLoadString;                                           /*   The   load   string.   */ 
        BOOL   endLoan;                                                         /*   Do   we   loan   buffers?   */ 
        void*   pBSP;                                                             /*   BSP   private   */ 
        BOOL   processed;                                                     /*   Has   this   been   processed?   */ 
        }   END_TBL_ENTRY; 

4. _netDpool(_pNetDpool)     _netSysPool(_pNetSysPool) 
系统网络缓冲区.netLibInit()-> netLibGeneralInit()-> mbinit()中初始化
_netDPool(_pNetSysPool)   
Only   used   for   data   transfer   in   the   network   stack. 
_netsyspool(_pNetDpool)   
Used   for   network   stack   system   structures   such   as   routes,   sockets,   protocol   control   blocks,   interface   addresses,   mulitcast   addresses,and   multicast   routing   entries. 

5. struct   ifnet   *ifnet; /*   list   of   all   network   interfaces   */ 
网络接口链表,   可以使用ifShow()通过遍历ifp-> if_next查看整个表.   if_attach(ifp)加入一个.   ipAttach()内部调用了if_attach(). 
IP层自己有一个IP_DRV_CTRL   ipDrvCtrl[IP_MAX_UNITS],   其中包含了struct   ifnet. 


最后一个成员是
struct ifqueue   if_snd; /*   output   queue   */ 

6. UDP和TCP都有in_pcb的链表,指向每个socketso_pcb. 

struct   inpcb   

LIST_ENTRY(inpcb)   inp_list; /*   list   for   all   PCBs   of   this   proto   */ 
LIST_ENTRY(inpcb)   inp_hash; /*   hash   list   */ 
struct inpcbinfo   *inp_pcbinfo; 

struct in_addr   inp_faddr; /*   foreign   host   table   entry   */ 
u_short inp_fport; /*   foreign   port   */ 

struct in_addr   inp_laddr; /*   local   host   table   entry   */ 
u_short inp_lport; /*   local   port   */ 

struct socket   *inp_socket; /*   back   pointer   to   socket   */ 
caddr_t inp_ppcb; /*   pointer   to   per-protocol   pcb   */ 
struct route   inp_route; /*   placeholder   for   routing   entry   */ 
int inp_flags; /*   generic   IP/datagram   flags   */ 
struct ip   inp_ip; /*   header   prototype;   should   have   more   */ 
struct mbuf   *inp_options; /*   IP   options   */ 
struct ip_moptions   *inp_moptions;   /*   IP   multicast   options   */ 
}; 
  

7. struct   protosw   inetsw   [IP_PROTO_NUM_MAX]     Pro tocol   switch   table,存放各种协议的函数

Each   protocol   has   a   handle   initializing   one   of   these   structures, 
which   is   used   for   protocol-protocol   and   system-protocol   communication. 
Protocols   pass   data   between   themselves   as   chains   of   mbufs   using   the   pr_input   and   pr_output   hooks. 

pr_input的调用 

ipintr() 

(*inetsw[ip_protox[ip-> ip_p]].pr_input)   (m,   hlen);   


struct   protosw 
    { 
        short pr_type; /*   socket   type   used   for   */ 
        struct domain   *pr_domain; /*   domain   protocol   a   member   of   */ 
        short pr_protocol; /*   protocol   number   */ 
        short pr_flags; /*   see   below   */ 

        void (*pr_input)   (); /*   input   to   protocol   (from   below)   */ 
        int (*pr_output)   (); /*   output   to   protocol   (from   above)   */ 
        void (*pr_ctlinput)   (); /*   control   input   (from   below)   */ 
        int (*pr_ctloutput)   (); /*   control   output   (from   above)   */ 
        int (*pr_usrreq)   (); /*   user   request:   see   list   below   */ 

        void (*pr_init)   (); /*   initialization   hook   */ 
        void (*pr_fasttimo)   (); /*   fast   timeout   (200ms)   */ 
        void (*pr_slowtimo)   (); /*   slow   timeout   (500ms)   */ 
        void (*pr_drain)   (); /*   flush   any   excess   space   possible   */ 
        int (*pr_sysctl)   (); /*   sysctl   for   protocol   */ 
}; 

ipLibInit(),icmpLibInit(),igmpLibInit(),udpLibInit(),tcpLibInit()等都会有一个protosw. 

pProtoSwitch 
-> pr_type =     (0,SOCK_RAW   ,   SOCK_RAW   ,   SOCK_DGRAM,   SOCK_STREAM) 
        -> pr_domain =     &inetdomain; 
        -> pr_protocol =     (IPPROTO_IP,   IPPROTO_ICMP,   IPPROTO_IGMP,IPPROTO_UDP,   IPPROTO_TCP) 
        -> pr_flags =     PR_ATOMIC   |   PR_ADDR; 
        -> pr_input =     (0,   icmp_input   ,   igmp_input   ,udp_input,tcp_input); 
        -> pr_output =     (ip_output   ,   rip_output,   rip_output,   0,0); 
        -> pr_ctlinput =     udp_ctlinput; 
        -> pr_ctloutput =     ip_ctloutput; 
        -> pr_usrreq =     udp_usrreq; 
        -> pr_init =     udp_init; 
_protoSwIndex++; 

8. struct   domain   *domains; /*   list   of   domain   descriptors   */ 
        由addDomain   (struct   domain   *   pDomain)添加一个新的域

系统预定义的一个域为
struct   domain   inetdomain   = 
          { 
    AF_INET,   "internet ",   0,   0,   0,   
                inetsw,   &inetsw[sizeof(inetsw)/sizeof(inetsw[0])],   0, 
                rn_inithead,   27,   sizeof(struct   sockaddr_in)   
}; 
9. RIP   ripState; 
整个系统的rip状态

10. struct   radix_node_head   *rt_tables[] 
整个系统的路由表.可以有很多种,rt_tables[AF_INET]. 

:   中断相关 
11. INTR_HANDLER intrVecTable[NUM_VEC_MAX] /*     Intr   vector   table   */ 
MPC860   core的外部中断表,   中断定位在0x500.intConnect()加入中断,ppc860IntrDeMux()相应中断并分解响应


:   设备相关 
12. DL_LIST   iosDvList 
所有已安装的device,   iosDevAdd()添加一个表项,   iosDelete()删除一个表项.表项为
typedef   struct   /*   DEV_HDR   -   device   header   for   all   device   structures   */ 

        DL_NODE node; /*   device   linked   list   node   */ 
        short drvNum; /*   driver   number   for   this   device,指向drvTable[]相应的位置   */ 

        char   * name; /*   device   name   */ 
}   DEV_HDR; 
  

13. DRV_ENTRY   drvTable[NUM_DRIVERS]   /*   max   20   drivers   in   drvTable   */ 
所有硬件驱动表.iosDrvInstall()添加表项.其中表项为
typedef   struct /*   DRV_ENTRY   -   entries   in   driver   jump   table   */ 
        { 
        FUNCPTR de_create; 
        FUNCPTR de_delete; 
        FUNCPTR de_open; 
        FUNCPTR de_close; 
        FUNCPTR de_read; 
        FUNCPTR de_write; 
        FUNCPTR de_ioctl; 
        BOOL de_inuse; 
        }   DRV_ENTRY; 
  
14. FD_ENTRY   fdTable[NUM_FILES]   /*   max   50   files   open   simultaneously   */ 
所有打开的文件描述符表.fopen()添加一个表项,fclose()删除一个表项.其中表项为
typedef   struct /*   FD_ENTRY   -   entries   in   file   table   */ 
        { 
        DEV_HDR   * pDevHdr; /*   device   header   for   this   file   */ 
        int   value; /*   driver 's   id   for   this   file   */ 
        char   * name; /*   actual   file   name   */ 
        BOOL   inuse; /*   active   entry   */ 
}   FD_ENTRY; 

:   其他 
15. 
#define   BOOT_LINE_OFFSET   0x4200 
#define   BOOT_LINE_ADRS ((char   *)   (LOCAL_MEM_LOCAL_ADRS+BOOT_LINE_OFFSET)) 
#define   DEFAULT_BOOT_LINE   "ide=0,0(0,0)host:/vxWorks.dat     f=0x8   tn=HC3600   o=cpm " 
启动时,如果用户没有输入boot参数,系统便把DEFAULT_BOOT_LINE拷贝到BOOT_LINE_ADRS   整个系统使用.