新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > 实时操作系统uC/0S II下TCP/IP协议栈的实现

实时操作系统uC/0S II下TCP/IP协议栈的实现

作者:时间:2012-08-21来源:网络收藏

sys_mbox_new() //创建一个消息队列

sys_mbox_free() //释放一个消息队列

sys_mbox_post() //向消息队列发送消息

sys_arch_mbox_fetch() //从消息队列中获取消息

同样了消息队列结构OSQ及其操作,但是没有对消息队列中的消息进行管理,因此不能直接使用,必须在的基础上重新。为了对消息的管理,我们定义了以下结构:

typedef struct {

OS_EVENT* pQ;

void* pvQEntries[MAX_QUEUE_ENTRIES];

} sys_mbox_t;

在以上结构中,包括OS_EVENT类型的队列指针(pQ)和队列内的消息(pvQEntries)两部分,对队列本身的管理利用uC/0SII自己的OSQ操作完成,然后使用uC/0SII中的内存管理模块实现对消息的创建、使用、删除回收,两部分综合起来形成了LwIP的消息队列功能。

(3) sys_arch_timeout 函数

LwIP中每个与外界网络连接的线程都有自己的timeout属性,即等待超时时间。这个属性表现为每个线程都对应一个sys_timeout结构体队列,包括这个线程的timeout时间长度,以及超时后应调用的timeout函数,该函数会做一些释放连接,回收资源的工作。如果一个线程对应的sys_timeout为空(NULL),说明该线程对连接做永久的等待。

timeout结构体已经由LwIP自己在sys.h中定义好了,而且对结构体队列的数据操作也由LwIP负责,我们所要实现的是如下函数:

struct sys_timeouts * sys_arch_timeouts(void)

这个函数的功能是返回目前正处于运行态的线程所对应的timeout队列指针。timeout队列属于线程的属性,因此是OS相关的函数,只能由用户实现。

(4) sys_thread_new 创建新线程

LwIP可以是单线程运行,即只有一个tcpip线程(tcpip_thread),负责处理所有的tcp/ucp连接,各种网络程序都通过tcpip线程与网络交互。但LwIP也可以多线程运行,以提高效率,降低编程复杂度。这时就需要用户实现创建新线程的函数:

void sys_thread_new(void (* thread)(void *arg), void *arg);

在uC/0S II中,没有线程(thread)的概念,只有任务(Task)。它已经提供了创建新任务的系统API调用OSTaskCreate,因此只要把OSTaskCreate封装一下,就可以实现sys_thread_new。需要注意的是LwIP中的thread并没有uC/0S II中优先级的概念,实现时要由用户事先为LwIP中创建的线程分配好优先级。

4.4 lib_arch中库函数的实现

LwIP栈中用到了8个外部函数,这些函数通常与用户使用的系统或编译器有关,因此留给用户自己实现。如下:

u16_t htONs(u16_t n); //16位数据高低字节交换

u16_t ntohs(u16_t n);

u32_t htonl(u32_t n); //32位数据大小头对调

u32_t ntohl(u32_t n);

int strlen(const char *str); //返回字符串长度

int strncmp(const char *str1, const char *str2, int len); //字符串比较

void bcopy(const void *src, void *dest, int len); //内存数据块之间的互相拷贝

void bzero(void *data, int n); //内存中指定长度的数据块清零

前四个函数通常由用户自己实现。Skyeye(ARM7)中,由于使用了gcc编译器,gcc的lib库里已经有了后四个函数。而ez80的编译器函数库中缺少bcopy和bzero两个,需要自己编写。用户在其它CPU上实现时应根据自己的编译器来决定。

4.5 网络设备驱动程序

ez80开发板自带的网络芯片为RealTek的8019as芯片,这是ISA 10BASE-T的以太网芯片,与Ne2k兼容。而我们在AT91模拟器Skyeye中所仿真的网络芯片也是Ne2k,所以目前实现的网络设备驱动是针对Ne2k的,其它类型的网络芯片驱动可以在LwIP的网站上找到。LwIP的网络驱动有一定的模型,/src/netif/ethernetif.c 文件即为驱动的模板,用户为自己的网络设备实现驱动时应参照此模板。

在LwIP中可以有多个网络接口,每个网络接口都对应了一个struct netif,这个netif包含了相应网络接口的属性、收发函数。LwIP调用netif的方法netif->input()及netif->output()进行以太网packet的收、发等操作。在驱动中主要做的,就是实现网络接口的收、发、初始化以及中断处理函数。驱动程序工作在IP模型的网络接口层,它提供给上层(IP层)的接口函数如下:

//网卡初始化函数

void ethernetif_init(struct netif *netif)

//网卡接收函数,从网络接口接收以太网数据包并把其中的IP报文向IP层发送

//在中断方式下由网卡ISR调用

void ethernetif_input(struct netif *netif)

//网卡发送函数,给IP层传过来的IP报文加上以太网包头并通过网络接口发送

err_t ethernetif_output(struct netif *netif, struct pbuf *p, struct ip_addr *ipaddr)

//网卡中断处理函数ISR

void ethernetif_isr(void);

以上的函数都可以分为栈本身的处理和对网络接口硬件的操作两部份,但硬件操作是对上层屏蔽的,具体参见RTL8019as、DM9008等Ne2k网络芯片的数据手册。驱动程序可以到Skyeye或LwIP的网站下载。

5 应用实例的建立和测试

做完上面的移植修改工作以后,就可以在uC/0SII中初始化LwIP,并创建TCP或UDP任务进行测试了。这部份完全是C语言的实现,因此这部份在ez80和ARM7上基本都是一样的。值得注意的是LwIP的初始化必须在uC/0SII完全启动之后也就是在任务中进行,因为它的初始化用到了信号量等OS相关的操作。关键部份的代码和说明如下:

main(){

OSInit();

OSTaskCreate(lwip_init_task, LineNo11, lwip_init_stk[TASK_STK_SIZE-1], 0);

OSTaskCreate(usr_task,LineNo12,usr_stk[TASK_STK_SIZE-1],1);

OSStart();

}

主程序中创建了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线程来与外界通信,这样做的好处是编程简单,结构清晰。

linux操作系统文章专题:linux操作系统详解(linux不再难懂)

c语言相关文章:c语言教程


linux相关文章:linux教程


tcp/ip相关文章:tcp/ip是什么




评论


相关推荐

技术专区

关闭