SylixOS网络协议栈数据收发流程

1. SylixOS网络协议栈基本介绍

    SylixOS网络协议栈使用目前非常流行的嵌入式TCP/IP协议栈lwiplwip是瑞典计算机科学院(SICS)Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。lwip特点是对RAMROM的占用非常少,只需十几KBRAM40K左右的ROM就可以运行,非常适合嵌入式系统使用。

    本文将会介绍基于dm9000网卡的数据包收发流程。

2. pbuf 结构介绍

        pbuf是lwip中用来表示数据包的结构体,数据包在协议栈各层的流动也是通过pbuf来实现的,基本结构如程序清单 2.1所示。

                                                                                  程序清单2.1

/* Main packet buffer struct */struct pbuf {2
    struct pbuf *next;            /* 下一个pbuf结构                      */
    void *payload;               /* 实际数据起始地址                     */
    u16_t tot_len;               /* pbuf链总长度                        */
    u16_t len;                  /* 当前pbuf长度                        */
    u8_t type_internal;            /* pbuf类型                          */
    u8_t flags;LWIP_PBUF_REF_T ref;    /* 初始化为1,pbuf->next指向自己时ref加1       */
    u8_t if_idx;void *if_out;
};

        pbuf的字段type_internal表示pbuf类型,lwip中有四种类型PBUF_RAM、PBUF_ROM、PBUF_REF和PBUF_POOL:

    1) PBUF_RAM类型的pbuf结构是一块很大的内存空间,内存首部是pbuf结构,实际数据接在后面,发送数据时常使用这种类型。如图 2.1所示;

图片.png

图 2.1 PBUF_RAM类型的pbuf结构

    2) PBUF_POOL类型是由多个pbuf结构组合而成,使用next字段将这些pbuf结构连接起来。其中tot_len表示此节点和next之后所有节点的长度和,len表示当前节点的数据长度。接收数据时常使用这类型的pbuf结构。如图 2.2所示;

图片.png

扫描二维码关注公众号,回复: 2369164 查看本文章

图 2.2 BUF_POOL类型的pbuf结构

    3) PBUF_REF与PBUF_ROM类似,pbuf结构是单独的一块内存空间,而data是另一块内存,二者并不相连,如图 2.3所示;

图片.png

图 2.3 BUF_REF和PBUF_ROM类型的pbuf结构

    Lwip协议栈使用pbuf结构在各层之间传递数据包,通过移动payload指针的方式在数据中添加报文头和剔除报文头,从而实现“零拷贝”机制,提高报文处理效率。

3. UDP 数据包发送流程

UDP数据包的发送是通过sendto()发起的(其他接口类似),整体实现流程如下:

    1) 通过文件描述符fd获取文件结构并提取lwipfd,再通过lwipfd从socket表中获取socket结构。Socket结构中包含了此udp链接中的connect信息;

    2) 使用netbuf_alloc()创建netbuf结构,这其中包含了pbuf结构。向这个结构导入需要发送的数据;

    3) Netbuf结构最终会传入udp_send()或udp_sendto(),这其中会通过ip_route()确定最终需要发送的网卡结构netif;

    4) Udp_sendto_if_src()添加udp包头;

    5) If_output_if_src()添加IP包头;

    6) 根据网卡结构netif获取发送接口netdev_netif_linkoutput(),最终调用网卡发送函数dm9000_transmit();

发送流程图如图 3.1所示;

图片.png

图 3.1 发送流程图

4. UDP 数据包接收流程

    数据包接收包括两个部分。首先网卡获取一个数据包并使用中断通知系统,系统解析这个数据包放入缓冲队列中。再由应用层调用接口recv()或recvfrom()获取这个数据包。

4.1 中断接收

    1) 系统在初始化时会注册网卡中断,处理函数为dm9000IntIsr()。当接收到一个数据包时会执行中断处理,中断处理内容很简短,仅添加一个接收处理函数dm9000_receive()到任务队列中,数据包主要在接受处理函数中被处理,减少了中断开销;

    2) 在dm9000_receive()中首先会创建一个POOL类型的pbuf结构,再调用驱动接口priv->inblk()从网卡中提取报文放入pbuf结构中,执行tcpip_input()处理这个数据包;

    3)通过消息队列发送数据到网络线程“t_netproto”,使用一个单独的线程处理此数据包;

    4) 进入线程后执行ip4_input()进行网络层处理,提取出运输层数据再通过udp_input()进行udp数据包处理;

    5) 根据ip与port找到对应的udp链接信息即udp_pcb结构,提取pcb中对应的接收处理函数pcb->recv()即recv_udp()和链接结构connect,recv_udp()中会将此数据包发送到此udp链接的消息队列中,等待用户层提取;

网卡中断接收流程如图 4.1所示;

图片.png

图 4.1 网卡中断接收流程

4.2 recvfrom()

    1) 过文件描述符fd获取文件结构,并提取lwipfd。再通过lwipfd从socket表中获取socket结构。Socket结构中包含了此udp链接中的connect信息;

    2) 根据connect信息获取对应UDP链接的接收消息队列,并通过调用netconn_recv_data()从消息队列中获取一条报文;

recvfrom()处理流程如图 4.2所示;

图片.png

图 4.2 recvfrom()处理流程


猜你喜欢

转载自blog.51cto.com/7199226/2150125
今日推荐