LWIP协议栈架构与设计解析,lwip网络协议栈解析
本作品内容为LWIP协议栈架构与设计解析,格式为 doc ,大小 219688 KB ,页数为 23页
('可编辑可修改LWIP协议栈及接口提取Version1.02012/06/20精选模板可编辑可修改版本1.0姓名部门邮件作者雷岩Leiyan3521@163.com审阅精选模板可编辑可修改版本历史版本日期修订姓名Rev1.02011/11/07FirstDraftleiyan精选模板可编辑可修改目录一、LWIP介绍...............................................................................................5二、LWIP源码分析........................................................................................71.LWIP协议栈的架构..................................................................................72.各个文件夹介绍..............................................................................................83.模块及源文件介绍.........................................................................................10三、LWIP协议栈处理数据流程.......................................................................17四、接口提取..............................................................................................18精选模板可编辑可修改一、LWIP介绍首先说明一下,这篇文档的主要目的是提取网络发送和接收数据的函数接口。然后用我们自己的驱动网卡的接口函数替代程序中的接口。如果对LWIP协议栈本身没什么兴趣的,可以跳过第一、二、三章,直接阅读第四章,使用我们的接口代替第四章的接口就行了。写第一、二、三章的主要目的是为了方便理解数据的发送和接收在LWIP协议栈中是如何进行处理的。这便于我们理解提取出来的接口。LWIP是瑞典计算机科学院开发的一套用于嵌入式系统的开放源代码的轻量级的TCP/IP协议栈。传统的,或者说是典型的TCP/IP协议族的设计都是按照分层的思想来设计的。这样设计有个好处,就是每层相对于其他层独立,代码方便理解。缺点就是,每层之间进行数据交互的时候必须要进行复制,而数据的复制是很耗时的,这就降低了实时性。LWIP采用了一种不同的设计方式来实现TCP/IP协议族。LWIP各层之间没有明显的界限,各层之间都可以访问到共享在内存中的数据。因为各层都可以访问共享内存,所以这就避免了内存复制产生的性能损失。但是并不是说LWIP就没有分层的概念了。只不过LWIP各层都是逻辑意义上的层。每个协议都以模块的形式被实现。而这些模块就共同组成了LWIP整体。下面一章将分析LWIP的源码,结合源码介绍这些模块。了解各个协议是怎么通过模块被实现的。其中这里最主要的是TCP协议模块的实现。TCP协议在LWIP协议栈中占得比例最大,有将近一半的代码是专门用来实现TCP协议的。所以重点会分析TCP协议。并且无线音频项目采用的也是TCP协议传输数据。LWIP逻辑上被分为四个层:应用层,传输层,网络层和网络接口层。如下图1.1所示:精选模板可编辑可修改图1.1LWIP协议栈的分层模型应用层主要是使用LWIP协议栈开发相应的网络通信程序。LWIP主要提供了三种接口供用户使用。三种接口分别为RAWAPI,NetconnAPI和BSDSocketAP。其中RAWAPI主要是采用回调函数的方式来完成数据的发送和接收,RAWAPI接口写的应用程序与LWIP协议栈处在同一个进程(或者称任务)中。NetconnAPI和BSDSocketAP工作于多线程方式中,要使用者两种接口,必须有多任务的操作系统的支持。这次项目采用的接口是RAWAPI。关于RAWAPI接口如何写应用层程序,另写了一个专门的文档介绍。这里不列出。传输层。我们最熟悉的TCP协议,UDP协议,以及我们使用ping命令时采用的ICMP协议都处在这一层。这一层提供了一些专门的接口,用于处理与应用层和网络层的数据传送(注明,这里说的数据传送并不是指数据的拷贝。事实上,LWIP协议栈使用的是内存共享技术,各层都能访问这段共享内存,各层传递的就是数据结构指针,所以LWIP协议栈降低了内存复制所产生的性能损失)。关于这层函数,会在下面进行具体说明。网络层主要的协议是IP协议。这一层主要是对底层接收的数据包进行发送,转发,丢弃组合等功能。网络接口层是和底层硬件驱动交互的层。我们所需要提取的网络接口就在这一层。精选模板可编辑可修改二、LWIP源码分析1.LWIP协议栈的架构LWIP协议栈源码的架构如下图2.1所示:图2.1LWIP协议栈源码架构api目录:应用程序接口文件,包括netconn和BSD2种API。这个文件夹主要是为了方便应用程序编写而为应用层提供的API接口。core目录:ICMP,IP,TCP,UDP协议的实现文件,以及一些辅助函数,LWIP实现的核心代码。其中这个文件夹里面实现TCP协议的代码量几乎占了整个lwip协议栈的一半。重点会讨论TCP协议。在此也可以简单看出LWIP协议栈没有严格区分传输层和网络层,因为传输层的代码和网络层的代码放在一个文件夹里实现了。目录里还提供了RAWAPI接口的实现。RAWAPI接口和上面两种接口一样,都是为了方便应用程序编写而为应用层提供的API接口。include目录:主要是LWIP协议栈使用的自定义的一些头文件。netif目录:这个目录里主要实现的是ARP协议。当然还有一些PPPOE等协议。当然PPPOE协议不是我们所关心的。port目录:最后写这个目录,主要是因为这个目录是我们最终所要修改的一个目录。可以精选模板可编辑可修改看到此目录下包含一个ethernetif.c文件。这个文件提供了与底层网络驱动的一个接口。我们要实现与自己的网络接口驱动交互,必须修改这个文件里的接口,使这些接口为上层屏蔽细节信息。精选模板可编辑可修改2.各个文件夹介绍【1】api目录,如图2.2图2.2api目录下的文件列表这个文件夹下面主要提供的是netconnAPI和BSDsocketAPI。这些接口不是我们所要关心的。所以直接忽略。我们使用下面文件夹里提供的RAWAPI接口。【2】core目录,如图2.3图2.3core目录下的文件列表这个文件下下面,ipv6文件夹和snmp文件夹我们不需要关心。主要关心的文件有精选模板可编辑可修改icmp.cIp.cip_addr.cip_frag.cdhcp.cinit.cmem.cmemp.cnetif.cpbuf.c精选模板可编辑可修改Raw.ctcp.ctcp_in.ctcp_out.cudp.c【3】include目录,如图2.4图2.4include目录下的文件列表这个文件夹下面主要包括所有.c文件用到的头文件。Ipv6文件夹我们不必关心。【4】netif目录,如图2.5图2.5netif目录下的文件列表这个文件夹下面主要关心etharp.c这个文件其余的不必关心。其中文件夹PPP是关于点对点协议的,不必关心【5】port目录,如图2.6精选模板可编辑可修改图2.6port目录下的文件列表这个文件夹下面关于arch目录,这个目录是移植时候主要修改的目录。这个目录下包含的是与体系结构相关的一些定义等。FreeRTOS这个文件夹不需要关心,因为这是基于实时操作系统的,而我们现在做的项目是基于单任务的,无需操作系统的支持。Standalone这个文件夹下面的ethernetif.c中给出的驱动接口。我们为自己的网络接口设计的驱动程最终用来代替这个文件里的接口。上面所有文件大致描述了LWIP协议栈的几个模块:配置模块、初始化模块、NetIf模块、Mem(memp)模块、netarp模块、ip模块、udp模块、icmp模块、igmp模块和dhcp模块。由于篇幅限制,不能对每个源文件都做介绍。只对我们感兴趣的模块的源文件做相应的介绍。3.模块及源文件介绍【1】配置模块配置模块的文件主要包含在include/lwip/opt.h里(这里及下文所指的路径均为相对路径)。配置模块通过各种宏定义的方式对系统、子模块进行了配置。比如,通过宏,配置了mem管理模块的参数。该配置模块还通过宏,配置了协议栈所支持的协议簇,通过宏定制的方式,决定了支持那些协议。/LWIP_ARP==1:EnableARPfunctionality.#ifndefLWIP_ARP#defineLWIP_ARP1#endif截取一段代码说明问题。上面的代码表示配置的时候支持ARP(地址解析协议)协议族,此精选模板可编辑可修改代码下面对ARP协议族进行一些其他的配置。精选模板可编辑可修改【2】初始化模块初始化模块(这里主要讲的是TCP协议的初始化模块)主要在文件api/tcpip.c中。贴上源代码voidtcpip_init(void(initfunc)(void),voidarg){lwip_init();tcpip_init_done=initfunc;tcpip_init_done_arg=arg;mbox=sys_mbox_new(TCPIP_MBOX_SIZE);#ifLWIP_TCPIP_CORE_LOCKINGlock_tcpip_core=sys_sem_new(1);#endif/LWIP_TCPIP_CORE_LOCKINGsys_thread_new(TCPIP_THREAD_NAME,tcpip_thread,NULL,TCPIP_THREAD_STACKSIZE,TCPIP_THREAD_PRIO);}这个初始化模块是如此的重要,以至于我们可以说这是整个程序的核心。首先调用lwip_init()初始化了所有的子模块。然后调用sys_thread_new()启动了协议栈管理进程。所有的程序就在tcpip_thread这个进程里运行(这里说的是使用RAWAPI接口的情况)。【3】NetIf模块netif模块是非常重要的一个模块,主要是因为里面有一个netif的结构体。Netif模块为协议栈与底层驱动的接口模块,其将底层的一个网口设备描述成协议栈的一个接口设备(netinterface)。这个接口设备就是用上面说的netif结构体来描述。所以说neitif结构体很重要。Netif模块主要文件为core/netif.c和include/lwip/netif.h。structnetif{/pointertonextinlinkedliststructnetifnext;/IPaddressconfigurationinnetworkbyteorderstructip_addrip_addr;structip_addrnetmask;精选模板可编辑可修改structip_addrgw;/ThisfunctioniscalledbythenetworkdevicedrivertopassapacketuptheTCP/IPstack.err_t(input)(structpbufp,structnetifinp);/ThisfunctioniscalledbytheIPmodulewhenitwantstosendapacketontheinterface.Thisfunctiontypicallyfirstresolvesthehardwareaddress,thensendsthepacket.err_t(output)(structnetifnetif,structpbufp,structip_addripaddr);/ThisfunctioniscalledbytheARPmodulewhenitwantstosendapacketontheinterface.Thisfunctionoutputsthepbufas-isonthelinkmedium.err_t(linkoutput)(structnetifnetif,structpbufp);#ifLWIP_NETIF_STATUS_CALLBACK/Thisfunctioniscalledwhenthenetifstateissettoupordownvoid(status_callback)(structnetifnetif);#endif/LWIP_NETIF_STATUS_CALLBACK#ifLWIP_NETIF_LINK_CALLBACK/Thisfunctioniscalledwhenthenetiflinkissettoupordownvoid(link_callback)(structnetifnetif);#endif/LWIP_NETIF_LINK_CALLBACK/Thisfieldcanbesetbythedevicedriverandcouldpointtostateinformationforthedevice.voidstate;#ifLWIP_DHCP/theDHCPclientstateinformationforthisnetifstructdhcpdhcp;#endif/LWIP_DHCP#ifLWIP_AUTOIP/theAutoIPclientstateinformationforthisnetifstructautoipautoip;#endif#ifLWIP_NETIF_HOSTNAME/thehostnameforthisnetif,NULLisavalidvaluecharhostname;#endif/LWIP_NETIF_HOSTNAME/maximumtransferunit(inbytes)u16_tmtu;/numberofbytesusedinhwaddr精选模板可编辑可修改u8_thwaddr_len;/linklevelhardwareaddressofthisinterfaceu8_thwaddr[NETIF_MAX_HWADDR_LEN];/flags(seeNETIF_FLAG_above)u8_tflags;/descriptiveabbreviationcharname[2];/numberofthisinterfaceu8_tnum;#ifLWIP_SNMP/linktype(from"snmp_ifType"enumfromsnmp.h)u8_tlink_type;/(estimate)linkspeedu32_tlink_speed;/timestampatlastchangemade(up/down)u32_tts;/countersu32_tifinoctets;u32_tifinucastpkts;u32_tifinnucastpkts;u32_tifindiscards;u32_tifoutoctets;u32_tifoutucastpkts;u32_tifoutnucastpkts;u32_tifoutdiscards;#endif/LWIP_SNMP#ifLWIP_IGMP/ThisfunctioncouldbecalledtoaddordeleteaentryinthemulticastfiltertableoftheethernetMAC./err_t(igmp_mac_filter)(structnetifnetif,structip_addrgroup,u8_taction);#endif/LWIP_IGMP#ifLWIP_NETIF_HWADDRHINTu8_taddr_hint;#endif/LWIP_NETIF_HWADDRHINT#ifENABLE_LOOPBACK/Listofpacketstobequeuedforourselves.structpbufloop_first;structpbufloop_last;#ifLWIP_LOOPBACK_MAX_PBUFSu16_tloop_cnt_current;#endif/LWIP_LOOPBACK_MAX_PBUFS#endif/ENABLE_LOOPBACK};精选模板可编辑可修改这个结构体里所有的成员的作用都已经注释了,这里我们只关心两个成员:err_t(input)(structpbufp,structnetifinp);err_t(output)(structnetifnetif,structpbufp,当收到一个信息包时,设备驱动程序调用input指针指向的函数。网络接口通过output指针连接到设备驱动。这个指针指向设备驱动中一个向物理网络发送信息包的函数,当信息包被发送时由IP层调用。这个字段由设备驱动的初始设置函数填充。也就是说,当我们从网络上接收一个数据的时候我们调用input函数指针指向的函数进行数据接收的处理。当我们要向网络中发送一个数据时,我们调用output函数指针指向的那个函数进行数据的发送工作。因为这个netif结构体是与底层驱动进行交互的。所以input和output函数指针是直接指向网络驱动收发接口函数的。回到netif.c文件中,netif.c文件通过链表的方式描述了系统中的所有网口设备。因为系统中可能会有很多网络设备。【4】Mem(memp)模块其实mem和memp管理的是不同类型的内存,但都属于内存管理。所以放在了一起。Mem模块同一管理了协议栈使用的内容缓冲区,并管理pbuf结构以及报文的字段处理。主要的文件包括mem.c、memp.c、pbuf.c。这里我们所要关心的是内存管理单元所管理的pbuf结构体。typedefenum{PBUF_RAM,/pbufdataisstoredinRAMPBUF_ROM,/pbufdataisstoredinROMPBUF_REF,/pbufcomesfromthepbufpoolPBUF_POOL/pbufpayloadreferstoRAM}pbuf_type;如上所示,pbuf有四种类型(但是文档中只介绍了三种类型)。关于这四种类型的pbuf有什么区别请参考相应的文档,一句话说明四者的不同,主要是pbuf结构体中存放的数据的位置不同。一句话说明pbuf类型的选择。当接收网络数据包时,我们选择的pbuf是PBUF_POOL类型的。至于网络数据包的发送,根据自己的实际情况自己选择。精选模板可编辑可修改structpbufpbuf_alloc(pbuf_layerl,u16_tsize,pbuf_typetype);voidpbuf_realloc(structpbufp,u16_tsize);u8_tpbuf_header(structpbufp,s16_theader_size);voidpbuf_ref(structpbufp);voidpbuf_ref_chain(structpbufp);u8_tpbuf_free(structpbufp);u8_tpbuf_clen(structpbufp);voidpbuf_cat(structpbufhead,structpbuftail);voidpbuf_chain(structpbufhead,structpbuftail);structpbufpbuf_dechain(structpbufp);err_tpbuf_copy(structpbufp_to,structpbufp_from);u16_tpbuf_copy_partial(structpbufp,voiddataptr,u16_tlen,u16_toffset);err_tpbuf_take(structpbufbuf,constvoiddataptr,u16_tlen);structpbufpbuf_coalesce(structpbufp,pbuf_layerlayer);这是对pbuf结构体进行操作的函数。【5】netarp模块netarp模块是处理arp协议的模块,主要源文件为netif/etharp.c。其主要入口函数为:err_tethernet_input(structpbufp,structnetifnetif)该入口函数通过判断输入报文p的协议类型来决定是按照arp协议进行处理还是将该报文提交到IP协议。如果报文是arp报文,该接口则调用etharp_arp_input,进行arp请求处理。如果是ip报文,该接口就调用etharp_ip_input进行arp更新,并调用ip_input接口,将报文提交给ip层。在该模块中,创建了设备的地址映射arp表,并提供地址映射关系查询接口。同时还提供了arp报文的发送接口。如下:err_tetharp_output(structnetifnetif,structpbufq,structip_addripaddr)该接口需要注册到netif的output字段,ip层在输出报文时,通过该接口获取目标机的MAC地址,组合最终报文后,由该接口调用底层设备的驱动接口发送数据。在etharp_output接口中,判断报文类型,如果是广播包或者组播包,就调用etharp_send_ip(组装目标mac和源mac)接口,etharp_send_ip调用netif结构中的设备驱动注册的linkoutput钩子函数发送最终报文。如果是单播包,etharp_output接口就调用etharp_query进行ip地址和MAC地址的映射,来获取到目标机的MAC地址。并在etharp_query中调用etharp_send_ip来发送最终组合报文。【6】ip模块ip模块实现了协议的ip层处理,主要文件为ipv4/ip.c。其主要入口函数为:err_tip_input(structpbufp,structnetifinp)该接口通过判断输入报文的协议类型,将其输入到相应的上层协议模块中去。比如,将udp报文送到udp_input。精选模板可编辑可修改该模块另外一个接口是输入函数,原型如下:err_tip_output(structpbufp,structip_addrsrc,structip_addrdest,u8_tttl,u8_ttos,u8_tproto)该接口通过路由表或者传输ip后,调用netif的output函数指针指向的函数发送报文。【7】udp模块不是我们所关心的,这里就不做介绍【8】icmp模块用的不多,不必关心【9】igmp模块用的不多,不必关心【10】dhcp模块可能会用到。dhcp模块用于获取设备ip地址的相关信息。其处理入口主要有这么几个:dpch的启动、dpch的接收报文处理以及定时器模块的处理。主要的接口原型如下:err_tdhcp_start(structnetifnetif)该接口用于设备启动dhcp模块,主要是客户端的功能。该模块实现设备dhcp描述结构生成,并将dhcp的端口绑定到udp协议中,以及将本dhcp模块跟远端服务器端口进行绑定。最后启动dhcp申请。staticvoiddhcp_recv(voidarg,structudp_pcbpcb,structpbufp,structip_addraddr,u16_tport)该接口为一个注册接口,用于dhcp报文接收。在startdhcp时,该接口通过dhcp的udppcb注册到udp协议层。Udp进行报文处理后,根据端口调用该注册接口。该接口中,实现dhcp报文的协议处理。Voiddhcp_fine_tmr()Voiddhcp_coarse_tmr()这两个函数接口实现了dhcp的相关超时处理监控。上面一个用于请求应答超时处理。下面一个用于地址租用情况的到期处理。从源码分析看,上述的接口在应用lwip的协议栈时,需要重点关注。对于小内存应用的场合,该协议栈的内存管理以及pbuf应用部分需要自行改写精选模板可编辑可修改三、LWIP协议栈处理数据流程协议栈处理数据的流程主要是处理TCP数据包。关于UDP数据报的处理流程暂且不做讨论。LWIP对TCP数据包的处理流程如下图3.1所示。精选模板可编辑可修改图3.1LWIP协议栈数据处理流程如上图所示,lwip协议栈对TCP数据包的处理经过四层的处理。分别为应用层,传输层,网络层和网络接口层。图中左半边部分是数据发送的处理部分,右半部分是数据接收的处理部分。精选模板可编辑可修改【1】数据的发送整个过程的发起者是应用层。应用层调用ip_write()函数,接着tcp_write()函数再将控制权交给tcp_enqueue()函数,这个函数会在必要时将数据分割为适当大小的TCP段,然后再把这些TCP段放到所属连接的传输队列中。这时,tcp_output()函数会检查现在是不是能够发送数据,也就是判断接收器窗口是否拥有足够大的空间,阻塞窗口是否也足够大,如果条件满足,就使用ip_route()及ip_output_if()函数发送数据。【2】数据的接收过程的发起者是网络接口层,这里不做描述。网络接口层将数据包传递给ip_input()函数,该函数验证IP头后移交TCP段给tcp_input()函数。tcp_input()函数完成两项工作:其一,初始完整性检查(也就是校验和验证与TCP选项解析);其二,判定这个TCP段属于哪个TCP连接。接着,这个TCP段到达tcp_process()函数,这个函数实现了TCP状态机,任何必要的状态转换在这里实现。当该TCP所属的连接正处于接受网络数据的状态tcp_receive()函数将被调用。最终,tcp_receive()函数将数据传给上层的应用程序,完成接收过程。如果这个TCP段由一个不被承认的ACK应答数据构成,数据将会从缓冲区移走,它所占用的存储区被收回。同样,如果收到一个ACK应答确认数据,接收器同意接收更多的数据,如图所示,tcp_output()函数将会被调用。四、接口提取看完第三章的协议栈处理数据的流程,就知道与网络底层驱动的接口部分主要是在网络接口层。主要是在netif结构体里提到的input和output函数指针所指向的函数。具体提取出来的接口有:staticvoidlow_level_init(structnetifnetif)staticerr_tlow_level_output(structnetifnetif,structpbufp)staticstructpbuflow_level_input(structnetifnetif)err_tethernetif_input(structnetifnetif)err_tethernetif_init(structnetifnetif)精选模板可编辑可修改重写以上这些接口,并用重写的接口替换ethernetif.c里的接口。所要做的工作就是这些。..精选模板',)
提供LWIP协议栈架构与设计解析,lwip网络协议栈解析会员下载,编号:1700665515,格式为 docx,文件大小为23页,请使用软件:wps,office word 进行编辑,PPT模板中文字,图片,动画效果均可修改,PPT模板下载后图片无水印,更多精品PPT素材下载尽在某某PPT网。所有作品均是用户自行上传分享并拥有版权或使用权,仅供网友学习交流,未经上传用户书面授权,请勿作他用。若您的权利被侵害,请联系963098962@qq.com进行删除处理。