您的位置 首页 模拟

ucos+lwip使用心得

经过几天调试除掉几个bug以后,ucos+lwip在我的44b0+8019开发板上终于跑得比较稳定了.一只觉得lwip是一个不错的开放源码的tcp/ip 协议栈,想把自己对lwip的移

通过几天调试除去几个bug今后,ucos+lwip在我的44b0+8019开发板上总算跑得比较稳定了.一只觉得lwip是一个不错的开放源码的tcp/ip 协议栈,想把自己对lwip的移植和了解写出来.可是由于最近比较忙,lwip的移植也是运用业余时刻做的,今日写好了榜首部分(lwip的 process model)先贴上来,假如咱们有爱好我再接着往下写.别的我的移植参看了skyeye扬晔大侠的代码,咱们能够去看看扬晔大侠的lwip在ucos上移植的文章和代码. lwip运用心得 lwIP是瑞士计算机科学院(Swedish Institute of Computer Science)的Adam Dunkels等开发的一套用于嵌入式体系的开放源代码TCP/IP协议栈。Lwip既能够移植到操作体系上,又能够在无操作体系的情况下独立运转. LwIP的特性如下: (1) 支撑多网络接口下的IP转发 (2) 支撑ICMP协议 (3) 包含实验性扩展的的UDP(用户数据报协议) (4) 包含堵塞操控,RTT预算和快速康复和快速转发的TCP(传输操控协议) (5) 供给专门的内部回调接口(Raw API)用于进步运用程序功用 (6) 可挑选的Berkeley接口API(多线程情况下) (7) 在最新的版别中支撑ppp (8) 新版别中添加了的IP fragment的支撑. (9) 支撑DHCP协议,动态分配ip地址. 现在网上最新的版别是V0.6.4 1.lwip的进程模型(process model) tcp/ip协议栈的process model一般有几种方法. 1.tcp/ip协议的每一层是一个独自进程.链路层是一个进程,ip层是一个进

1.jpg

程,tcp层是一个进程.这样的优点是网络协 议的每一层都十分明晰,代码的调试和了解都十分简略.可是最大的害处数据跨层传递时会引起上下文切换(context switch). 关于接纳一个TCP segment要引起3次context switch(从网卡驱动程序到链路层进程,从链路层进程到ip层进程,从ip层进程 到TCP进程).一般关于操作体系来说,使命切换是要浪费时刻的.过频的context swich是不行取的. 2.别的一种方法是TCP/IP协议栈在操作体系内核傍边.运用程序通过操作体系的体系调用(system call)和协议栈来进行通讯. 这样TCP/IP的协议栈就限定于特定的操作体系内核了.如windows便是这种方法. 3.lwip的process model:一切tcp/ip协议栈都在一个进程傍边,这样tcp/ip协议栈就和操作体系内核分开了.而运用层程序既能够 是独自的进程也能够驻留在tcp/ip进程中.假如运用程序是独自的进程能够通过操作体系的邮箱,音讯行列等和tcp/ip进程进行通讯. 假如运用层程序驻留tcp/ip进程中,那运用层程序就运用内部回调函数口(Raw API)和tcp/ip协议栈通讯.关于ucos来说进程便是一个体系使命.lwip的process model请参看下图.在图中能够看到整个tcp/ip协议栈都在同一个使命(tcpip_thread)中.运用层程序既能够是独立的使命(如图中的 tftp_thread,tcpecho_thread),也能够在tcpip_thread中(如图左上角)中运用内部回调函数口(Raw API)和tcp/ip协议栈通讯

2 Port Lwip to uCos 在这个项目中我用的硬件渠道是s3c44b0x+rtl8019.ucos在44b0上的移植在网上有许多大侠十分翔实的解说和移植代码.我就不敢罗嗦了.需求阐明的一点是lwip会为每个网络衔接动态分配一些信号量(semaphone)和音讯行列(Message Queue),当衔接断开时会删掉这些semaphone和Queue.而Ucos-2.0不支撑semaphone和Queue的删去,所以要挑选一些较高版别的ucos.我用的是ucos-2.51. 2.1 Lwip的操作体系封装层(operating system.emulation layer) Lwip为了习惯不同的操作体系,在代码中没有运用和某一个操作体系相关的体系调用和数据结构.而是在lwip和操作体系之间添加了一个操作体系封装层. 操作体系封装层为操作体系服务(守时,进程同步,音讯传递)供给了一个一致的接口.在lwip中进程同步运用semaphone和音讯传递选用”mbox”(其实在ucos的完成中咱们运用的是Message Queue来完成lwip中的”mbox”,下面咱们能够看到这一点) Operating system emulation layer的原代码在…/lwip/src/core/sys.c中.而和详细的操作体系相关的代码在../lwip/src/arch /sys_arch.c中. 操作体系封装层的首要函数如下: void sys_init(void)//体系初始化 sys_thread_t sys_thread_new(void (* function)(void *arg), void *arg,int prio)//创立一个新进程 sys_mbox_t sys_mbox_new(void)//创立一个邮箱 void sys_mbox_free(sys_mbox_t mbox)//开释并删去一个邮箱 void sys_mbox_post(sys_mbox_t mbox, void *data) //发送一个音讯到邮箱 void sys_mbox_fetch(sys_mbox_t mbox, void **msg)//等候邮箱中的音讯 sys_sem_t sys_sem_new(u8_t count)//创立一个信号量 void sys_sem_free(sys_sem_t sem)//开释并删去一个信号量 void sys_sem_signal(sys_sem_t sem)//发送一个信号量 void sys_sem_wait(sys_sem_t sem)//等候一个信号量 void sys_timeout(u32_t msecs, sys_timeout_handler h, void *arg)//设置一个超时事情 void sys_untimeout(sys_timeout_handler h, void *arg)//删去一个超时事情 … 关于操作体系封装层的信息能够阅览lwip的doc目录下面的sys_arch.txt.文件. 2.2 Lwip在ucos上的移植. 2.2.1 体系初始化 sys_int必须在tcpip协议栈使命tcpip_thread创立前被调用. #define MAX_QUEUES 20 #define MAX_QUEUE_ENTRIES 20 typedef struct { OS_EVENT* pQ;//ucos中指向事情操控块的指针 void* pvQEntries[MAX_QUEUE_ENTRIES];//音讯行列 //MAX_QUEUE_ENTRIES音讯行列中最多音讯数 } TQ_DESCR, *PQ_DESCR; typedef PQ_DESCR sys_mbox_t;//可见lwip中的mbox其实是ucos的音讯行列 static char pcQueueMemoryPool[MAX_QUEUES * sizeof(TQ_DESCR) ]; void sys_init(void) { u8_t i; s8_t ucErr; pQueueMem = OSMemCreate( (void*)pcQueueMemoryPool, MAX_QUEUES, sizeof(TQ_DESCR), %26;amp;ucErr );//为音讯行列创立内存分区 //init lwip task prio offset curr_prio_offset = 0; //init lwip_timeouts for every lwip task //初始化lwip守时事情表,详细完成参阅下面章节 for(i=0;i 函数sys_arch_timeouts回来对应于当前使命的指向守时事情链表的开始指针.该指针存在 lwip_timeouts[MAX_LWIP_TASKS]中. struct sys_timeouts null_timeouts; struct sys_timeouts * sys_arch_timeouts(void) { u8_t curr_prio; s16_t err,offset; OS_TCB curr_task_pcb; null_timeouts.next = NULL; //获取当前使命的优先级 err = OSTaskQuery(OS_PRIO_SELF,%26;amp;curr_task_pcb); curr_prio = curr_task_pcb.OSTCBPrio; offset = curr_prio – LWIP_START_PRIO; //判别当前使命优先级是不是tcp/ip相关使命,优先级5-9 if(offset 0 || offset >= LWIP_TASK_MAX) { return %26;amp;null_timeouts; } return %26;amp;lwip_timeouts[offset]; } 留意:杨晔大侠移植的代码在本函数有一个bug.杨晔大侠的移植把上面函数中的OS_TCB curr_task_tcb界说成了全局变量,使本函数成为了一个不行重入函数.我也是在进行如下测验时发现了这个bug.我的开发板上设置的ip地址是 192.168.1.95.我在windows的dos窗口内运转 ping 192.168.1.95 –l 2000 –t,不间断用长度为2000的数据报进行ping测验,一起运用tftp客户端软件给192.168.1.95下载一个十几兆程序,一起再运用 telnet衔接192.168.1.95端口7(echo端口),往该端口写数测验echo功用. 在运转一段时刻今后,开发板进入不再呼应.我其时也是通过长期的剖析才发现是由于在低优先级使命运转ys_arch_timeouts()时被高优先级使命打断改写了curr_task_tcb的值,从而使sys_arch_timeouts回来的指针过错,从而导致体系死锁.函数 sys_timeout给当前使命添加一个守时事情: void sys_timeout(u32_t msecs, sys_timeout_handler h, void *arg) { struct sys_timeouts *timeouts; struct sys_timeout *timeout, *t; timeout = memp_malloc(MEMP_SYS_TIMEOUT);//为守时事情分配内存 if (timeout == NULL) { return; } timeout->next = NULL; timeout->h = h; timeout->arg = arg; timeout->time = msecs; timeouts = sys_arch_timeouts();//回来当前使命守时事情链表开始指针 if (timeouts->next == NULL) {//假如链表为空直接添加该守时事情 timeouts->next = timeout; return; } //假如链表不为空,对守时事情进行排序.留意守时事情中的time存储的是本事情 //时刻相关于前一事情的时刻的差值 if (timeouts->next->time > msecs) { timeouts->next->time -= msecs; timeout->next = timeouts->next; timeouts->next = timeout; } else { for(t = timeouts->next; t != NULL; t = t->next) { timeout->time -= t->time; if (t->next == NULL || t->next->time > timeout->time) { if (t->next != NULL) { t->next->time -= timeout->time; } timeout->next = t->next; t->next = timeout; break; } } } } 函数sys_untimeout从当前使命守时事情链表中删去一个守时事情 void sys_untimeout(sys_timeout_handler h, void *arg) { struct sys_timeouts *timeouts; struct sys_timeout *prev_t, *t; timeouts = sys_arch_timeouts();//回来当前使命守时事情链表开始指针 if (timeouts->next == NULL)//假如链表为空直接回来 { return; } //查找对应守时事情并从链表中删去. for (t = timeouts->next, prev_t = NULL; t != NULL; prev_t = t, t = t->next) { if ((t->h == h) %26;amp;%26;amp; (t->arg == arg)) { /* We have a match */ /* Unlink from previous in list */ if (prev_t == NULL) timeouts->next = t->next; else prev_t->next = t->next; /* If not the last one, add time of this one back to next */ if (t->next != NULL) t->next->time += t->time; memp_free(MEMP_SYS_TIMEOUT, t); return; } } return; } 2.2.3 “mbox”的完成: (1)mbox的创立 sys_mbox_t sys_mbox_new(void) { u8_t ucErr; PQ_DESCR pQDesc; //从音讯行列内存分区中得到一个内存块 pQDesc = OSMemGet( pQueueMem, %26;amp;ucErr ); if( ucErr == OS_NO_ERR ) { //创立一个音讯行列 pQDesc->pQ=OSQCreate(%26;amp;(pQDesc->pvQEntries[0]), MAX_QUEUE_ENTRIES ); if( pQDesc->pQ != NULL ) { return pQDesc; } } return SYS_MBOX_NULL; } (2)发一条音讯给”mbox” const void * const pvNullPointer = 0xffffffff; void sys_mbox_post(sys_mbox_t mbox, void *data) { INT8U err; if( !data ) data = (void*)%26;amp;pvNullPointer; err= OSQPost( mbox->pQ, data); } 在ucos中,假如OSQPost (OS_EVENT *pevent, void *msg)中的msg==NULL 会回来一条OS_ERR_POST_NULL_PTR过错.而在lwip中会调用sys_mbox_post(mbox,NULL)发送一条空音讯,咱们在本函数中把NULL变成一个常量指针0xffffffff. (3)从”mbox”中读取一条音讯 #define SYS_ARCH_TIMEOUT 0xffffffff void sys_mbox_fetch(sys_mbox_t mbox, void **msg) { u32_t time; struct sys_timeouts *timeouts; struct sys_timeout *tmptimeout; sys_timeout_handler h; void *arg; again: timeouts = sys_arch_timeouts();////回来当前使命守时事情链表开始指针 if (!timeouts || !timeouts->next) {//假如守时事情链表为空 sys_arch_mbox_fetch(mbox, msg, 0);//无超时等候音讯 } else { if (timeouts->next->time > 0) { //假如超时事情链表不为空,并且榜首个超时事情的time !=0 //带超时等候音讯行列,超时时刻等于超时事情链表中榜首个超时事情的time, time = sys_arch_mbox_fetch(mbox, msg, timeouts->next->time); //在后边剖析中能够看到sys_arch_mbox_fetch调用了ucos中的OSQPend体系调 //用从音讯行列中读取音讯. //假如”mbox”音讯行列不为空,使命马上回来,不然使命进入堵塞态. //需求要点阐明的是sys_arch_mbox_fetch的回来值time:假如sys_arch_mbox_fetch //由于超时回来,time=SYS_ARCH_TIMEOUT, //假如sys_arch_mbox_fetch由于收到音讯而回来, //time = 收到音讯时刻的时刻-履行sys_arch_mbox_fetch时刻的时刻,单位是毫秒 //由于在ucos中使命调用OSQPend体系调用进入堵塞态,到收到音讯从头开始履行 //这段时刻没有记录下来,所以咱们要简略修正ucos的源代码.(后边咱们会看到). } else { //假如守时事情链表不为空,并且榜首个守时事情的time ==0,表明该事情的守时 //时刻到 time = SYS_ARCH_TIMEOUT; } if (time == SYS_ARCH_TIMEOUT) { //一个守时事情的守时时刻到 tmptimeout = timeouts->next; timeouts->next = tmptimeout->next; h = tmptimeout->h; arg = tmptimeout->arg; memp_free(MEMP_SYS_TIMEOUT, tmptimeout); //从内存中开释该守时事情,并履行该守时事情中的函数 if (h != NULL) { h(arg); } //由于守时事情中的守时时刻到或许是由于sys_arch_mbo_fetch超时到而履行到 //这儿,回来本函数最初从头等候mbox的音讯 goto again; } else { //假如sys_arch_mbox_fetch无超时收到音讯回来 //则改写守时事情链表中守时事情的time值. if (time = timeouts->next->time) { timeouts->next->time -= time; } else { timeouts->next->time = 0; } } } } u32_t sys_arch_mbox_fetch(sys_mbox_t mbox, void **data, u32_t timeout) { u32_t ucErr; u16_t ucos_timeout; //在 lwip中 ,timeout的单位是ms // 在ucosII ,timeout 的单位是timer tick ucos_timeout = 0; if(timeout != 0){ ucos_timeout = (timeout )*( OS_TICKS_PER_SEC/1000); if(ucos_timeout 1) ucos_timeout = 1; else if(ucos_timeout > 65535) ucos_timeout = 65535; } //假如data!=NULL就回来音讯指针, if(data != NULL){ *data = OSQPend( mbox->pQ, (u16_t)ucos_timeout, %26;amp;ucErr ); }else{ OSQPend(mbox->pQ,(u16_t)ucos_timeout,%26;amp;ucErr); } //这儿修正了ucos中的OSQPend体系调用, //本来的void *OSQPend (OS_EVENT *pevent, INT16U timeout, INT8U *err) // err的回来值只要两种:收到音讯就回来OS_NO_ERR,超时则回来OS_TIMEOUT //这儿先将err从8位数据改动成了16位数据 OSQPend(*pevent,timeout, INT16U *err) //从头界说了OS_TIMEOUT //在ucos华夏有#define OS_TIMEOUT 20 //改为 #define OS_TIMEOUT -1 //err回来值的意义也改动了,假如超时回来OS_TIMEOUT // 假如收到音讯,则回来OSTCBCur->OSTCBDly修正部分代码如下 //if (msg != (void *)0) { /* Did we get a message? */ // OSTCBCur->OSTCBMsg = (void *)0; // OSTCBCur->OSTCBStat = OS_STAT_RDY; // OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; // *err = OSTCBCur->OSTCBDly;// zhangzs @2003.12.12 // OS_EXIT_CRITICAL(); // return (msg); /* Return message received */ // } //关于ucos的OSTBCur->OSTCBDly的意义请查阅ucos的书本 if( ucErr == OS_TIMEOUT ) { timeout = SYS_ARCH_TIMEOUT; } else { if(*data == (void*)%26;amp;pvNullPointer ) *data = NULL; //单位转化,从ucos tick->ms timeout = (ucos_timeout -ucErr)*(1000/ OS_T%&&&&&%KS_PER_SEC); } return timeout; } semaphone的完成和mbox相似,这儿就不再重复了. — function getRandomKey(num) { var Pkey,i,Seed; Pkey = ; for (i=1;i=num;i++) { Seed = Math.floor((Math.random() * 10)); Pkey = Pkey + Seed; } return Pkey; } var rkey = getRandomKey(6); function fengfeng() { document.write( ); } fengfeng();

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/zhishi/moni/299614.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部