📄 自由风os梦工厂--实时操作系统uc-0s ii下tcp-ip协议栈的实现.htm
字号:
Computer Science)的Adam
Dunkels等开发的一套用于嵌入式系统的开放源代码TCP/IP协议栈。LwIP的含义是Light
Weight(轻型)IP协议。LwIP可以移植到操作系统上,也可以在无操作系统的情况下独立运行。LwIP
TCP/IP实现的重点是在保持TCP协议主要功能的基础上减少对RAM的占用,一般它只需要几十K的RAM和40K左右的ROM就可以运行,这使LwIP协议栈适合在低端嵌入式系统中使用。<BR>LwIP的特性如下:<BR>(1)
支持多网络接口下的IP转发<BR>(2) 支持ICMP协议 <BR>(3)
包括实验性扩展的的UDP(用户数据报协议)<BR>(4)
包括阻塞控制,RTT估算和快速恢复和快速转发的TCP(传输控制协议)<BR>(5)
提供专门的内部回调接口(Raw API)用于提高应用程序性能<BR>(6)
可选择的Berkeley接口API(多线程情况下)<BR>我们目前使用的是LwIP的最新稳定版V0.5.3。有关LwIP的详细内容,可以参考其代码和网站上的文档。</SPAN></P>
<UL>
<LI><SPAN class=p4>4 LwIP在uC/0S II下的实现</SPAN> </LI></UL>
<P><SPAN class=p4>4.1
概述<BR>LwIP协议栈在设计时就考虑到了将来的移植问题,因此把所有与硬件、OS、编译器相关的部份独立出来,放在/src/arch目录下。因此LwIP在uC/0S
II上的实现就是修改这个目录下的文件,其它的文件一般不应该修改。下面分几部份分别说明相应文件的实现原理和过程。具体的代码限于篇幅没有给出,Skyeye网站上有完整的代码和说明。<BR>4.2
与CPU或编译器相关的include文件<BR>/src/arch/include/arch目录下cc.h、cpu.h、perf.h中有一些与CPU或编译器相关的定义,如数据长度,字的高低位顺序等。这应该与用户实现uC/0S
II时定义的数据长度等参数是一致的。<BR>#define BYTE_ORDER LITTLE_ENDIAN
//ARM7默认为小端存储系统<BR>//数据类型长度的定义<BR>typedef unsigned char
u8_t;<BR>typedef signed char s8_t;<BR>typedef unsigned
short u16_t;<BR>typedef signed short s16_t;<BR>typedef
unsigned int u32_t;<BR>typedef signed int
s32_t;<BR>此外还有一点:一般情况下C语言的结构体struct是4字节对齐的,但是在处理数据包的时候,LwIP使用的是通过结构体中不同数据的长度来读取相应的数据的,所以,一定要在定义struct的时候使用_packed关键字,让编译器放弃struct的字节对齐。LwIP也考虑到了这个问题,所以,在它的结构体定义中有几个PACKED_FIELD_xxx宏,默认的时候这几个宏都是空的,可以在移植的时候添加不同的编译器所对应的_packed关键字。比如在Skyeye(ARM7)上对应gcc编译器的定义:<BR>#define
PACK_STRUCT_FIELD(x) x
__attribute__((packed))<BR>#define PACK_STRUCT_STRUCT
__attribute__((packed))<BR>#define
PACK_STRUCT_BEGIN<BR>#define PACK_STRUCT_END<BR>4.3
sys_arch操作系统相关部份<BR>sys_arch.[ch]中的内容是与OS相关的一些结构和函数,主要可以分为四个部份:<BR>(1)
sys_sem_t
信号量<BR>LwIP中需要使用信号量通信,所以在sys_arch中应实现信号量结构体和处理函数:<BR>struct
sys_sem_t<BR>sys_sem_new() //创建一个信号量结构<BR>sys_ sem
_free() //释放一个信号量结构<BR>sys_ sem _signal()
//发送信号量<BR>sys_ arch_sem _wait()
//请求信号量<BR>由于uC/0SII已经实现了信号量OS_EVENT的各种操作,并且功能和LwIP上面几个函数的目的功能是完全一样的,所以只要把uC/0SII的函数重新包装成上面的函数,就可以直接使用了。<BR>(2)
sys_mbox_t
消息<BR>LwIP使用消息队列来缓冲、传递数据报文,因此要在sys_arch中实现消息队列结构sys_mbox_t,以及相应的操作函数:<BR>sys_mbox_new()
//创建一个消息队列<BR>sys_mbox_free()
//释放一个消息队列<BR>sys_mbox_post()
//向消息队列发送消息<BR>sys_arch_mbox_fetch()
//从消息队列中获取消息<BR>uC/0SII同样实现了消息队列结构OSQ及其操作,但是uC/0SII没有对消息队列中的消息进行管理,因此不能直接使用,必须在uC/0SII的基础上重新实现。为了实现对消息的管理,我们定义了以下结构:<BR>typedef
struct {<BR>OS_EVENT* pQ;<BR>void*
pvQEntries[MAX_QUEUE_ENTRIES];<BR>}
sys_mbox_t;<BR>在以上结构中,包括OS_EVENT类型的队列指针(pQ)和队列内的消息(pvQEntries)两部分,对队列本身的管理利用uC/0SII自己的OSQ操作完成,然后使用uC/0SII中的内存管理模块实现对消息的创建、使用、删除回收,两部分综合起来形成了LwIP的消息队列功能。<BR>(3)
sys_arch_timeout
函数<BR>LwIP中每个与外界网络连接的线程都有自己的timeout属性,即等待超时时间。这个属性表现为每个线程都对应一个sys_timeout结构体队列,包括这个线程的timeout时间长度,以及超时后应调用的timeout函数,该函数会做一些释放连接,回收资源的工作。如果一个线程对应的sys_timeout为空(NULL),说明该线程对连接做永久的等待。<BR>timeout结构体已经由LwIP自己在sys.h中定义好了,而且对结构体队列的数据操作也由LwIP负责,我们所要实现的是如下函数:<BR>struct
sys_timeouts *
sys_arch_timeouts(void)<BR>这个函数的功能是返回目前正处于运行态的线程所对应的timeout队列指针。timeout队列属于线程的属性,因此是OS相关的函数,只能由用户实现。<BR>(4)
sys_thread_new
创建新线程<BR>LwIP可以是单线程运行,即只有一个tcpip线程(tcpip_thread),负责处理所有的tcp/ucp连接,各种网络程序都通过tcpip线程与网络交互。但LwIP也可以多线程运行,以提高效率,降低编程复杂度。这时就需要用户实现创建新线程的函数:<BR>void
sys_thread_new(void (* thread)(void *arg), void
*arg);<BR>在uC/0S
II中,没有线程(thread)的概念,只有任务(Task)。它已经提供了创建新任务的系统API调用OSTaskCreate,因此只要把OSTaskCreate封装一下,就可以实现sys_thread_new。需要注意的是LwIP中的thread并没有uC/0S
II中优先级的概念,实现时要由用户事先为LwIP中创建的线程分配好优先级。<BR>4.4
lib_arch中库函数的实现<BR>LwIP协议栈中用到了8个外部函数,这些函数通常与用户使用的系统或编译器有关,因此留给用户自己实现。如下:<BR>u16_t
htons(u16_t n); //16位数据高低字节交换<BR>u16_t ntohs(u16_t
n);<BR>u32_t htonl(u32_t n); //32位数据大小头对调<BR>u32_t
ntohl(u32_t n);<BR>int strlen(const char *str);
//返回字符串长度<BR>int strncmp(const char *str1, const char
*str2, int len); //字符串比较<BR>void bcopy(const void *src,
void *dest, int len); //内存数据块之间的互相拷贝<BR>void bzero(void
*data, int n);
//内存中指定长度的数据块清零<BR>前四个函数通常由用户自己实现。Skyeye(ARM7)中,由于使用了gcc编译器,gcc的lib库里已经有了后四个函数。而ez80的编译器函数库中缺少bcopy和bzero两个,需要自己编写。用户在其它CPU上实现时应根据自己的编译器来决定。<BR><BR>4.5
网络设备驱动程序<BR>ez80开发板自带的网络芯片为RealTek的8019as芯片,这是ISA
10BASE-T的以太网芯片,与Ne2k兼容。而我们在AT91模拟器Skyeye中所仿真的网络芯片也是Ne2k,所以目前实现的网络设备驱动是针对Ne2k的,其它类型的网络芯片驱动可以在LwIP的网站上找到。LwIP的网络驱动有一定的模型,/src/netif/ethernetif.c
文件即为驱动的模板,用户为自己的网络设备实现驱动时应参照此模板。<BR>在LwIP中可以有多个网络接口,每个网络接口都对应了一个struct
netif,这个netif包含了相应网络接口的属性、收发函数。LwIP调用netif的方法netif->input()及netif->output()进行以太网packet的收、发等操作。在驱动中主要做的,就是实现网络接口的收、发、初始化以及中断处理函数。驱动程序工作在IP协议模型的网络接口层,它提供给上层(IP层)的接口函数如下:<BR>//网卡初始化函数<BR>void
ethernetif_init(struct netif
*netif)<BR>//网卡接收函数,从网络接口接收以太网数据包并把其中的IP报文向IP层发送<BR>//在中断方式下由网卡ISR调用<BR>void
ethernetif_input(struct netif
*netif)<BR>//网卡发送函数,给IP层传过来的IP报文加上以太网包头并通过网络接口发送<BR>err_t
ethernetif_output(struct netif *netif, struct pbuf *p,
struct ip_addr *ipaddr)<BR>//网卡中断处理函数ISR<BR>void
ethernetif_isr(void);<BR>以上的函数都可以分为协议栈本身的处理和对网络接口硬件的操作两部份,但硬件操作是对上层屏蔽的,具体参见RTL8019as、DM9008等Ne2k网络芯片的数据手册。驱动程序可以到Skyeye或LwIP的网站下载。</SPAN></P>
<UL>
<LI><SPAN class=p4>5 应用实例的建立和测试</SPAN> </LI></UL>
<P><SPAN
class=p4>做完上面的移植修改工作以后,就可以在uC/0SII中初始化LwIP,并创建TCP或UDP任务进行测试了。这部份完全是C语言的实现,因此这部份在ez80和ARM7上基本都是一样的。值得注意的是LwIP的初始化必须在uC/0SII完全启动之后也就是在任务中进行,因为它的初始化用到了信号量等OS相关的操作。关键部份的代码和说明如下:<BR>main(){<BR>OSInit();<BR>OSTaskCreate(lwip_init_task,
&LineNo11, &lwip_init_stk[TASK_STK_SIZE-1], 0);
<BR>OSTaskCreate(usr_task,&LineNo12,&usr_stk[TASK_STK_SIZE-1],1);<BR>OSStart();<BR>}<BR>主程序中创建了lwip_init_task初始化LwIP任务(优先级0)和usr_task用户任务(优先级1)。lwip_init_task任务中除了初始化硬件时钟和LwIP之外,还创建了tcpip_thread(优先级5)和tcpecho_thread(优先级6)。实际上tcpip_thread才是LwIP的主线程,多线程的Berkley
API也是基于这个线程实现的,即上面的tcpecho_thread线程也要依靠tcpip_thread线程来与外界通信,这样做的好处是编程简单,结构清晰。<BR>实用Berkley
API实现的tcpecho_thread是一个TCP
echo服务器,监听7号端口,程序框架如下:<BR>void tcpecho_thread(void
*arg){<BR>conn = netconn_new(NETCONN_TCP);
//创建新的连接标识<BR>netconn_bind(conn, NULL, 7);
//绑定到7号端口<BR>netconn_listen(conn);
//开始监听端口<BR>while(1){<BR>newconn = netconn_accept(conn);
//接收外部到来的连接<BR>buf = netconn_recv(newconn) //获取数据<BR>…….
//处理数据<BR>netconn_write(newconn, data, len,
NETCONN_COPY); //发送数据<BR>netconn_delete(newconn);
//释放本次连接<BR>}<BR>}<BR>编译运行后,用ping ip地址命令可以得到ICMP
reply响应。用telnet ip地址 7(登录7号端口)命令可以看到echo
server的回显效果。说明ARP、ICMP、IP、TCP协议都已正确运行。</SPAN></P>
<P><BR>参考文献<BR>1. JEAN J.LABROSSE,邵贝贝译. uC/0S-II
――源码公开的实时嵌入式操作系统. 中国电力出版社<BR>2. Adam Dunkels. LwIP
source code.<BR>3. Adam Dunkels. sys_arch.txt in LwIP
source code.</P></TD></TR></TBODY></TABLE>
<P
class=p4> </P><!-- InstanceEndEditable --><BR></TD></TR></TBODY></TABLE><BR></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width=750 align=center border=0>
<TBODY>
<TR>
<TD background="自由风OS梦工厂--实时操作系统uC-0S II下TCP-IP协议栈的实现.files/bot.gif"
height=40>
<DIV align=center><A href="http://half.nease.net/info/about.htm"><FONT
color=#666666>◇</FONT> 关于本站</A> <FONT color=#666666>◇</FONT> <A
href="http://half.nease.net/info/server.htm">网站服务</A> <FONT
color=#666666>◇ </FONT><A
href="http://half.nease.net/info/declare.htm">网站声明</A> <FONT
color=#666666>◇ </FONT><A
href="http://half.nease.net/info/help.htm">帮助信息</A> <FONT
color=#666666>◇</FONT><A
href="http://gb.go98.com:81/gbbs.cgi?user=freewind" target=_blank> 留 言
簿</A> <FONT color=#666666>◇</FONT> <A
href="mailto:freewindos@sina.com">给我写信</A> <FONT
color=#666666>◇</FONT></DIV></TD></TR>
<TR bgColor=#cccccc>
<TD bgColor=#ffffff height=100>
<DIV align=center>
<P class=p7 align=center><FONT color=#666666>★ Copyright © 2003 FreeWind
Studio All Rights Reserved ★<BR><IMG height=20
src="自由风OS梦工厂--实时操作系统uC-0S II下TCP-IP协议栈的实现.files/vline.gif" width=1><IMG
height=20 src="自由风OS梦工厂--实时操作系统uC-0S II下TCP-IP协议栈的实现.files/hline.gif"
width=102> 自由风工作室版权所有<FONT color=#ffffff> </FONT></FONT><FONT
color=#666666><IMG height=20
src="自由风OS梦工厂--实时操作系统uC-0S II下TCP-IP协议栈的实现.files/hline.gif" width=102><IMG
height=20 src="自由风OS梦工厂--实时操作系统uC-0S II下TCP-IP协议栈的实现.files/vline.gif"
width=1></FONT></P>
<P class=p7 align=center><FONT class=p2 color=#666666><IMG
src="自由风OS梦工厂--实时操作系统uC-0S II下TCP-IP协议栈的实现.files/count.png"
align=absMiddle></FONT></P></DIV></TD></TR></TBODY></TABLE>
<DIV id=Layer1
style="Z-INDEX: 1; LEFT: 221px; VISIBILITY: hidden; WIDTH: 49px; POSITION: absolute; TOP: 289px; HEIGHT: 45px"><FONT
class=p2 color=#666666>
<SCRIPT>var tc_user="guojingfen";var tc_class="2";</SCRIPT>
<SCRIPT src="自由风OS梦工厂--实时操作系统uC-0S II下TCP-IP协议栈的实现.files/stat.js"></SCRIPT>
</FONT></DIV>
<P align=center><SPAN class=p7><FONT class=p2 color=#666666></FONT></SPAN></P><!-- InstanceEnd --></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -