LWIP专区

格芯单片机LWIP协议栈笔记(5)---操作系统封装层

发布日期:2011/12/30

1.1 什么是操作系统封装层

    我们前面已经讲到,lwip3API接口,其中RAM API是不需要操作系统的支持,这样可以在不跑系统的情况下使用lwip。而LWIP API BSD API需要操作系统的支持。

    所以要实现LWIP API BSD API需要如下几个条件:

    1. 嵌入式系统,需要支持任务,信号量和邮箱

    2. 需要增加一个叫做操作系统封装层的中间层,也有人叫操作系统模拟层。

 

    什么是操作系统封装层(sys_arch)呢?

    Lwip为了适用不同的操作系统,在代码中没有使用和某一个操作系统相关的系统调用和数据结构,而是在Lwip和操作系统之间增加了一个操作系统封装层,也叫操作系统模拟层。操作系统封装层为操作系统服务(定时,进程同步,消息传递)提供了一个统一的接口。在Lwip中进程同步使用信号量(semaphone)和邮箱队列(mbox)

 

    操作系统封装层需要嵌入式能够实现多线程,信号量和邮箱。

1.2 操作系统封装层的实现

    lwip 预留了sys_arch.c, 操作系统封装层的函数需要在此文件中实现。

sys_arch.h文件中,我们指定数据类型“sys_sem_t”表示信号量,“sys_mbox_t”表示邮箱。sys_arch.c文件中,操作系统封装层需要实现如下功能函数。

1.2.1 void  sys_init(void)

    初始化sys_arch层。 

 

1.2.2 sys_sem_t  sys_sem_new(u8_t count) 

    建立并返回一个新的信号量。参数count指定信号量的初始状态。 

 

1.2.3 void  sys_sem_free(sys_sem_t sem)

    释放信号量。 

1.2.4 void  sys_sem_signal(sys_sem_t sem) 

    发送一个信号。 

 

1.2.5 u32_t  sys_arch_sem_wait(sys_sem_t sem, u32_t timeout)

    等待指定的信号并阻塞线程。

    timeout参数为0,线程会一直被阻塞至收到指定的信号;非0,则线程仅被阻塞至指定的timeout时间(单位为毫秒)。

    在timeout参数值非0的情况下,返回值为等待指定的信号所消耗的毫秒数。

    如果在指定的时间内并没有收到信号,返回值为 SYS_ARCH_TIMEOUT

    如果线程不必再等待这个信号(也就是说,已经收到信号),返回值也可以为0

    注意,LwIP实现了一个名称与之相似的函数来调用这个函数,sys_sem_wait(),注意区别。

 

1.2.6 sys_mbox_t  sys_mbox_new(void)

    建立一个空的邮箱。 

 

1.2.7 void  sys_mbox_free(sys_mbox_t mbox) 

    释放一个邮箱。

    如果释放时邮箱中还有消息,它表明LwIP中存在一个编程错误,应该通知开发者(原文如此,这句话很费解。个人理解的意思是:当执行sys_mbox_free()这个函数时,按道理邮箱中不应该再存在任何消息,如果用户使用LwIP时发现邮箱中还存在消息,说明LwIP的开发者存在一个编程错误,不能把邮箱中的消息全部取出并处理掉。

 

1.2.8 void  sys_mbox_post(sys_mbox_t mbox, void *msg)

    投递消息“msg”到指定的邮箱“mbox”。 

 

1.2.9 u32_t  sys_arch_mbox_fetch(sys_mbox_t mbox, void **msg, u32_t timeout) 

    阻塞线程直至邮箱收到至少一条消息。

    最长阻塞时间由timeout参数指定(与sys_arch_sem_wait()函数类似)。

    msg是一个结果参数,用来保存邮箱中的消息指针(即*msg = ptr),它的值由这个函数设置。“msg”参数有可能为空,这表明当前这条消息应该被丢弃。 

返回值与sys_arch_sem_wait()函数相同:等待的毫秒数或者SYS_ARCH_TIMEOUT――如果时间溢出的话。

    注意,LwIP实现的函数中,有一个名称与之相似的――sys_mbox_fetch(),注意区分。

 

1.2.10 struct  sys_timeouts  *sys_arch_timeouts(void) 

    返回一个指向当前线程使用的sys_timeouts结构的指针。

LwIP中,每一个线程都有一个timeouts链表,这个链表由sys_timeout结构组成,sys_timeouts结构则保存了指向这个链表的指针。这个函数由LwIP的超时调度程序调用,并且不能返回一个空(NULL)值。 

    单线程sys_arch实现中,这个函数只需简单返回一个指针即可。这个指针指向保存在sys_arch模块中的sys_timeouts全局变量。 

 

如果底层操作系统支持多线程并且LwIP中需要这样的功能,那么,下面的函数必须实现: 

1.2.11 sys_thread_t  sys_thread_new(void(*thread)(void *arg), void *arg, int prio) 

    启动一个由函数指针thread指定的新线程。

    arg将作为参数传递给thread()函数。

    prio指定这个新线程的优先级。

    返回值为这个新线程的IDID和优先级由底层操作系统决定。

 

1.2.12 sys_prot_t sys_arch_protect(void)

    这是一个可选函数,它负责完成临界区域保护并返回先前的保护状态。该函数只有在小的临界区域需要保护时才会被调用。

    基于ISR驱动的嵌入式系统可以通过禁止中断来实现这个函数。基于任务的系统可以通过互斥量或禁止任务来实现这个函数。

    该函数应该支持来自于同一个任务或中断的递归调用。换句话说,当该区域已经被保护,sys_arch_protect()函数依然能被调用。

    这时,函数的返回值会通知调用者该区域已经被保护。 如果你的移植正在支持一个操作系统,sys_arch_protect()函数仅仅是一个需要。

 

1.2.13 void sys_arch_unprotect(sys_prot_t pval)

    该函数同样是一个可选函数。它的功能就是恢复受保护区域的先前保护状态,先前是受到保护还是没有受到保护由参数pval指定。

    它与sys_arch_protect()函数配套使用,详细信息参看sys_arch_protect()函数。

 

1.3 操作系统封装层相关的头文件

1.3.1 cc.h

    与硬件平台及编译器相关的环境变量及数据类型声明文件。 

        LwIP使用的数据类型定义

        u8_t, s8_t, u16_ts16_tu32_ts32_tmem_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)提供的头文件――这个文件定义了标准的*nix错误编码,要么增加一条宏定义语句:#define LWIP_PROVIDE_ERRNO,这将使得lwip/arch.h头文件来定义这些编码。这些编码被用于LwIP的各个部分。

 

1.3.2 perf.h

    定义了性能测量使用的宏,由LwIP调用,可以将其定义为一个空的宏。 

    PERF_START  开始测量。 

    PERF_STOP(x)  结束测量并记录结果。

 

1.3.3 sys_arch.h 

    sys_arch.c的头文件。定义Arch(即整个移植所依赖的操作系统平台,译注)需要的数据类型:sys_sem_tsys_mbox_tsys_thread_t,以及可选类型:sys_prot_t

    sys_mbox_tsys_sem_t变量的NULL值定义: 

    SYS_MBOX_NULL  NULL

    SYS_SEM_NULL  NULL