• 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这个宏用于启用协议栈锁定功能,是通过互斥量实现的,要求互斥量具备优先级继承机制。使能后,在 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 */
    
    • 1
    • 2
    • 3
    • 4

    在函数 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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    使用宏做了一层包装

    #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 */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    函数或宏

    互斥量

    struct _sys_mut {
      void *mut;
    };
    typedef struct _sys_mut sys_mutex_t;
    
    • 1
    • 2
    • 3
    • 4
    名称功能
    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;
    
    • 1
    • 2
    • 3
    • 4
    名称功能
    sys_sem_valid(sema)宏,判断信号量是否有效
    sys_sem_set_invalid(sema)宏,将一个信号量设置为无效
    err_t sys_sem_new(sys_sem_t *sem, u8_t initial_count)函数,创建信号量
    sem 指向新的信号量
    initial_count01 ,指定信号量的初始状态。为 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;
    
    • 1
    • 2
    • 3
    • 4
    名称功能
    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 进程
    在操作系统中,lwIP 被封装到一个名为 tcpip_thread 的进程中。在函数 tcpip_init 中,会创建这个进程:

    sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
    
    • 1

    TCPIP_THREAD_STACKSIZETCPIP_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);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
  • 相关阅读:
    linux C语言 socket的server、client 实现
    Ubuntu20运行SegNeXt代码提取道路水体(五)——使用SegNeXt跑自己的数据集结果及分析
    [附源码]计算机毕业设计JAVA小区宠物管理系统
    作用域理解
    Linux安装JDK最新版
    深入理解 C++ 中的多态与文件操作
    每日OJ题_优先级队列_堆②_力扣703. 数据流中的第 K 大元素
    【Linux】《Linux命令行与shell脚本编程大全 (第4版) 》笔记-Chapter22-gawk 进阶
    基于JAVA疫情物质管理系统计算机毕业设计源码+系统+mysql数据库+lw文档+部署
    银行业数据治理的实施路径——基于资产管理的视角
  • 原文地址:https://blog.csdn.net/zhzht19861011/article/details/125993458