lwIP更新记01:全局互斥锁替代消息机制

news2024/11/23 10:59:54

从 lwIP-2.0.0 开始,在 opt.h 中多了一个宏开关 LWIP_TCPIP_CORE_LOCKING,默认使能。这个宏用于启用 内核锁定 功能,使用 全局互斥锁 实现。在之前,lwIP 使用 消息机制 解决 lwIP 内核线程安全问题。消息机制易于实现,因为实现简单,更容易做到高稳定性。但使用消息机制效率低下,所以 lwIP 从 V2.0.0 开始,正式支持(并默认使用)全局互斥锁,同时仍保留消息机制。全局互斥锁方案实现复杂,但效率非常高。
本文探索全局互斥锁方案是怎么诞生并发展的。


2007 年 5 月 24 日,Simon Goldschmid (以下简称 西蒙 )提交了一个任务:task #6935(使用当前 socket/netconn API 需要解决的问题)。

西蒙提出,当前的 socket/netconn API 还存在某些设计问题需要改进,比如:

1.将消息机制改为互斥(锁定 netconns)

move from message passing to mutual exclusion (lock netconns)

这里需要解释一下 消息机制 是什么。

在使用操作系统的情况下,lwIP 内核作为操作系统的一个任务tchip_thread )运行,内核线程应用程序线程 间的通讯是通过消息机制来完成的。

在当时,距离发布 lwIP-1.3.0 版本还有 10 个月,在这个版本中,如果 应用程序线程 使用 sendto 函数发送 UDP 数据,这是一个复杂的过程。实际上应用程序线程无权发送数据,它会先构造一个称为 TCPIP_MSG_API 的消息,连带发送的数据信息,投递给 内核线程,由内核线程进行实际的数据发送:

请添加图片描述

从图中可以看出,一个普通的数据发送,需要两个线程协同、进行两次任务切换才能完成。多年以来,lwIP 对外声称都是”为了获得良好的性能,你应该使用 raw API“。这就是原因!因为线程间通讯低效造成的。

为什么要让内核线程完成实际的发送?直接在 应用程序线程 中发送不可以吗?

不可以!

这涉及到多年以来 lwIP 对外声称的另一个注意事项:“lwIP 不是线程安全的”!!!

从 lwip 诞生以来,直到最近发布的 lwIP-2.1.x,以及可以看到的将来,lwIP 都不是线程安全的

所以 lwIP 采用了一个迂回的策略,用来满足多线程编程:使用 并发设计 中的 队列模式

举一个很好理解的例子。

如果有两个任务使用同一个 串口 打印调试信息,任务 A 打印:

printf("Task A: ABCDEFG\n");

任务 B 打印:

printf("Task B: 1234567\n");

任务A正在打印的时候,可能被任务B打断,这时任务A的打印信息就会显得不完整,任务 A 打印的信息被任务 B 打印的信息分成两部分:

Task A: ABTask B: 1234567
CDEFG

出现这个问题的原因是有多个任务同时使用一个资源,那么让任务不去同时访问资源,更进一步,干脆让任务不访问资源不就解决了吗。队列模式 就是这个思路,即所有任务都不直接使用串口资源,而是将要打印的信息放到一个队列中,串口发送也独立出一个任务,该任务从队列中取出信息,打印信息。完美解决大家争资源的问题。

lwIP 处理 线程不安全 也是用的这个思路:创建一个邮箱队列,投递消息相当于入队,内核线程 从邮箱队列中取出消息,根据消息的指示做出相应的动作,所有 应用程序线程 都不访问 lwIP 内核,也就避免了线程不安全的问题。

除了一个问题外,效率低。

西蒙萌生出了改变这一现状的想法:

  • 对于发送,我们可以锁定整个核心,而不是将消息传递给另一个线程。这样 TX 可以优先处理。锁定互斥锁的运行时间不应比消息传递长多少。
  • 对于接收,同样可以锁定整个核心,这样 tchip_thread 只需要处理 RX定时器

随着讨论的进行,大家觉得使用全局互斥锁是个可行的方案。

Frédéric Bernon (以下简称 佛雷德里克)根据这个思路修改了代码,并进行了测试。他的测试代码是用 sendto 函数发送 1458 字节的数据(UDP 连接),测试结果如下:

Sequential API: lwip_sendto()raw API: udp_sendto
使用消息机制204 us74 us
使用全局互斥量67 us74 us

可以看出,使用全局互斥量后:

  • lwip_sendto 函数用时减少了 3 倍
  • 与 udp_sendto 函数执行时间相比,仅慢了 3us 。

可以说效果拔群。 佛雷德里克 是怎么做到的呢?原因在于他使用了全局互斥量让发送数据成了 原子操作 ,从而避免了线程间的切换,实现了在 应用程序线程 中调用 udp_sendto 函数直接完成数据的发送:

请添加图片描述

由于少了消息传递和任务切换的开销,速度自然也就快了起来。

这种使用互斥量保护共享资源的操作,使用了 并发设计 中的 守卫调用模式。如果多个线程以某种方式同时调用相互干扰的服务集合,可以使用守卫调用模式使得多个线程串行访问服务。最常见的实现方式是使用互斥量给共享资源加锁。

继续用上面的例子,有两个任务使用同一个 串口 打印调试信息,则可能出现打印乱套的问题。如果换一种方式,任务 A 在使用串口前,先把串口锁定,一旦锁定,除非任务 A 主动解锁,否则其它任务都不能使用串口。这样就能避免任务 A 正在使用串口打印信息时,被任务 B 打断的现象了。

2007 年 6 月 9 日, 佛雷德里克 将这个更改推送到了 lwIP 代码仓库,这是历史上第一次,宏 LWIP_TCPIP_CORE_LOCKING 出现在 lwIP 的源码中,默认设置为 0 ,即不启用该新功能。

但此时的代码还是实验性质的,仅供 lwIP 开发者测试用,在这个宏开关的注释中,有着明显的提示:

/* EXPERIMENTAL, Don't use it if you're not an active lwIP project member */

实验性质,如果你不是 lwIP 项目的开发人员,请不要使用它

之后,使用互斥量 锁定内核 的操作方式不断完善:

  • 2007 年 7 月底,API 消息(TCPIP_MSG_API)已经几乎完全可以使用锁定内核的方式替换掉,这包括连接、发送等操作
  • 2010 年 2 月份,西蒙 第一次引入宏 LWIP_TCPIP_CORE_LOCKING_INPUT,底层数据包输入消息(TCPIP_MSG_INPKT)也可以选择使用锁定内核的方式了。当然,这依旧是实验性质的。
  • 2016 年 3 月 17,终于, LWIP_TCPIP_CORE_LOCKINGLWIP_TCPIP_CORE_LOCKING_INPUT 不再是实验性质的,它们正式向用户开放了!从第一次讨论“将消息机制改为互斥”,到最终推出这个功能,用了将近 10 年!
  • 2016 年 7 月 1,宏 LWIP_TCPIP_CORE_LOCKING 的默认值修改为 1 ,这意味着解决 “lwIP 不是线程安全的”的方案, lwIP 开发者更推荐使用互斥锁。
  • 2016 年 11 月 10, lwIP-2.0.0 发布,在这个版本中,我们第一次可以放心使用互斥锁方案。

时代变了。“为了获得良好的性能,你应该使用 raw API” 这一印象,从 lwIP-2.0.0 开始,要改写了!从此,使用 Sequentialsocket API 也可以达到接近 raw API 的性能!

有关操作系统移植层的信息,可以参考我的这篇博文。






读后有收获,资助博主养娃 - 千金难买知识,但可以买好多奶粉 (〃‘▽’〃)
千金难买知识,但可以买好多奶粉


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/550062.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Linux安装Docker

Docker官网:Docker Docs: How to build, share, and run applications | Docker Documentation 学习任何技术,一定要参考相应的官网学习,一定要参考官网学习!!! 目录 一、环境准备 1.1 配置源 1.1.1 下载…

Vue+uniapp桃源婚恋交友APP 安卓小程序 nodejs java python

小程序框架:uniapp 小程序开发软件:HBuilder X 开发模式:混合开发本文先提出了开发基于小程序的桃源婚恋交友APP系统的背景意义,然后通过功能性和非功能性分析阐述本系统的需求,然后进行系统设计。技术实现部分选择Jav…

c++ 11标准模板(STL) std::set(九)

定义于头文件 <set> template< class Key, class Compare std::less<Key>, class Allocator std::allocator<Key> > class set;(1)namespace pmr { template <class Key, class Compare std::less<Key>> using se…

vue参照企业微信日程写一个小组件

今天公司要求做日程 这体的话 和企业微信的日程功能挺想的 也没有找到特别好的工具 就直接自己手敲了一个 先看效果 因为样式使用 sass 写的 所以 项目中要引入 sass 感兴趣的 可以把代码拿去二开一下 <template><section class "skeletonPositioning"…

Java线程概述 (一)线程介绍

文章目录 &#x1f412;个人主页&#x1f3c5;JavaSE系列专栏&#x1f4d6;前言&#xff1a;&#x1fa85;什么是程序 、进程、线程&#xff1f;&#x1fa85;线程的生命周期&#x1fa85;多线程&#x1fa85;守护者线程&#x1fa85;线程并行与并发&#x1fa85;死锁&#x1f…

机器学习项目实战-能源利用率 Part-4(模型构建)

博主前期相关的博客可见下&#xff1a; 机器学习项目实战-能源利用率 Part-1&#xff08;数据清洗&#xff09; 机器学习项目实战-能源利用率 Part-2&#xff08;探索性数据分析&#xff09; 机器学习项目实战-能源利用率 Part-3&#xff08;特征工程与特征筛选&#xff09; 这…

AList挂载工具安装搭建使用教程,快速访问多个网盘的资源(保姆级图文)

目录 1. 下载AList2. 命令行启动快速启动小技巧 3. 用户登录4. 添加阿里云网盘帐号5. 添加百度云网盘资源总结 欢迎关注 『发现你走远了』 博客&#xff0c;持续更新中 欢迎关注 『发现你走远了』 博客&#xff0c;持续更新中 软件功能&#xff1a;将多个网盘的资源聚合在一起&…

本地部署 privateGPT

本地部署 privateGPT 1. 什么是 privateGPT2. Github 地址3. 创建虚拟环境4. 部署 privateGPT5. 配置 .env6. 下载模型7. 将文件放入 source_documents 目录中8. 摄取所有数据9. 向本地文档提问 1. 什么是 privateGPT 利用 GPT 的强大功能&#xff0c;私密地与您的文档交互&am…

Flutter 笔记 | Flutter 中的路由、包、资源、异常和调试

路由管理 Flutter中的路由通俗的讲就是页面跳转。在Flutter中通过Navigator组件管理路由导航。并提供了管理堆栈的方法。如&#xff1a;Navigator.push和Navigator.pop Flutter中给我们提供了两种配置路由跳转的方式&#xff1a;1、基本路由&#xff0c; 2、命名路由 普通路…

详解c++STL—函数对象

目录 1、函数对象 1.1、函数对象概念 1.2、函数对象的使用 2、谓词 2.1、谓词概念 2.2、一元谓词 2.3、二元谓词 3、内建函数对象 3.1、理解内建函数对象 3.2、算术仿函数 3.3、关系仿函数 3.4、逻辑仿函数 1、函数对象 1.1、函数对象概念 概念&#xff1a; 重载…

数据结构第三天 【二叉搜索树】

这道题真是写的我想吐了&#xff0c;主要是函数太多&#xff0c;排错太难了&#xff0c;搞了两个小时&#xff0c;基本就是在排错&#xff0c;排了一个小时&#xff0c;后面自己心态也有点崩溃了&#xff0c;其实不是一道很难的题&#xff0c;但是是一个非常麻烦的题目&#xf…

使用Serv-U搭建FTP服务器并公网访问

文章目录 1. 前言2. 本地FTP搭建2.1 Serv-U下载和安装2.2 Serv-U共享网页测试2.3 Cpolar下载和安装 3. 本地FTP发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 转载自内网穿透工具的文章&#xff1a;使用Serv-U搭建FTP服务器并公网访问【内网穿透】_ 1. 前言…

linux专题:GDB详细调试方法与实现

系列文章目录 例如&#xff1a;第一章 Linux-GDB 调试实验的使用 文章目录 目录 系列文章目录 文章目录 一、实验目的 二、实验现象 三、实验准备 四、Linux GDB调试实验流程 五、Linux GDB 调试器 总结 一、实验目的 掌握使用 gcc 分步编译 c 代码为可执行程序步骤以及 gc…

【数学建模】步长的选择(优化建模)

人们每天都在行走&#xff0c;排除以运动健身为目的的走路方式&#xff0c;而仅仅考虑距离固定&#xff0c;以节省体力为最终目的的行走&#xff0c;那么选择多大的步长才最省力&#xff1f; 人在走路时所做的功等于抬高人体重心所需的势能与两腿运动所需的动能之和。在给定速度…

又到520了,来画一朵抽搐的玫瑰花吧

文章目录 静态的玫瑰 敲了这么多年代码&#xff0c;每年都得画一些心啊花啊什么的&#xff0c;所以现在常规的已经有些倦怠了&#xff0c;至少也得来个三维图形才看着比较合理&#xff0c;而且光是三维的也没啥意思&#xff0c;最好再加上能动起来。 静态的玫瑰 网上有很多生…

AIGC技术研究与应用 ---- 下一代人工智能:新范式!新生产力!(1-简介)

文章大纲 AI GC参考文献与学习路径模型进化券商研报陆奇演讲AI GC AI模型可大致分为决策式/分析式AI(Discriminant/Analytical AI)和生成式AI (Generative AI)两类。 决策式AI:学习数据中的条件概率分布,根据已有数据进行分析、判断、预测,主要应用模型有用于推荐系 统和…

Elasticsearch 集群部署插件管理及副本分片概念介绍

Elasticsearch 集群配置版本均为8以上 安装前准备 CPU 2C 内存4G或更多 操作系统: Ubuntu20.04,Ubuntu18.04,Rocky8.X,Centos 7.X 操作系统盘50G 主机名设置规则为nodeX.qingtong.org 生产环境建议准备单独的数据磁盘主机名 #各自服务器配置自己的主机名 hostnamectl set-ho…

chatgpt赋能Python-pythonf检验

Python的重要性与应用 Python是一种高级编程语言&#xff0c;因其简单易学和灵活性而备受欢迎。它已经成为数据分析、web开发、机器学习等许多领域的重要工具。在本篇文章中&#xff0c;我们将探讨Python在SEO中的作用。 Python对SEO的影响 SEO是搜索引擎优化的缩写&#xf…

【数据结构】线性表 ⑥ ( 双循环链表 | 双循环链表插入操作 | 双循环链表删除操作 | LinkedList 双循环链表源码分析 )

文章目录 一、双循环链表插入操作处理二、双循环链表删除操作处理三、LinkedList 双循环链表源码分析1、链表节点2、LinkedList 链表中收尾元素指针3、链表插入操作4、链表向指定位置插入操作5、获取指定索引的元素6、删除指定索引的元素 一、双循环链表插入操作处理 双循环链表…

【JVM】6. 堆

文章目录 6.1. 堆&#xff08;Heap&#xff09;的核心概述6.1.1. 堆内存细分6.1.2. 堆空间内部结构&#xff08;JDK7&#xff09;6.1.3. 堆空间内部结构&#xff08;JDK8&#xff09; 6.2. 设置堆内存大小与OOM6.2.1. 堆空间大小的设置6.2.2. OutOfMemory举例 6.3. 年轻代与老年…