[ZLG-ARM] ucos+lwip应用心得

[复制链接]
7726|28
 楼主| tmake 发表于 2009-7-7 13:13 | 显示全部楼层 |阅读模式
&nbsp;经过几天调试除掉几个bug以后,ucos+lwip在我的44b0+8019开发板上终于跑得比较稳定了.一只觉得lwip是一个不错的开放源码的tcp/ip&nbsp;协议栈,想把自己对lwip的移植和理解写出来.但是由于最近比较忙,lwip的移植也是利用业余时间做的,今天写好了第一部分(lwip的&nbsp;process&nbsp;model)先贴上来,如果大家有兴趣我再接着往下写.另外我的移植参看了skyeye扬晔大侠的代码,大家可以去看看扬晔大侠的lwip在ucos上移植的**和代码.&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lwIP是瑞士计算机科学院(Swedish&nbsp;Institute&nbsp;of&nbsp;Computer&nbsp;Science)的Adam&nbsp;Dunkels等开发的一套用于嵌入式系统的开放源代码TCP/IP协议栈。Lwip既可以移植到操作系统上,又可以<br />在无操作系统的情况下独立运行.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />LwIP的特性如下:<br />(1)&nbsp;&nbsp;&nbsp;&nbsp;支持多网络接口下的IP转发<br />(2)&nbsp;&nbsp;&nbsp;&nbsp;支持ICMP协议&nbsp;<br />(3)&nbsp;&nbsp;&nbsp;&nbsp;包括实验性扩展的的UDP(用户数据报协议)<br />(4)&nbsp;&nbsp;&nbsp;&nbsp;包括阻塞控制,RTT估算和快速恢复和快速转发的TCP(传输控制协议)<br />(5)&nbsp;&nbsp;&nbsp;&nbsp;提供专门的内部回调接口(Raw&nbsp;API)用于提高应用程序性能<br />(6)&nbsp;&nbsp;&nbsp;&nbsp;可选择的Berkeley接口API(多线程情况下)<br />(7)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在最新的版本中支持ppp<br />(8)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;新版本中增加了的IP&nbsp;fragment的支持.<br />(9)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;支持DHCP协议,动态分配ip地址.<br /><br />现在网上最新的版本是V0.6.4<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />1.lwip的进程模型(process&nbsp;model)<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tcp/ip协议栈的process&nbsp;model一般有几种方式.<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1.tcp/ip协议的每一层是一个单独进程.链路层是一个进程,ip层是一个进程,tcp层是一个进程.这样的好处是网络协<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;议的每一层都非常清晰,代码的调试和理解都非常容易.但是最大的坏处数据跨层传递时会引起上下文切换(context&nbsp;switch).<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;对于接收一个TCP&nbsp;segment要引起3次context&nbsp;switch(从网卡驱动程序到链路层进程,从链路层进程到ip层进程,从ip层进程<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;到TCP进程).通常对于操作系统来说,任务切换是要浪费时间的.过频的context&nbsp;swich是不可取的.<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2.另外一种方式是TCP/IP协议栈在操作系统内核当中.应用程序通过操作系统的系统调用(system&nbsp;call)和协议栈来进行通讯.<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这样TCP/IP的协议栈就限定于特定的操作系统内核了.如windows就是这种方式.<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3.lwip的process&nbsp;model:所有tcp/ip协议栈都在一个进程当中,这样tcp/ip协议栈就和操作系统内核分开了.而应用层程序既可以<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;是单独的进程也可以驻留在tcp/ip进程中.如果应用程序是单独的进程可以通过操作系统的邮箱,消息队列等和tcp/ip进程进行通讯.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果应用层程序驻留tcp/ip进程中,那应用层程序就利用内部回调函数口(Raw&nbsp;API)和tcp/ip协议栈通讯.对于ucos来说进程就是一个系统任务.lwip的process&nbsp;model请参看下图.在图中可以看到整个tcp/ip协议栈都在同一个任务(tcpip_thread)中.应用层程序既可以是独立的任务(如图中的tftp_thread,tcpecho_thread),也可以在tcpip_thread中(如图左上角)中利用内部回调函数口(Raw&nbsp;API)和tcp/ip协议栈通讯<br /><br />2&nbsp;Port&nbsp;Lwip&nbsp;to&nbsp;uCos<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在这个项目中我用的硬件平台是s3c44b0x+rtl8019.ucos在44b0上的移植在网上有很多大侠非常详尽的讲解和移植代码.我就不敢罗嗦了.需要说明的一点是lwip会为每个网络连接动态分配一些信号量(semaphone)和消息队列(Message&nbsp;Queue),当连接断开时会删掉这些semaphone和Queue.而Ucos-2.0不支持semaphone和Queue的删除,所以要选择一些较高版本的ucos.我用的是ucos-2.51.<br /><br />2.1&nbsp;Lwip的操作系统封装层(operating&nbsp;system.emulation&nbsp;layer)<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Lwip为了适应不同的操作系统,在代码中没有使用和某一个操作系统相关的系统调用和数据结构.而是在lwip和操作系统之间增加了一个操作系统封装层.操作系统封装层为操作系统服务(定时,进程同步,消息传递)提供了一个统一的接口.在lwip中进程同步使用semaphone和消息传递采用”mbox”(其实在ucos的实现中我们使用的是Message&nbsp;Queue来实现lwip中的”mbox”,下面大家可以看到这一点)<br /><br />Operating&nbsp;system&nbsp;emulation&nbsp;layer的原代码在…/lwip/src/core/sys.c中.而和具体的操作系统相关的代码在../lwip/src/arch/sys_arch.c中.<br />操作系统封装层的主要函数如下:<br />void&nbsp;sys_init(void)//系统初始化<br />sys_thread_t&nbsp;sys_thread_new(void&nbsp;(*&nbsp;function)(void&nbsp;*arg),&nbsp;void&nbsp;*arg,int&nbsp;prio)//创建一个新进程<br />&nbsp;&nbsp;sys_mbox_t&nbsp;sys_mbox_new(void)//创建一个邮箱<br />&nbsp;&nbsp;void&nbsp;&nbsp;sys_mbox_free(sys_mbox_t&nbsp;mbox)//释放并删除一个邮箱<br />&nbsp;&nbsp;void&nbsp;&nbsp;sys_mbox_post(sys_mbox_t&nbsp;mbox,&nbsp;void&nbsp;*data)&nbsp;//发送一个消息到邮箱<br />&nbsp;&nbsp;void&nbsp;sys_mbox_fetch(sys_mbox_t&nbsp;mbox,&nbsp;void&nbsp;**msg)//等待邮箱中的消息<br />&nbsp;&nbsp;sys_sem_t&nbsp;sys_sem_new(u8_t&nbsp;count)//创建一个信号量<br />void&nbsp;sys_sem_free(sys_sem_t&nbsp;sem)//释放并删除一个信号量<br />void&nbsp;sys_sem_signal(sys_sem_t&nbsp;sem)//发送一个信号量<br />void&nbsp;sys_sem_wait(sys_sem_t&nbsp;sem)//等待一个信号量<br />&nbsp;&nbsp;void&nbsp;sys_timeout(u32_t&nbsp;msecs,&nbsp;sys_timeout_handler&nbsp;h,&nbsp;void&nbsp;*arg)//设置一个超时事件<br />&nbsp;&nbsp;void&nbsp;sys_untimeout(sys_timeout_handler&nbsp;h,&nbsp;void&nbsp;*arg)//删除一个超时事件<br />&nbsp;&nbsp;…<br />关于操作系统封装层的信息可以阅读lwip的doc目录下面的sys_arch.txt.文件.<br /><br />2.2&nbsp;Lwip在ucos上的移植.<br /><br />2.2.1&nbsp;系统初始化<br /><br />&nbsp;&nbsp;&nbsp;sys_int必须在tcpip协议栈任务tcpip_thread创建前被调用.<br />&nbsp;&nbsp;#define&nbsp;MAX_QUEUES&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;20<br />#define&nbsp;MAX_QUEUE_ENTRIES&nbsp;&nbsp;&nbsp;20<br />typedef&nbsp;struct&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OS_EVENT*&nbsp;&nbsp;&nbsp;pQ;//ucos中指向事件控制块的指针<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void*&nbsp;&nbsp;pvQEntries[MAX_QUEUE_ENTRIES];//消息队列<br />//MAX_QUEUE_ENTRIES消息队列中最多消息数<br />}&nbsp;TQ_DESCR,&nbsp;*PQ_DESCR;<br />typedef&nbsp;PQ_DESCR&nbsp;&nbsp;sys_mbox_t;//可见lwip中的mbox其实是ucos的消息队列<br />static&nbsp;char&nbsp;pcQueueMemoryPool[MAX_QUEUES&nbsp;*&nbsp;sizeof(TQ_DESCR)&nbsp;];<br />&nbsp;&nbsp;&nbsp;void&nbsp;sys_init(void)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;u8_t&nbsp;i;<br />&nbsp;&nbsp;&nbsp;&nbsp;s8_t&nbsp;&nbsp;&nbsp;ucErr;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;pQueueMem&nbsp;=&nbsp;OSMemCreate(&nbsp;(void*)pcQueueMemoryPool,&nbsp;MAX_QUEUES,&nbsp;sizeof(TQ_DESCR),&nbsp;&ucErr&nbsp;);//为消息队列创建内存分区<br />&nbsp;&nbsp;&nbsp;&nbsp;//init&nbsp;lwip&nbsp;task&nbsp;prio&nbsp;offset<br />&nbsp;&nbsp;&nbsp;&nbsp;curr_prio_offset&nbsp;=&nbsp;0;<br />&nbsp;&nbsp;&nbsp;&nbsp;//init&nbsp;lwip_timeouts&nbsp;for&nbsp;every&nbsp;lwip&nbsp;task<br />&nbsp;&nbsp;&nbsp;&nbsp;//初始化lwip定时事件表,具体实现参考下面章节<br />&nbsp;&nbsp;&nbsp;&nbsp;for(i=0;i&ltLWIP_TASK_MAX;i++){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lwip_timeouts.next&nbsp;=&nbsp;NULL;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}<br /><br />2.2.2&nbsp;创建一个和tcp/ip相关新进程:<br />lwip中的进程就是ucos中的任务,创建一个新进程的代码如下:<br />#define&nbsp;LWIP_STK_SIZE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;10*1024//和tcp/ip相关任务的堆栈大小.可以根据情况自<br />//己设置,44b0开发板上有8M的sdram,所以设大<br />//一点也没有关系<br />//max&nbsp;number&nbsp;of&nbsp;lwip&nbsp;tasks<br />#define&nbsp;LWIP_TASK_MAX&nbsp;&nbsp;&nbsp;&nbsp;5&nbsp;//和tcp/ip相关的任务最多数目<br />//first&nbsp;prio&nbsp;of&nbsp;lwip&nbsp;tasks<br />#define&nbsp;LWIP_START_PRIO&nbsp;&nbsp;&nbsp;5&nbsp;//和tcp/ip相关任务的起始优先级,在本例中优先级可<br />//以从(5-9).注意tcpip_thread在所有tcp/ip相关进程中//应该是优先级最高的.在本例中就是优先级5&nbsp;<br />//如果用户需要创建和tcp/ip无关任务,如uart任务等,<br />//不要使用5-9的优先级<br />&nbsp;&nbsp;&nbsp;&nbsp;OS_STK&nbsp;LWIP_TASK_STK[LWIP_TASK_MAX][LWIP_STK_SIZE];//和tcp/ip相关进程<br />//的堆栈区<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;u8_t&nbsp;curr_prio_offset&nbsp;;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sys_thread_t&nbsp;sys_thread_new(void&nbsp;(*&nbsp;function)(void&nbsp;*arg),&nbsp;void&nbsp;*arg,int&nbsp;prio)<br />{<br />&nbsp;&nbsp;if(curr_prio_offset&nbsp;&lt&nbsp;LWIP_TASK_MAX){&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;OSTaskCreate(function,(void*)0x1111,&nbsp;&LWIP_TASK_STK[curr_prio_offset][LWIP_STK_SIZE-1],<br />LWIP_START_PRIO+curr_prio_offset&nbsp;);<br />&nbsp;&nbsp;&nbsp;&nbsp;curr_prio_offset++;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;1;<br />&nbsp;&nbsp;}&nbsp;else&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;PRINT('&nbsp;lwip&nbsp;task&nbsp;prio&nbsp;out&nbsp;of&nbsp;range&nbsp;!&nbsp;error!&nbsp;');<br />&nbsp;&nbsp;}<br />}<br />从代码中可以看出tcpip_thread应该是最先创建的.<br />&nbsp;&nbsp;<br />2.2.3&nbsp;Lwip中的定时事件<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在tcp/ip协议中很多时候都要用到定时,定时的实现也是tcp/ip协议栈中一个重要的部分.lwip中定时事件的数据结构如下.&nbsp;<br />struct&nbsp;sys_timeout&nbsp;{<br />&nbsp;&nbsp;struct&nbsp;sys_timeout&nbsp;*next;//指向下一个定时结构<br />&nbsp;&nbsp;u32_t&nbsp;time;//定时时间<br />&nbsp;&nbsp;sys_timeout_handler&nbsp;h;//定时时间到后执行的函数<br />&nbsp;&nbsp;void&nbsp;*arg;//定时时间到后执行函数的参数.<br />};<br />struct&nbsp;sys_timeouts&nbsp;{<br />&nbsp;&nbsp;struct&nbsp;sys_timeout&nbsp;*next;<br />};<br />struct&nbsp;sys_timeouts&nbsp;lwip_timeouts[LWIP_TASK_MAX];<br />Lwip中的定时事件表的结构如下图,每个和tcp/ip相关的任务的一系列定时事件组成一个单向链表.每个链表的起始指针存在lwip_timeouts的对应表项中.<br />函数sys_arch_timeouts返回对应于当前任务的指向定时事件链表的起始指针.该指针存在lwip_timeouts[MAX_LWIP_TASKS]中.<br />struct&nbsp;sys_timeouts&nbsp;null_timeouts;<br />struct&nbsp;sys_timeouts&nbsp;*&nbsp;sys_arch_timeouts(void)<br />{<br />&nbsp;&nbsp;u8_t&nbsp;curr_prio;<br />&nbsp;&nbsp;s16_t&nbsp;err,offset;<br />OS_TCB&nbsp;curr_task_pcb;<br />&nbsp;&nbsp;null_timeouts.next&nbsp;=&nbsp;NULL;<br />&nbsp;&nbsp;//获取当前任务的优先级<br />&nbsp;&nbsp;err&nbsp;=&nbsp;OSTaskQuery(OS_PRIO_SELF,&curr_task_pcb);<br />&nbsp;&nbsp;curr_prio&nbsp;=&nbsp;curr_task_pcb.OSTCBPrio;&nbsp;&nbsp;<br />&nbsp;&nbsp;offset&nbsp;=&nbsp;curr_prio&nbsp;-&nbsp;LWIP_START_PRIO;<br />&nbsp;&nbsp;//判断当前任务优先级是不是tcp/ip相关任务,优先级5-9<br />&nbsp;&nbsp;if(offset&nbsp;&lt&nbsp;0&nbsp;||&nbsp;offset&nbsp;&gt=&nbsp;LWIP_TASK_MAX)<br />&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;&null_timeouts;<br />&nbsp;&nbsp;}<br />&nbsp;&nbsp;return&nbsp;&lwip_timeouts[offset];<br />}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;注意:杨晔大侠移植的代码在本函数有一个bug.杨晔大侠的移植把上面函数中的OS_TCB&nbsp;curr_task_tcb定义成了全局变量,使本函数成为了一个不可重入函数.我也是在进行如下测试时发现了这个bug.我的开发板上设置的ip地址是192.168.1.95.我在windows的dos窗口内运行<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ping&nbsp;192.168.1.95&nbsp;–l&nbsp;2000&nbsp;–t,不间断用长度为2000的数据报进行ping测试,同时使用tftp客户端软件给192.168.1.95下载一个十几兆程序,同时再使用telnet连接192.168.1.95端口7(echo端口),往该端口写数测试echo功能.<br />在运行一段时间以后,开发板进入不再响应.我当时也是经过长时间的分析才发现是因为在低优先级任务运行ys_arch_timeouts()时被高优先级任务打断改写了curr_task_tcb的值,从而使sys_arch_timeouts返回的指针错误,进而导致系统死锁.函数sys_timeout给当前任务增加一个定时事件:<br />void&nbsp;sys_timeout(u32_t&nbsp;msecs,&nbsp;sys_timeout_handler&nbsp;h,&nbsp;void&nbsp;*arg)<br />{<br />&nbsp;&nbsp;struct&nbsp;sys_timeouts&nbsp;*timeouts;<br />&nbsp;&nbsp;struct&nbsp;sys_timeout&nbsp;*timeout,&nbsp;*t;<br />&nbsp;&nbsp;timeout&nbsp;=&nbsp;memp_malloc(MEMP_SYS_TIMEOUT);//为定时事件分配内存<br />&nbsp;&nbsp;if&nbsp;(timeout&nbsp;==&nbsp;NULL)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;return;<br />&nbsp;&nbsp;}<br />&nbsp;&nbsp;timeout-&gtnext&nbsp;=&nbsp;NULL;<br />&nbsp;&nbsp;timeout-&gth&nbsp;=&nbsp;h;<br />&nbsp;&nbsp;timeout-&gtarg&nbsp;=&nbsp;arg;<br />&nbsp;&nbsp;timeout-&gttime&nbsp;=&nbsp;msecs;<br />&nbsp;&nbsp;timeouts&nbsp;=&nbsp;sys_arch_timeouts();//返回当前任务定时事件链表起始指针<br />&nbsp;&nbsp;if&nbsp;(timeouts-&gtnext&nbsp;==&nbsp;NULL)&nbsp;{//如果链表为空直接增加该定时事件<br />&nbsp;&nbsp;&nbsp;&nbsp;timeouts-&gtnext&nbsp;=&nbsp;timeout;<br />&nbsp;&nbsp;&nbsp;&nbsp;return;<br />&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;//如果链表不为空,对定时事件进行排序.注意定时事件中的time存储的是本事件<br />//时间相对于前一事件的时间的差值<br />&nbsp;&nbsp;if&nbsp;(timeouts-&gtnext-&gttime&nbsp;&gt&nbsp;msecs)&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;<br />timeouts-&gtnext-&gttime&nbsp;-=&nbsp;msecs;<br />&nbsp;&nbsp;&nbsp;&nbsp;timeout-&gtnext&nbsp;=&nbsp;timeouts-&gtnext;<br />&nbsp;&nbsp;&nbsp;&nbsp;timeouts-&gtnext&nbsp;=&nbsp;timeout;<br />&nbsp;&nbsp;}&nbsp;else&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;for(t&nbsp;=&nbsp;timeouts-&gtnext;&nbsp;t&nbsp;!=&nbsp;NULL;&nbsp;t&nbsp;=&nbsp;t-&gtnext)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timeout-&gttime&nbsp;-=&nbsp;t-&gttime;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(t-&gtnext&nbsp;==&nbsp;NULL&nbsp;||<br />&nbsp;&nbsp;&nbsp;t-&gtnext-&gttime&nbsp;&gt&nbsp;timeout-&gttime)&nbsp;{<br />&nbsp;&nbsp;if&nbsp;(t-&gtnext&nbsp;!=&nbsp;NULL)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;t-&gtnext-&gttime&nbsp;-=&nbsp;timeout-&gttime;<br />&nbsp;&nbsp;}<br />&nbsp;&nbsp;timeout-&gtnext&nbsp;=&nbsp;t-&gtnext;<br />&nbsp;&nbsp;t-&gtnext&nbsp;=&nbsp;timeout;<br />&nbsp;&nbsp;break;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;}<br />}<br /><br />函数sys_untimeout从当前任务定时事件链表中删除一个定时事件<br />void&nbsp;sys_untimeout(sys_timeout_handler&nbsp;h,&nbsp;void&nbsp;*arg)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;sys_timeouts&nbsp;*timeouts;<br />&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;sys_timeout&nbsp;*prev_t,&nbsp;*t;<br />&nbsp;&nbsp;&nbsp;&nbsp;timeouts&nbsp;=&nbsp;sys_arch_timeouts();//返回当前任务定时事件链表起始指针<br />&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(timeouts-&gtnext&nbsp;==&nbsp;NULL)//如果链表为空直接返回<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;//查找对应定时事件并从链表中删除.<br />&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(t&nbsp;=&nbsp;timeouts-&gtnext,&nbsp;prev_t&nbsp;=&nbsp;NULL;&nbsp;t&nbsp;!=&nbsp;NULL;&nbsp;prev_t&nbsp;=&nbsp;t,&nbsp;t&nbsp;=&nbsp;t-&gtnext)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;((t-&gth&nbsp;==&nbsp;h)&nbsp;&&&nbsp;(t-&gtarg&nbsp;==&nbsp;arg))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;We&nbsp;have&nbsp;a&nbsp;match&nbsp;*/<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;Unlink&nbsp;from&nbsp;previous&nbsp;in&nbsp;list&nbsp;*/<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(prev_t&nbsp;==&nbsp;NULL)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timeouts-&gtnext&nbsp;=&nbsp;t-&gtnext;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;prev_t-&gtnext&nbsp;=&nbsp;t-&gtnext;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;If&nbsp;not&nbsp;the&nbsp;last&nbsp;one,&nbsp;add&nbsp;time&nbsp;of&nbsp;this&nbsp;one&nbsp;back&nbsp;to&nbsp;next&nbsp;*/<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(t-&gtnext&nbsp;!=&nbsp;NULL)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t-&gtnext-&gttime&nbsp;+=&nbsp;t-&gttime;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memp_free(MEMP_SYS_TIMEOUT,&nbsp;t);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;return;<br />}<br /><br />2.2.3&nbsp;&nbsp;“mbox”的实现:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(1)mbox的创建<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sys_mbox_t&nbsp;sys_mbox_new(void)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;u8_t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ucErr;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PQ_DESCR&nbsp;&nbsp;&nbsp;&nbsp;pQDesc;&nbsp;&nbsp;&nbsp;&nbsp;<br />//从消息队列内存分区中得到一个内存块<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pQDesc&nbsp;=&nbsp;OSMemGet(&nbsp;pQueueMem,&nbsp;&ucErr&nbsp;);&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(&nbsp;ucErr&nbsp;==&nbsp;OS_NO_ERR&nbsp;)&nbsp;{&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//创建一个消息队列<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pQDesc-&gtpQ=OSQCreate(&(pQDesc-&gtpvQEntries[0]),&nbsp;MAX_QUEUE_ENTRIES&nbsp;);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(&nbsp;pQDesc-&gtpQ&nbsp;!=&nbsp;NULL&nbsp;)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;pQDesc;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;SYS_MBOX_NULL;<br />}&nbsp;<br />&nbsp;&nbsp;(2)发一条消息给”mbox”<br />&nbsp;&nbsp;const&nbsp;void&nbsp;*&nbsp;const&nbsp;pvNullPointer&nbsp;=&nbsp;0xffffffff;<br />void&nbsp;sys_mbox_post(sys_mbox_t&nbsp;mbox,&nbsp;void&nbsp;*data)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;INT8U&nbsp;err;<br />&nbsp;&nbsp;&nbsp;&nbsp;if(&nbsp;!data&nbsp;)&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data&nbsp;=&nbsp;(void*)&pvNullPointer;<br />&nbsp;&nbsp;&nbsp;err=&nbsp;OSQPost(&nbsp;mbox-&gtpQ,&nbsp;data);<br />}<br />在ucos中,如果OSQPost&nbsp;(OS_EVENT&nbsp;*pevent,&nbsp;void&nbsp;*msg)中的msg==NULL&nbsp;会返回一条OS_ERR_POST_NULL_PTR错误.而在lwip中会调用sys_mbox_post(mbox,NULL)发送一条空消息,我们在本函数中把NULL变成一个常量指针0xffffffff.<br />(3)从”mbox”中读取一条消息<br />#define&nbsp;SYS_ARCH_TIMEOUT&nbsp;0xffffffff<br />void&nbsp;sys_mbox_fetch(sys_mbox_t&nbsp;mbox,&nbsp;void&nbsp;**msg)<br />{<br />&nbsp;&nbsp;u32_t&nbsp;time;<br />&nbsp;&nbsp;struct&nbsp;sys_timeouts&nbsp;*timeouts;<br />&nbsp;&nbsp;struct&nbsp;sys_timeout&nbsp;*tmptimeout;<br />&nbsp;&nbsp;sys_timeout_handler&nbsp;h;<br />&nbsp;&nbsp;void&nbsp;*arg;<br />again:<br />&nbsp;&nbsp;timeouts&nbsp;=&nbsp;sys_arch_timeouts();////返回当前任务定时事件链表起始指针<br />&nbsp;&nbsp;if&nbsp;(!timeouts&nbsp;||&nbsp;!timeouts-&gtnext)&nbsp;{//如果定时事件链表为空<br />&nbsp;&nbsp;&nbsp;&nbsp;sys_arch_mbox_fetch(mbox,&nbsp;msg,&nbsp;0);//无超时等待消息<br />&nbsp;&nbsp;}&nbsp;else&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(timeouts-&gtnext-&gttime&nbsp;&gt&nbsp;0)&nbsp;{<br />&nbsp;&nbsp;&nbsp;//如果超时事件链表不为空,而且第一个超时事件的time&nbsp;!=0<br />//带超时等待消息队列,超时时间等于超时事件链表中第一个超时事件的time,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;time&nbsp;=&nbsp;sys_arch_mbox_fetch(mbox,&nbsp;msg,&nbsp;timeouts-&gtnext-&gttime);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//在后面分析中可以看到sys_arch_mbox_fetch调用了ucos中的OSQPend系统调<br />//用从消息队列中读取消息.<br />//如果”mbox”消息队列不为空,任务立刻返回,否则任务进入阻塞态.<br />//需要重点说明的是sys_arch_mbox_fetch的返回值time:如果sys_arch_mbox_fetch<br />//因为超时返回,time=SYS_ARCH_TIMEOUT,<br />//如果sys_arch_mbox_fetch因为收到消息而返回,<br />//time&nbsp;=&nbsp;收到消息时刻的时间-执行sys_arch_mbox_fetch时刻的时间,单位是毫秒<br />//由于在ucos中任务调用OSQPend系统调用进入阻塞态,到收到消息重新开始执行<br />//这段时间没有记录下来,所以我们要简单修改ucos的源代码.(后面我们会看到).<br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//如果定时事件链表不为空,而且第一个定时事件的time&nbsp;==0,表示该事件的定时<br />//时间到<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;time&nbsp;=&nbsp;SYS_ARCH_TIMEOUT;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(time&nbsp;==&nbsp;SYS_ARCH_TIMEOUT)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//一个定时事件的定时时间到<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tmptimeout&nbsp;=&nbsp;timeouts-&gtnext;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timeouts-&gtnext&nbsp;=&nbsp;tmptimeout-&gtnext;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h&nbsp;=&nbsp;tmptimeout-&gth;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;arg&nbsp;=&nbsp;tmptimeout-&gtarg;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memp_free(MEMP_SYS_TIMEOUT,&nbsp;tmptimeout);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//从内存中释放该定时事件,并执行该定时事件中的函数<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(h&nbsp;!=&nbsp;NULL)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h(arg);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//因为定时事件中的定时时间到或者是因为sys_arch_mbo_fetch超时到而执行到<br />//这里,返回本函数开头重新等待mbox的消息<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;goto&nbsp;again;<br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;//如果sys_arch_mbox_fetch无超时收到消息返回<br />//则刷新定时事件链表中定时事件的time值.<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(time&nbsp;&lt=&nbsp;timeouts-&gtnext-&gttime)&nbsp;{<br />&nbsp;&nbsp;timeouts-&gtnext-&gttime&nbsp;-=&nbsp;time;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{<br />&nbsp;&nbsp;timeouts-&gtnext-&gttime&nbsp;=&nbsp;0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;}<br />}<br /><br />u32_t&nbsp;&nbsp;sys_arch_mbox_fetch(sys_mbox_t&nbsp;mbox,&nbsp;void&nbsp;**data,&nbsp;u32_t&nbsp;timeout)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;u32_t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ucErr;<br />&nbsp;&nbsp;&nbsp;&nbsp;u16_t&nbsp;ucos_timeout;<br />&nbsp;&nbsp;//在&nbsp;lwip中&nbsp;,timeout的单位是ms&nbsp;&nbsp;<br />&nbsp;&nbsp;//&nbsp;在ucosII&nbsp;,timeout&nbsp;的单位是timer&nbsp;tick&nbsp;<br />&nbsp;&nbsp;&nbsp;ucos_timeout&nbsp;=&nbsp;0;<br />&nbsp;&nbsp;if(timeout&nbsp;!=&nbsp;0){<br />&nbsp;&nbsp;ucos_timeout&nbsp;=&nbsp;(timeout&nbsp;)*(&nbsp;OS_TICKS_PER_SEC/1000);<br />&nbsp;&nbsp;if(ucos_timeout&nbsp;&lt&nbsp;1)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ucos_timeout&nbsp;=&nbsp;1;<br />&nbsp;&nbsp;else&nbsp;if(ucos_timeout&nbsp;&gt&nbsp;65535)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ucos_timeout&nbsp;=&nbsp;65535;<br />&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;//如果data!=NULL就返回消息指针,<br />&nbsp;&nbsp;if(data&nbsp;!=&nbsp;NULL){<br />&nbsp;&nbsp;&nbsp;&nbsp;*data&nbsp;=&nbsp;OSQPend(&nbsp;mbox-&gtpQ,&nbsp;(u16_t)ucos_timeout,&nbsp;&ucErr&nbsp;);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;}else{<br />&nbsp;&nbsp;&nbsp;&nbsp;OSQPend(mbox-&gtpQ,(u16_t)ucos_timeout,&ucErr);<br />&nbsp;&nbsp;}<br />//这里修改了ucos中的OSQPend系统调用,&nbsp;<br />//原来的void&nbsp;&nbsp;*OSQPend&nbsp;(OS_EVENT&nbsp;*pevent,&nbsp;INT16U&nbsp;timeout,&nbsp;INT8U&nbsp;*err)<br />//&nbsp;err的返回值只有两种:收到消息就返回OS_NO_ERR,超时则返回OS_TIMEOUT<br />//这里先将err从8位数据改变成了16位数据&nbsp;OSQPend(*pevent,timeout,&nbsp;INT16U&nbsp;*err)<br />//重新定义了OS_TIMEOUT<br />//在ucos中原有#define&nbsp;OS_TIMEOUT&nbsp;20<br />//改为&nbsp;#define&nbsp;&nbsp;OS_TIMEOUT&nbsp;&nbsp;-1<br />//err返回值的意义也改变了,如果超时返回OS_TIMEOUT<br />//&nbsp;如果收到消息,则返回OSTCBCur-&gtOSTCBDly修改部分代码如下<br />//if&nbsp;(msg&nbsp;!=&nbsp;(void&nbsp;*)0)&nbsp;{&nbsp;/*&nbsp;Did&nbsp;we&nbsp;get&nbsp;a&nbsp;message?&nbsp;&nbsp;*/<br />//&nbsp;OSTCBCur-&gtOSTCBMsg&nbsp;=&nbsp;(void&nbsp;*)0;<br />//&nbsp;OSTCBCur-&gtOSTCBStat&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;OS_STAT_RDY;<br />//&nbsp;OSTCBCur-&gtOSTCBEventPtr&nbsp;=&nbsp;(OS_EVENT&nbsp;*)0;<br />//&nbsp;*err&nbsp;=&nbsp;OSTCBCur-&gtOSTCBDly;//&nbsp;zhangzs&nbsp;@2003.12.12<br />//&nbsp;&nbsp;&nbsp;&nbsp;OS_EXIT_CRITICAL();<br />//&nbsp;return&nbsp;(msg);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;Return&nbsp;message&nbsp;received&nbsp;*/<br />//&nbsp;&nbsp;&nbsp;&nbsp;}<br />//关于ucos的OSTBCur-&gtOSTCBDly的含义请查阅ucos的书籍<br />&nbsp;&nbsp;if(&nbsp;ucErr&nbsp;==&nbsp;OS_TIMEOUT&nbsp;)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timeout&nbsp;=&nbsp;SYS_ARCH_TIMEOUT;<br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(*data&nbsp;==&nbsp;(void*)&pvNullPointer&nbsp;)&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*data&nbsp;=&nbsp;NULL;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//单位转换,从ucos&nbsp;tick-&gtms<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timeout&nbsp;=&nbsp;(ucos_timeout&nbsp;-ucErr)*(1000/&nbsp;OS_TICKS_PER_SEC);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;return&nbsp;timeout;<br />}<br />semaphone的实现和mbox类似,这里就不再重复了.<br />&nbsp;<br /> &nbsp;&nbsp;<br />
postcode 发表于 2009-7-7 13:22 | 显示全部楼层

很详细,Good

  
chun1chun 发表于 2009-7-7 13:34 | 显示全部楼层

谢谢

  
 楼主| tmake 发表于 2009-7-9 13:29 | 显示全部楼层

真是详细

  
jinjing999 发表于 2009-9-28 14:56 | 显示全部楼层
敬礼,学习
小猪的天空 发表于 2009-9-29 16:32 | 显示全部楼层
很不错呀,谢谢,继续关注
skyhigh 发表于 2009-9-30 09:15 | 显示全部楼层
转载要标明,谢谢
pigeon0411 发表于 2009-10-8 10:10 | 显示全部楼层
不足之处就是像其他低级简单的TCP/IP协议栈一样,都不支持broadcast ,multicast,用起来不是那么爽。
而且占用RAM相当大。
nthq2004 发表于 2009-10-15 16:53 | 显示全部楼层
非常好,学习中。
精益求精 发表于 2009-10-16 08:27 | 显示全部楼层
孤独行者 发表于 2009-10-25 10:06 | 显示全部楼层
呵呵,你很强啊
孤独行者 发表于 2009-10-25 10:06 | 显示全部楼层
说得很好,我学到了很多
孤独行者 发表于 2009-10-25 10:09 | 显示全部楼层
呵呵,好像是考配的啊!
torch888 发表于 2009-10-25 13:11 | 显示全部楼层
1# tmake


不错,想问一下,除了要修改系统封装层,硬件底层的驱动是不是也要修改,例如如何和PHY芯片通信,如何将PHY收到的数据送到IP层,是不是在写控制PHY芯片的读写函数中要涉及到上面所定义的信号量或者邮箱MBOX,能不能再写具体一点
LiangXinJi 发表于 2009-11-13 18:01 | 显示全部楼层
做个记号.学习了
yoyoxian 发表于 2009-11-14 16:50 | 显示全部楼层
好东西,学习了
beny5566 发表于 2009-11-15 13:38 | 显示全部楼层
确实不错啊,
收藏了。
swolf 发表于 2009-11-15 17:00 | 显示全部楼层
总结的真好,
收藏了。
linhai1986 发表于 2009-11-15 23:03 | 显示全部楼层
好东西,学习了
wuzx-61 发表于 2009-11-15 23:11 | 显示全部楼层
您需要登录后才可以回帖 登录 | 注册

本版积分规则

40

主题

179

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部