资料目录
LWIP专区
格芯单片机LWIP协议栈笔记(5)---操作系统封装层
发布日期:2011/12/30
我们前面已经讲到,lwip有3中API接口,其中RAM API是不需要操作系统的支持,这样可以在不跑系统的情况下使用lwip。而LWIP API 和BSD API需要操作系统的支持。
所以要实现LWIP API 和BSD API需要如下几个条件:
1. 嵌入式系统,需要支持任务,信号量和邮箱
2. 需要增加一个叫做操作系统封装层的中间层,也有人叫操作系统模拟层。
什么是操作系统封装层(sys_arch)呢?
Lwip为了适用不同的操作系统,在代码中没有使用和某一个操作系统相关的系统调用和数据结构,而是在Lwip和操作系统之间增加了一个操作系统封装层,也叫操作系统模拟层。操作系统封装层为操作系统服务(定时,进程同步,消息传递)提供了一个统一的接口。在Lwip中进程同步使用信号量(semaphone)和邮箱队列(mbox)。
操作系统封装层需要嵌入式能够实现多线程,信号量和邮箱。
lwip 预留了sys_arch.c, 操作系统封装层的函数需要在此文件中实现。
在sys_arch.h文件中,我们指定数据类型“sys_sem_t”表示信号量,“sys_mbox_t”表示邮箱。sys_arch.c文件中,操作系统封装层需要实现如下功能函数。
初始化sys_arch层。
建立并返回一个新的信号量。参数count指定信号量的初始状态。
释放信号量。
发送一个信号。
等待指定的信号并阻塞线程。
timeout参数为0,线程会一直被阻塞至收到指定的信号;非0,则线程仅被阻塞至指定的timeout时间(单位为毫秒)。
在timeout参数值非0的情况下,返回值为等待指定的信号所消耗的毫秒数。
如果在指定的时间内并没有收到信号,返回值为 SYS_ARCH_TIMEOUT。
如果线程不必再等待这个信号(也就是说,已经收到信号),返回值也可以为0。
注意,LwIP实现了一个名称与之相似的函数来调用这个函数,sys_sem_wait(),注意区别。
建立一个空的邮箱。
释放一个邮箱。
如果释放时邮箱中还有消息,它表明LwIP中存在一个编程错误,应该通知开发者(原文如此,这句话很费解。个人理解的意思是:当执行sys_mbox_free()这个函数时,按道理邮箱中不应该再存在任何消息,如果用户使用LwIP时发现邮箱中还存在消息,说明LwIP的开发者存在一个编程错误,不能把邮箱中的消息全部取出并处理掉。
投递消息“msg”到指定的邮箱“mbox”。
阻塞线程直至邮箱收到至少一条消息。
最长阻塞时间由timeout参数指定(与sys_arch_sem_wait()函数类似)。
msg是一个结果参数,用来保存邮箱中的消息指针(即*msg = ptr),它的值由这个函数设置。“msg”参数有可能为空,这表明当前这条消息应该被丢弃。
返回值与sys_arch_sem_wait()函数相同:等待的毫秒数或者SYS_ARCH_TIMEOUT――如果时间溢出的话。
注意,LwIP实现的函数中,有一个名称与之相似的――sys_mbox_fetch(),注意区分。
返回一个指向当前线程使用的sys_timeouts结构的指针。
LwIP中,每一个线程都有一个timeouts链表,这个链表由sys_timeout结构组成,sys_timeouts结构则保存了指向这个链表的指针。这个函数由LwIP的超时调度程序调用,并且不能返回一个空(NULL)值。
单线程sys_arch实现中,这个函数只需简单返回一个指针即可。这个指针指向保存在sys_arch模块中的sys_timeouts全局变量。
如果底层操作系统支持多线程并且LwIP中需要这样的功能,那么,下面的函数必须实现:
启动一个由函数指针thread指定的新线程。
arg将作为参数传递给thread()函数。
prio指定这个新线程的优先级。
返回值为这个新线程的ID,ID和优先级由底层操作系统决定。
这是一个可选函数,它负责完成临界区域保护并返回先前的保护状态。该函数只有在小的临界区域需要保护时才会被调用。
基于ISR驱动的嵌入式系统可以通过禁止中断来实现这个函数。基于任务的系统可以通过互斥量或禁止任务来实现这个函数。
该函数应该支持来自于同一个任务或中断的递归调用。换句话说,当该区域已经被保护,sys_arch_protect()函数依然能被调用。
这时,函数的返回值会通知调用者该区域已经被保护。 如果你的移植正在支持一个操作系统,sys_arch_protect()函数仅仅是一个需要。
该函数同样是一个可选函数。它的功能就是恢复受保护区域的先前保护状态,先前是受到保护还是没有受到保护由参数pval指定。
它与sys_arch_protect()函数配套使用,详细信息参看sys_arch_protect()函数。
与硬件平台及编译器相关的环境变量及数据类型声明文件。
LwIP使用的数据类型定义
u8_t, s8_t, u16_t,s16_t,u32_t,s32_t,mem_ptr_t。
与编译器相关的LwIP结构体封装宏:
PACK_STRUCT_FIELD(x)
PACK_STRUCT_STRUCT
PACK_STRUCT_BEGIN
PACK_STRUCT_END
与平台相关的调试输出:
LWIP_PLATFORM_DIAG(X) 非故障,输出一条提示信息。
LWIP_PLATFORM_ASSERT(x) 故障,输出一条故障信息并放弃执行。
“轻便的(lightweight)”同步机制:
SYS_ARCH_DECL_PROTECT(x) 声明一个保护状态变量。
SYS_ARCH_PROTECT(x) 进入保护模式。
SYS_ARCH_UNPROTECT(x) 脱离保护模式。
如果编译器不提供memset()函数,这个文件必须包含它的定义,或者包含(include)一个定义它的文件。
这个文件要么包含一个本地系统(system-local)提供的头文件
定义了性能测量使用的宏,由LwIP调用,可以将其定义为一个空的宏。
PERF_START 开始测量。
PERF_STOP(x) 结束测量并记录结果。
sys_arch.c的头文件。定义Arch(即整个移植所依赖的操作系统平台,译注)需要的数据类型:sys_sem_t,sys_mbox_t,sys_thread_t,以及可选类型:sys_prot_t。
sys_mbox_t和sys_sem_t变量的NULL值定义:
SYS_MBOX_NULL NULL
SYS_SEM_NULL NULL