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

从 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 的性能!

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






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


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

winpcap 发包工具

本工具主要用来进行网络协议的调试,主要方法是,对现场数据抓包,然后将数据包带回交给开发人员,开发人员将该数据包重新发送和处理,模拟现场环境以便于调试和分析。 (一)使用方法 命令行下输入s…

Visual Studio插件DevExpress CodeRush v22.1- 支持C# 10

DevExpress CodeRush是一个强大的Visual Studio .NET 插件,它利用整合技术,通过促进开发者和团队效率来提升开发者体验。为Visual Studio IDE增压、消除重复的代码并提高代码质量,可以快速思考、自动化测试、可视化调试和重构。 CodeRush v2…

有了 IP 地址,为什么还要用 MAC 地址?

MAC地址等价于快递包裹上的收件人姓名。 MAC地址更多是用于确认对方信息而存在的。就如同快递跨越几个城市来到你面前,快递员需要和你确认一下收件人是否正确,才会把包裹交给你一样。 IP66在线查IP地址位置:https://www.ip66.net/?utm-sour…

软件设计师 数据库刷题项并包含知识点总结

**两级映像 有概念模式和内模式跟物理独立性相关,有外模式和概念模式跟逻辑独立性相关 ** 属性列就是RS共同拥有的ABC,一般去除后面的,所以就只有前面三个ABC,元组就是有没有自然连接成功的,就是R.AS.A R.BS.B… 选项里…

希望所有计算机专业同学都知道这些老师

C语言教程——翁凯老师、赫斌 翁恺老师是土生土长的浙大码农,从本科到博士都毕业于浙大计算机系,后来留校教书,一教就是20多年。 翁恺老师的c语言课程非常好,讲解特别有趣,很适合初学者学习。 郝斌老师的思路是以初学…

Linux-初学者系列6_kvm虚拟机

速通配置kvm虚拟机 通过虚拟化技术将一台计算机虚拟为多台逻辑计算机。 在一台计算机上同时运行多个逻辑计算机,每个逻辑计算机可以运行不同的操作系统,并且应用程序都可以在相互独立的空间内运行互不影响,提高计算机的工作效率。 一、 配置…

【STM32】STM32使用RFID读卡器

STM32使用RFID读卡器 RFID卡片 ID卡(身份标识):作用就是比如你要输入学号,你刷卡直接就相当于输入学号,省去了输入的过程 IC卡:集成电路卡,是将一种微电子芯片嵌入卡片之中 RFID的操作 1、…

设计模式之代理模式

文章目录 1、代理模式基本介绍2、Jdk中的动态代理2.1、场景推导2.2、Jdk动态代理 3、静态代理4、代理模式的关键点5、代理模式和适配器模式的比较6、代理模式UML图 1、代理模式基本介绍 代理模式的定义: 为其他对象提供一种代理以控制对这个对象的访问。在某些情况下&#xf…

I2C通信协议原理和MPU6050

一、串口通讯 只能在两个设备之间进行 若要三台设备两两通信,则每个设备得需要两组窗口,为3组相互独立的窗口通讯 为解决这个问题:设计了总线通讯,有多种,I2C为其中一种 二、I2C通信 (1&#…

VMware虚拟机三种网络模式详解之NAT(地址转换模式)

VMware虚拟机三种网络模式详解 NAT(地址转换模式) 二、NAT(地址转换模式) 刚刚我们说到,如果你的网络ip资源紧缺,但是你又希望你的虚拟机能够联网,这时候NAT模式是最好的选择。NAT模式借助虚拟…

进攻即是最好的防御!19个练习黑客技术的在线网站

前言 进攻即是最好的防御,这句话同样适用于信息安全的世界。这里罗列了19个合法的来练习黑客技术的网站,不管你是一名开发人员、安全工程师、代码审计师、渗透测试人员,通过不断的练习才能让你成为一个优秀安全研究人员。以下网站希望能给各…

Hadoop基础学习---5、MapReduce概述和WordCount实操(本地运行和集群运行)、Hadoop序列化

1、MapReduce概述 1.1 MapReduce定义 MapReduce是一个分布式运算程序的编程框架,是用户开发“基于Hadoop的数据分析应用”的核心框架。 MapReduce核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个Had…

【计算机系统基础bomb lab】CSAPP实验:Bomb Lab

【计算机系统基础bomb lab】CSAPP实验:Bomb Lab CSAPP 实验:Bomb Lab实验内容简述实验环境实验过程:phase 1phase 1 调试过程 实验过程:phase 2phase 2 调试过程 实验过程:phase 3phase 3 调试过程 实验过程&#xff1…

Java字符串知多少:String、StringBuffer、StringBuilder

一、String 1、简介 String 是 Java 中使用得最频繁的一个类了,不管是作为开发者的业务使用,还是一些系统级别的字符使用, String 都发挥着重要的作用。String 是不可变的、final的,不能被继承,且 Java 在运行时也保…

C++的cout详解

2023年5月20日,周六早上: 我发现我找不到非常详细的cout类的成员函数,只好自己写了。 不定期更新。 cout的继承关系 cout类继承自ostream类,ostream类继承自ios类,ios类继承自ios_base类 cout类拥有的所有成员函数 …

pg事务:事务的处理

事务的处理 事务块 从事务形态划分可分为隐式事务和显示事务。隐式事务是一个独立的SQL语句,执行完成后默认提交。显示事务需要显示声明一个事务,多个sql语句组合到一起称为一个事务块。 事务块通过begin,begin transaction,st…

QT学习记录(三)绘图

按照下面两个教程学习 QT学习教程(全面)_Strive--顾的博客-CSDN博客_qt学习 天山老妖S的博客_QT开发(3)_51CTO博客 1、绘图 VC项目右键增加QT GUI Class,在QT Designer中编辑DlgDraw.ui 在DlgDraw中重载函数 void DlgDraw::paintEvent(Q…

C++之堆排

堆排的原理和结构: 堆排序是一种常见的排序算法,基于堆这种数据结构实现。堆是一种特殊的树形数据结构,它满足以下两个条件: 堆是一棵完全二叉树。 堆的任意节点的值,都必须大于等于(或小于等于&#xff0…

基于ROS2的costmap中Obstacle Layer中对障碍物信息的增加与删除机制的方案调研。

文章目录 1.背景2.目标3. 障碍物信息添加方式发送数据的数据结构与接收数据的数据结构 4. 障碍物清理机制4.1 可调参数4.2 优化光追算法4.3 障碍物跟踪 1.背景 基于costmap地图,使用navigation导航时,会出现由于激光雷达/图像测距的局限性, …

由浅入深Netty粘包与半包解决方案

目录 1 粘包现象2 半包现象3 现象分析4 解决方案4.1 方法1:短链接4.2:方法2:固定长度4.3 方法3:固定分隔符4.4 方法4:预设长度 1 粘包现象 服务端代码 public class HelloWorldServer {static final Logger log Logg…