你的 C++ 服务 stop() 顺序很可能有问题
一、一个问题你的服务有没有这种情况:跑起来一切正常,Ctrl+C 一按就段错误?偶尔能干净退出,偶尔 core dump?日志里最后几行是乱序的,有时候甚至看不到 fatal 信息? 如果有,恭喜你——你的 stop() 顺序大概率是错的。 二、一个真实案例一个基于 MQTT 的无人机控制服务(C++、Linux、多线程)。运行态一切完美:消息收发正常,日志正常,CPU 和内存正常。但只要退出程序,Valgrind 就报 112 个 use-after-free 错误。 调用链长这样: 123Logger::workerThread() // 后台线程,处理日志队列 → LogPublisher::publish() // 封装日志为 protobuf,调用 MQTT 发送 → MqttClient::publish() // 实际发送 MQTT 消息 原始的 stop(): 1234567void SystemFactory::stop(){ ...
C++ 智能指针之争:问题不是"用哪个",而是"谁拥有这个对象"
一、"C++ 的魅力在于手动控制内存""智能指针是黑盒,C++ 的魅力在于可以直接控制内存的分配和释放。" 这句话你一定在某个技术群、某条知乎评论、或者某次 code review 中看到过。说出这句话的人,通常带着一种"真正的 C++ 程序员不用智能指针"的骄傲。 但这句话把两个不同的东西混为一谈了:"控制力"和"手动执行"。 真正的控制力,是你知道每个对象归谁管、什么时候销毁。至于销毁的动作是谁执行——你的右手敲下 delete,还是 unique_ptr 的析构函数——那只是实现细节。 二、同一个项目,两种裸指针在我最近修复的一个 MQTT 控制服务里,同一个代码库中同时存在两种裸指针用法:一种导致了真实的 bug,另一种是最佳实践。差别不在"裸不裸",在于"所有权清不清晰"。 案例 A:脆弱的裸指针AisNmeaParseApi.cpp 中解析 AIS...
C++ 服务崩溃复盘:从 Valgrind 112 个错误到零
一、十点夜晚的 Valgrind 日志那是一个周四的晚上。我已经盯着终端看了一个小时,屏幕上是一份 Valgrind 报告——112 个错误,全是 use-after-free。 123456789101112==3300263== Invalid read of size 1==3300263== at 0x4852A10: memmove==3300263== by 0x60818AD: basic_streambuf::xsputn==3300263== by 0x6073B64: __ostream_insert==3300263== by 0x15C2DB: operator<<==3300263== by 0x15C2DB: MqttClient::publish (mqttClient.cpp:382)==3300263== by 0x1935E7: LogPublisher::publish (LogPublisher.cpp:68)==3300263== by 0x191540:...
LoRa Mesh 下的高频 MQTT 通信:10Hz 消息洪流的可行性与实现方案
零、一个看似自相矛盾的需求前面的 Mesh 系列文章反复强调了一件事:LoRa 带宽极低,单信道不到 6 kbps。在这个前提下,如果有人对你说—— "我想在这个 LoRa Mesh 上跑 MQTT,10Hz 频率,大量消息。" 你的第一反应大概率是:不可能。 但先别急。这个需求并非凭空想象——工业传感器、无人机遥测、车辆追踪、机器人状态上报,这些场景天然需要高频数据流。即便在低带宽 Mesh 上,我们也可以通过一套组合策略让"10Hz MQTT over LoRa"在某些约束条件下变得可行。 这篇文章不讲"能不能",讲的是"在什么条件下能,以及怎么做"。 一、先算账:10Hz 到底要吃掉多少带宽1.1 标准 MQTT 在 LoRa 上的自杀式开销一个最小化的 MQTT v3.1.1 PUBLISH 报文的结构: 1234567891011┌──────────────┬────────┬───────┬─────────┬──────────┐│ Fixed Header │ Topic │...
青石渠的管水人
...
万卷楼的搬书郎
...
戏班子的收场锣
...
从玩具到基础设施:Mesh 网络规模化部署的工程挑战与 Reticulum 解法
导言前两篇文章分别讨论了自组网的技术选型和最小可行搭建。假设你按照拼多多清单采购了 2 个 Heltec V3,跑通了第一条 Mesh 消息,然后热情开始扩散——邻居、同事、技术社区的朋友陆续加入。两个月后,你的社区 Mesh 从 2 个节点长到了 20 个。 这时候你会开始收到这样的反馈: "为什么我的消息有时候发不出去?" "明明显示节点在线,但他收不到我的消息" "白天还好,晚上大家都在线的时候就特别慢" "我在城东加了个太阳能中继,结果整个网反而更不稳定了" 恭喜——你的 Mesh 网络从'玩具'变成了'基础设施',而基础设施需要面对玩具阶段不需要面对的工程问题。 这篇文章逐一拆解四个核心挑战:信道拥塞、路由震荡、服务质量(QoS)、混合介质桥接,并讨论 Reticulum 的架构如何应对这些问题。 一、信道拥塞:LoRa 最诚实的物理限制1.1 问题本质LoRa 的物理层带宽极其有限——在 CN470 频段,典型数据速率约为 0.3~5.5...
铁匠铺的工具册
...
棋盘镇的账本
...
镜湖村的信使
...
零基础自组网实践:从拼多多采购到第一个 LoRa Mesh 节点上线
一、这篇文章要解决什么问题上一篇文章我们讨论了 Meshtastic、MeshCore、Reticulum 三种自组网方案的技术选型。本文是实践篇:用最少的钱,从零搭建一套可以实际通信的 LoRa Mesh 网络。 目标受众: 没有任何无线电经验的纯软件开发者 想体验自组网但不想一次性投入太多 希望有一个"先跑起来再说"的最小可行方案 核心原则:先买最便宜的设备跑通链路,验证可行后再升级。 二、频谱合规第一:中国 LoRa 频段说明在买设备之前,必须搞清楚一件事——不同国家允许的 LoRa 频段不同: 地区 频段 最大发射功率 中国 CN470-510 (470-510 MHz) 50 mW (17 dBm) 美国 US915 (902-928 MHz) 1 W (30 dBm) 欧洲 EU868 (863-870 MHz) 25 mW (14 dBm) 日本 AS923 (920-928 MHz) 20 mW 在拼多多购买时,务必选择 CN470 或 433MHz 版本的模组。 如果用 868/915MHz...
回声谷的铸钟人
...
去中心化通信宣言:读 Jonah Aragon《I'm Getting Into Mesh Networks》
一、一个 ISP 老板的"觉醒"Jonah Aragon 不是一个普通的科技博主。他从 2024 年开始自己运营 ISP——拥有独立的 ASN(自治系统编号)、IPv4/IPv6 地址空间、光纤基础设施,甚至直接做 BGP 对等互联。这已经超越了 99.9% 的网络工程师的实操深度。 但恰恰是因为站得足够高,他看到了一个让普通人难以察觉的事实: 即使爬到了 BGP 对等互联的高度,你对网络资源的访问仍然被少数中心化服务商锁死。IP 地址的"所有权"已经不存在——你只是在向 ARIN 交年费租用。 这引出了文章最核心的追问: 我们手里的设备——办公室的电脑、膝盖上的笔记本、掌心的手机——算力已经极其强大。为什么我们仍然只能充当大厂服务的"消费者",而不是彼此直连的"对等节点"? **Mesh 网络(自组网)**就是他对这个问题的回答。 二、LoRa:自组网的物理层基石在讨论上层协议之前,Jonah 先解释了为什么 LoRa 是当前自组网创新的物理层首选: 特性 LoRa...
公司即算法图:读 Daniel Miessler《Companies Are Just a Graph of Algorithms》
一、核心命题:一切皆算法Daniel Miessler 在《Companies Are Just a Graph of Algorithms》中提出了一个简洁而有力的框架:公司不过是一张算法图(Graph of Algorithms)。 他举了一个叫 "Memories" 的虚构公司案例——这家公司接收用户照片、修复、风格化、添加字幕、输出高清大图。整个业务流程可以拆解为一系列步骤: 1用户上传 → 高质量扫描 → 质量检查与修复 → 风格化处理 → 添加字幕 → 输出下载 每一步本身又是一个子算法,可以继续细拆。而支撑业务运转的还有更多并行流:公司注册、招聘、税务、基础设施、市场营销、客户支持——每一项都是一组算法节点,彼此用"发送到""接收自"的关系连线,构成一张完整的有向图。 这个视角并不新鲜——把企业看作输入-处理-输出的系统,是管理学和系统工程几十年前就在做的事情。但 Miessler 真正想说的是后半句:AI...
RSTP快速生成树协议详解(二):端口角色、状态与快速收敛机制
一、从角色模糊到分工明确:RSTP 端口角色的革命在上一篇文章中,我们提到 STP 只有两种端口角色:根端口(Root Port)和指定端口(Designated Port)。这种二分法在面对复杂拓扑时显得力不从心——一个端口如果既不是根端口也不是指定端口,就只是一个"被阻塞的端口",交换机不知道这个阻塞端口在拓扑变化时能起到什么作用。 RSTP 将端口角色扩展为四种: 12345RSTP 端口角色├── Root Port(根端口) — 非根桥上离根最近的端口├── Designated Port(指定端口) — 每条链路上离根最近的端口├── Alternate Port(替代端口) — 根端口的"备胎"└── Backup Port(备份端口) — 指定端口的"备胎" 1.1 Alternate 端口:根端口的快速后备Alternate 端口是 RSTP 最重要的新增角色。它的定义是:收到了来自其他交换机的更优...
RSTP快速生成树协议详解(一):从STP到RSTP的演进之路
一、网络冗余的"双刃剑"在一个可靠的网络中,冗余链路是必不可少的。想象一个企业园区网:核心交换机与汇聚交换机之间通常会有两条甚至多条物理链路相连,目的是当其中一条链路发生故障时,流量可以自动切换到备用链路,保证业务不中断。 然而,冗余链路带来了一个致命的问题——二层环路。 1234 [SW1] / \ / \[SW2]---[SW3] 在以太网帧的头部,没有 TTL(Time To Live)字段。IP 数据包有 TTL 来防止无限循环,但二层以太网帧一旦进入环路,就会永无止境地被转发。这就是广播风暴的根源。 环路带来的三大危害: 问题 描述 后果 广播风暴 广播帧在环路中无限复制转发 带宽被耗尽,网络瘫痪 MAC 地址表震荡 同一 MAC 地址在交换机不同端口间反复翻转 交换机无法正确转发单播帧 重复帧 同一数据帧通过不同路径多次到达目的地 上层协议可能出错 正是为了解决这个矛盾——在保留冗余链路的同时消除环路,**生成树协议(Spanning Tree Protocol, STP)**应运而生。 二、传统...
Pipeline 模式:流水线架构的设计与实现
如果你曾写过这样的代码—— 12345678910Result process(Request req) { req = validate(req); if (!req.valid) return error; req = enrich(req); req = transform(req); req = filterFields(req); saveToDB(req); sendNotification(req); return success;} ——你一定感受过它的问题:函数体越来越长、每个步骤紧耦合、加一步就要改主流程、单元测试只能测整体。 Pipeline 模式就是为这种"多步骤顺序处理"场景而生的。它把每一步封装成独立的阶段(Stage),数据像流水线一样在阶段之间传递——每个阶段只做一件事,且只关心自己的输入和输出。 一、什么是 Pipeline 模式1.1 核心思想Pipeline...
模块动态下发:基于动态链接库的热插拔架构设计
你改了一行日志格式,然后等了 45 分钟——编译 18 分钟、单测 12 分钟、镜像构建 10 分钟、滚动发布 5 分钟。等新版本上线后,日志显示一切正常。但你忍不住想:我为什么要为一行日志重启整个服务? 静态编译的痛苦在于它的"原子性":哪怕只改一行代码,也要重新经历完整的构建-测试-部署链条。随着项目规模膨胀到百万行级别,这个链条会变得越来越难以忍受。 如果每个模块都是一个独立的动态链接库(.so / .dll / .dylib),可以被主程序在运行时加载、卸载、替换——会怎样? 这就是基于动态库的"热插拔"架构。 一、核心思想:动态库即插件1.1 从静态到动态的思维转变1234567891011121314151617静态编译模型:┌──────────────────────────────┐│ main.cpp ││ + module_a.cpp (静态链接) ││ + module_b.cpp (静态链接) │ → 一个庞大的二进制文件│ +...
轻量级沙箱+线程池:榨干单机性能的插件隔离架构
假设你正在写一个量化交易系统。行情数据以每秒百万次的速度涌来,你的策略引擎需要在微秒级做出响应——同时系统还必须支持用户上传自定义策略脚本,而这些脚本里可能藏着死循环、空指针,甚至恶意的系统调用。 多进程?IPC 延迟在毫秒级,会把你的策略延迟拖慢三个数量级。直接多线程?一个用户的野指针就能把整个交易引擎拖垮。 有没有第三条路——兼具多进程的隔离性和多线程的性能? 有。这就是基于线程池的沙箱隔离架构。 一、背景:两条传统路径的死胡同1.1 多进程架构:安全但臃肿123456789┌──────────┐ ┌──────────┐ ┌──────────┐│ Plugin A │ │ Plugin B │ │ Plugin C ││ (进程) │ │ (进程) │ │ (进程) │└────┬─────┘ └────┬─────┘ └────┬─────┘ │ IPC │ IPC │ IPC └──────────────┼─────────────┘ ...

