lwIP 操作系统模拟层
Posted 研究是为了理解
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了lwIP 操作系统模拟层相关的知识,希望对你有一定的参考价值。
注1:除非特别说明,以下内容针对 lwIP 2.0.0 及以上版本。
注2:操作系统使用 FreeRTOS
contrib 2.1.0 给出了一个 lwIP 的操作系统模拟层,使用的操作系统为 FreeRTOS。模拟层位于:\\contrib-2_1_0\\ports\\freertos
。
宏 LWIP_TCPIP_CORE_LOCKING
定义在 opt.h
中:
/**
* LWIP_TCPIP_CORE_LOCKING
* Creates a global mutex that is held during TCPIP thread operations.
* Can be locked by client code to perform lwIP operations without changing
* into TCPIP thread using callbacks. See LOCK_TCPIP_CORE() and
* UNLOCK_TCPIP_CORE().
* Your system should provide mutexes supporting priority inversion to use this.
*/
#if !defined LWIP_TCPIP_CORE_LOCKING || defined __DOXYGEN__
#define LWIP_TCPIP_CORE_LOCKING 1
#endif
这个宏用于启用协议栈锁定功能,是通过互斥量实现的,要求互斥量具备优先级继承机制。使能后,在 tcpip.c
中定义全局变量:
#if LWIP_TCPIP_CORE_LOCKING
/** The global semaphore to lock the stack. */
sys_mutex_t lock_tcpip_core;
#endif /* LWIP_TCPIP_CORE_LOCKING */
在函数 tcpip_init
中会申请互斥量:
/**
* @ingroup lwip_os
* Initialize this module:
* - initialize all sub modules
* - start the tcpip_thread
*
* @param initfunc a function to call when tcpip_thread is running and finished initializing
* @param arg argument to pass to initfunc
*/
void
tcpip_init(tcpip_init_done_fn initfunc, void *arg)
lwip_init();
tcpip_init_done = initfunc;
tcpip_init_done_arg = arg;
if (sys_mbox_new(&tcpip_mbox, TCPIP_MBOX_SIZE) != ERR_OK)
LWIP_ASSERT("failed to create tcpip_thread mbox", 0);
#if LWIP_TCPIP_CORE_LOCKING
if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) // <--- 这里
LWIP_ASSERT("failed to create lock_tcpip_core", 0);
#endif /* LWIP_TCPIP_CORE_LOCKING */
sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
使用宏做了一层包装
#if LWIP_TCPIP_CORE_LOCKING
/** The global semaphore to lock the stack. */
extern sys_mutex_t lock_tcpip_core;
#if !defined LOCK_TCPIP_CORE || defined __DOXYGEN__
/** Lock lwIP core mutex (needs @ref LWIP_TCPIP_CORE_LOCKING 1) */
#define LOCK_TCPIP_CORE() sys_mutex_lock(&lock_tcpip_core) // <-- 这里
/** Unlock lwIP core mutex (needs @ref LWIP_TCPIP_CORE_LOCKING 1) */
#define UNLOCK_TCPIP_CORE() sys_mutex_unlock(&lock_tcpip_core) // <-- 这里
#endif /* LOCK_TCPIP_CORE */
#else /* LWIP_TCPIP_CORE_LOCKING */
#define LOCK_TCPIP_CORE()
#define UNLOCK_TCPIP_CORE()
#endif /* LWIP_TCPIP_CORE_LOCKING */
函数或宏
互斥量
struct _sys_mut
void *mut;
;
typedef struct _sys_mut sys_mutex_t;
名称 | 功能 |
---|---|
sys_mutex_valid (mutex) | 宏,判断互斥量是否有效 |
sys_mutex_set_invalid (mutex) | 宏,将一个互斥量设置为无效 |
err_t sys_mutex_new (sys_mutex_t *mutex) | 函数,创建互斥量 |
void sys_mutex_lock (sys_mutex_t *mutex) | 函数,阻塞进程,直到互斥量可用 |
void sys_mutex_unlock (sys_mutex_t *mutex) | 函数,释放先前使用 sys_mutex_lock 函数锁定的互斥量 |
void sys_mutex_free (sys_mutex_t *mutex) | 函数,释放互斥量资源 |
信号量
struct _sys_sem
void *sem;
;
typedef struct _sys_sem sys_sem_t;
名称 | 功能 |
---|---|
sys_sem_valid (sema) | 宏,判断信号量是否有效 |
sys_sem_set_invalid (sema) | 宏,将一个信号量设置为无效 |
err_t sys_sem_new (sys_sem_t *sem, u8_t initial_count) | 函数,创建信号量 由 sem 指向新的信号量initial_count 为 0 或 1 ,指定信号量的初始状态。为 1 表示信号量有效 |
void sys_sem_signal (sys_sem_t *sem) | 函数,释放信号量(发信号) |
void sys_arch_sem_wait (sys_sem_t *sem, u32_t timeout_ms) | 函数,阻塞线程直到一个信号量有效。timeout_ms 用于指定超时时间, = 0:表示无限阻塞,=其它:阻塞指定毫秒 |
void sys_sem_free (sys_sem_t *sem) | 函数,释放互斥量资源 |
邮箱
FreeRTOS 并没有邮箱机制,模拟层使用 队列
来模拟邮箱。
struct _sys_mbox
void *mbx;
;
typedef struct _sys_mbox sys_mbox_t;
名称 | 功能 |
---|---|
sys_mbox_valid (mbox) | 宏,判断邮箱是否有效 |
sys_mbox_set_invalid (mbox) | 宏,将一个邮箱设置为无效 |
err_t sys_mbox_new (sys_mbox_t *mbox, int size) | 函数,创建一个空的邮箱,最大可存放 size 个元素存储到邮箱的元素应为指针类型 你应该在 lwipopts.h 中定义 _MBOX_SIZE 来确定邮箱的个数 |
void sys_mbox_post (sys_mbox_t *mbox, void *msg) | 函数,向邮箱投递消息,如果邮箱满则无限阻塞,只能用于任务中。决不能在中断中调用。 |
err_t sys_mbox_trypost (sys_mbox_t *mbox, void *msg) | 函数,尝试向邮箱投递信息,如果邮箱满则返回错误 ERR_MEM |
err_t sys_mbox_trypost_fromisr (sys_mbox_t *mbox, void *msg) | 函数,尝试向邮箱投递信息,如果邮箱满则返回错误 ERR_MEM ,用于中断 |
u32_t sys_arch_mbox_fetch (sys_mbox_t *mbox, void **msg, u32_t timeout_ms) | 函数,阻塞线程直到一个消息有效。timeout_ms 用于指定超时时间, = 0:表示无限阻塞,=其它:阻塞指定毫秒msg 可以为 NULL ,表示丢弃掉这个消息返回 SYS_ARCH_TIMEOUT 表示发生超时 |
u32_t sys_arch_mbox_tryfetch (sys_mbox_t *mbox, void **msg) | 函数,接收一个消息,如果当前邮箱为空,则立即返回 SYS_MBOX_EMPTY 。成功返回 0 。 |
void sys_mbox_free (sys_mbox_t *mbox) | 函数,释放邮箱资源。 如果释放邮箱资源时,检测到邮箱内仍有消息,啧表明有了编程错误,应该设法通知开发者。 |
在 lwIP 中,上层 API 与协议栈内核之间的数据交互都是通过邮箱实现的。
在使用上层 API 时,API 需要调用内核相关的函数,信号量和互斥量为上层 API 与内核函数的执行提供了同步和互斥支持。
比如应用层调用数据发送 API 函数时,API 函数会阻塞在一个信号量上,同时请求内核进程 tcpip_thread
调用对应的内核数据发送函数,当内核函数执行完成后,释放信号量,这样原来阻塞的 API 函数得以继续执行。
lwIP 进程
在操作系统中,lwIP 被封装到一个名为 tcpip_thread
的进程中。在函数 tcpip_init
中,会创建这个进程:
sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
宏 TCPIP_THREAD_STACKSIZE
和 TCPIP_THREAD_PRIO
分别表示进程的堆栈空间和优先级,需要在 lwipopts.h
中定义。
tcpip_thread
进程会阻塞在一个邮箱上,等待消息,处理消息。这个邮箱内的消息可能来自于底层硬件驱动接收到的数据包,也可能来自上层应用程序的 API 调用。
/**
* The main lwIP thread. This thread has exclusive access to lwIP core functions
* (unless access to them is not locked). Other threads communicate with this
* thread using message boxes.
*
* It also starts all the timers to make sure they are running in the right
* thread context.
*
* @param arg unused argument
*/
static void
tcpip_thread(void *arg)
struct tcpip_msg *msg;
LWIP_MARK_TCPIP_THREAD();
LOCK_TCPIP_CORE();
if (tcpip_init_done != NULL)
tcpip_init_done(tcpip_init_done_arg);
while (1) /* MAIN Loop */
LWIP_TCPIP_THREAD_ALIVE();
/* wait for a message, timeouts are processed while waiting */
TCPIP_MBOX_FETCH(&tcpip_mbox, (void **)&msg);
if (msg == NULL)
LWIP_ASSERT("tcpip_thread: invalid message", 0);
continue;
tcpip_thread_handle_msg(msg);
头文件 lwipopts.h
#define NO_SYS 0
#define LWIP_SOCKET 1
#define LWIP_NETCONN 1
#define TCPIP_MBOX_SIZE
#define TCPIP_THREAD_STACKSIZE
#define TCPIP_THREAD_PRIO
以上是关于lwIP 操作系统模拟层的主要内容,如果未能解决你的问题,请参考以下文章
《嵌入式 - Lwip开发指南》第2章 LWIP开发环境简介