简介
- 记录一些C++零乱的知识点
微信文章 –
top
- 与进程相关的两个指标:VIRT Virtual Memory, 虚拟内存 和 RES Resident Memory,常驻内存,通常称为物理内存
- 虚拟内存,是指整个进程申请的内存,包括程序本身占用的内存,new或者malloc分配的内存等
- 物理内存,就是这个进程在主板上内存条中占用的内存
- 所以,通过top查看进程内存时,如果发现VIRT占用很大,说明这个程序用new或者malloc等分配了很多内存,但如果RES不是很大,那就不要慌,可能这只是程序的一个缓存优化,实际程序占用的物理内存并不大,但如果RES也很高,那可能就有点慌了
内存泄漏
- 内存泄漏是导致进程内存持续上涨最常见的原因,而这是C++中常见但不好处理的问题。
-
解决这个问题没有什么通用的快捷的办法,只能根据实际业务处理
- 第一,从业务上,能不能重现内存泄漏。
- 例如,做游戏,加入玩家不停地登录,就会导致内存不断上涨,那就说明问题就在登录游戏,把整个流程拆分,一个个屏蔽测试,最终找出问题
- 第二,从部署上,能不能定位内存泄漏。
- 例如,最近更新了一个版本,发现内存占用变得很高,那就可以确定,是这个版本的修改出现了问题,一个版本的代码量终究是有限的,查找起来也比较容易
-
第三,使用valgrind memcheck。如果能够复现内存泄漏,但无法定位是哪个逻辑,那就可以用valgrind memcheck。复现内存泄漏,这个通常比较难实现,一般是线下测试无法复现,线上用户量大,运行久了才会复现,而valgrind会导致程序运行很慢,无法支撑线上测试,因此这个选项不太适用于线上
-
第四,使用Visual Leak Detector.valgrind使linux下的,如果程序可以跨平台,或者只在windows下,可以试试这个
- 第五,重载new,delete。
- 可以简单加个计数,用于平时预防泄漏,也可更深入一些,记录内存的分配,得到内存泄漏的堆栈,但是这个是否能够支撑线上debug,不一定
- 第六,使用自己的内存分配函数,每一个内存分配,都是用自己的函数,每一个STL的容器,都传入自己的分配器,然后分别记录这些内存分配的大小。
- 这个方法看起来很不现实,但如果能够使用并实现,对内存统计,查找有很大的帮助,而且支持线上debug。
- 查找内存,只需要打印每个分配器分配的内存大小基本上可以得到结论是哪个分配器除了问题。
- 唯一的问题是它增加了开发难度,而且不能像valgrind那样不需要修改程序即可使用
- 第七,使用valgrind massif。valgrind memcheck需要复现内存泄漏,所以不容易找出问题。它会定时记录分配内存的各个堆栈以及分配内存的量,当出现内存泄漏时,根据分配内存的量检查各个堆栈,应该可以找到问题的。
- massif也会导致程序运行满,但比memcheck要快,能不能线上debug,这个依然需要看具体情况
- 第八,使用第三方内存分配器,例如jemalloc。并不是说使用第三方内存分配器就解决问题了,而是jemalloc自带了一大堆工具,其中jeprof可以得到内存的大小以及堆栈等信息,对查找内存泄漏有很大帮助
- 不过开启prof后,效率如何,能不能在线上使用,不一定
内存碎片
- 加入找不到内存泄漏,也许本来就没有内存泄漏,这时不妨考虑内存碎片的问题
- 以linux下的ptmalloc为例
内存利用率
- 如果一个进程占用的内存远高于预期,但没有持续上涨,还需要考虑下是不是内存使用率的问题。
- 当使用new分配一块内存时,系统需要为这次分配记录大小,地址,分配的内存也需要对其。
- 加入分配的内存很小,例如1b,那系统最终需要消耗的内存是远大于1b的
简介
- C++编译器
g++ -Werror=type-limits是什么
g++ -Werror=type-limits
是 GCC 编译器的一个编译选项,用于将与类型限制相关的警告转换为错误。这个选项告诉编译器将类型限制的警告视为编译错误,即在出现此类警告时,编译过程将中断,并生成一个错误,而不是继续生成可执行文件。
具体而言,-Werror=type-limits
用于处理与类型限制(type limits)相关的警告。例如,在比较有符号和无符号整数时,或者在进行其他可能导致溢出或意外行为的操作时,编译器可能会发出此类警告。通过将这些警告视为错误,开发者可以更加强制执行对于类型安全性的规则,从而避免一些潜在的运行时错误。
在实际的编译过程中,使用 -Werror=type-limits
可能会使得编译时更加严格,但也需要确保代码中不存在不希望的类型限制问题,否则编译将失败。
以下是使用这个编译选项的示例:
1
g++ -Werror=type-limits -o my_program my_program.cpp
这将编译名为 my_program.cpp
的源文件,并在编译时将类型限制的警告转换为错误。
简介
- TIPC协议笔记
linux tipc协议 详解
TIPC(Transparent Inter-Process Communication)是一种在 Linux 中用于进程间通信的协议。它允许在集群中的不同节点之间进行通信,提供了可靠的消息传递和服务发现机制。下面是 TIPC 协议的一些详细解释:
- 通信模式:
- 消息模式(Message Mode):在这种模式下,发送方发送消息给接收方,并等待确认。这种模式适用于需要确保消息到达并被处理的情况。
- 流模式(Stream Mode):与消息模式不同,流模式下发送方可以连续地向接收方发送数据,而不需要等待确认。这种模式适用于实时数据传输等场景。
- 消息类型:
- 点对点消息:消息从一个节点直接发送到另一个节点。
- 组播消息:消息被发送到一个特定的组,并被组中的所有节点接收。
-
服务发现:TIPC 提供了服务发现机制,允许节点在集群中发现和订阅特定的服务。这种机制可以使得节点能够动态地发现和连接到可用的服务。
-
路由机制:TIPC 使用一种分层的路由机制,以确保消息可以通过集群中的节点进行传递。这种机制使得 TIPC 能够在大规模集群中进行高效的通信。
-
配置和管理:TIPC 的配置和管理通常是通过一组命令行工具完成的,这些工具允许管理员配置节点的网络参数、查看节点状态等。
- 应用领域:TIPC 通常用于构建需要高可靠性和实时性的分布式系统,如电信网络、工业自动化等领域。
总的来说,TIPC 提供了一种可靠的、高性能的进程间通信机制,适用于构建各种分布式系统。
linux tipc头文件 详解
在 Linux 中,TIPC 的头文件通常位于 /usr/include/linux/tipc.h
。这个头文件包含了 TIPC 协议的相关定义、数据结构和函数声明,为开发者提供了使用 TIPC 的接口和工具。
以下是 /usr/include/linux/tipc.h
中可能包含的一些重要内容:
-
数据结构定义:TIPC 头文件包含了许多数据结构的定义,这些数据结构用于表示 TIPC 协议中的消息、节点信息、连接状态等。
-
常量定义:头文件中包含了一些常量的定义,用于表示 TIPC 协议中的各种参数和限制,如最大消息长度、最大节点数等。
-
函数声明:TIPC 头文件中还包含了一些函数的声明,这些函数提供了对 TIPC 协议的操作接口,包括消息的发送和接收、节点的注册和发现、连接的建立和管理等。
-
错误码定义:TIPC 头文件中可能还包含了一些错误码的定义,用于表示 TIPC 操作过程中可能发生的错误情况,方便开发者进行错误处理和调试。
-
其他相关定义:除了上述内容之外,TIPC 头文件还可能包含一些其他相关的定义,如事件类型、选项参数等。
开发者可以通过包含 /usr/include/linux/tipc.h
头文件来访问 TIPC 协议的相关接口和功能,从而在应用程序中实现进程间通信、服务发现等功能。
为什么要用内存池?
- 首先,在7 * 24h的服务器中如果不使用内存池,而使用malloc和free,那么就非常容易产生内存碎片,早晚都会申请内存失败,并且在比较复杂的代码中,非常容易出现内存泄漏
- 为了解决这两个问题,内存池就应运而生了.内存池预先分配一大块内存用来做一个内存池,业务中的内存分配和释放都由这个内存池来管理,内存池内的内存不足时其内部会自己申请.所以,内存碎片的问题就交由内存池的算法来优化,而内存泄漏的问题只需要遵守内存池提供的api,就非常容易避免内存泄漏了.
- 而且即使出现了内存泄漏,排查的思路也很清晰:
- 检查是不是内存池的问题
- 如果不是内存池的问题,就检查是不是第三方库的内存泄漏
内存池的使用场景:
- 全局内存池,
- 一个连接一个内存池
设计一个内存池
- 以4k为分界线,大于4k的认为是大块内存;小于4k的认为是小块内存;并且这里的4k,并不是严格遵照4096,而是在描述上,用4k比较好描述
- 在真正使用内存之前,内存池提前分配一定数量且大小相等的内存块以作备用,当真正被用户调用api分配内存的时候,直接从内存块中获取内存(指小块内存),当内存块不够用了,再由内存池去申请新的内存块;而如果是需要大块内存,则内存池直接申请大块内存再返回给用户.
- 内存池:就是将这些提前申请的内存块组织管理起来的数据结构. 内存池实现原理主要分为:分配,回收,扩容三个部分.
- 内存池原理之小块内存:
- 分配:
- 内存池预申请一块4k的内存块,这里称为block,即block=4k内存块.当用户向内存池申请内存的size小于4k时,内存池从block的空间中划分出去size空间,当再有新的申请时,再划分出去
- 扩容:
- 直到block中的剩余空间不足以分配size大小,那么此时内存池会再次申请一块block,再从新的block中划分size空间给用户
- 回收:
- 每一次申请小块内存,都会在对应的block中引用计数加1,每一次释放小块内存时,都会在block中引用计数减1,只有当引用计数为0的时候,才会回收block使他重新成为空闲空间,以便重复利用空间资源.
- 这样,内存池避免频繁向内核申请/释放内存,从而提高系统性能
- 分配:
- 内存池原理之大块内存:
- 分配:
- 因为大块内存是大于4k的,所以内存池不预先申请内存,也就是用户申请的时候,内存池再申请内存,然后响应用户申请的时候,将申请的内存资源返回给用户
- 扩容:
- 大块内存不存在扩容
- 分配:
简介
- C++适用的应用领域
实时数据处理
- 实时数据处理是指在数据流入时对其进行处理的数据处理方法。实时数据处理通常用于处理高速、大量数据,例如实时监控、社交媒体分析等。实时数据处理的主要优点是低延迟、高吞吐量,但缺点是系统复杂度较高。
简介
- 整理自己的技术栈
- C++是一门讲究深度的语言,其深度不是体现在会多少C++语法,而是能够洞察所写的C++代码背后的系统原理,这是需要长期不断的积累的,没有
常见知识点
- auto 关键字
- for-each 循环
- 右值及移动构造函数,std::forward,std::move,stl容器
- std::thread库
- std::chrono库
- 智能指针系列(std::shared_ptr/std::unique_ptr/std::weak_ptr),智能指针的实现原理
- 线程库std::thread + 线程同步技术库std::mutex/std::condition_variable/std::lock_guard等
- Lambda表达式
- std::bind/std::function库
- 关键字的用法(override, final, delete)
简介
- C++ 机器人控制系统相关笔记
C++ 机器人控制系统相关的C++库和框架
C++ 是一种通用编程语言,可以用于编写各种类型的应用程序,包括机器人控制系统。
机器人控制系统通常涉及硬件控制和数据处理两个方面。在 C++ 中,您可以使用特定的库和框架来实现这些功能。
以下是一些与机器人控制相关的 C++ 库和框架:
Robot Operating System (ROS):ROS 是一个用于构建机器人软件的开源框架。它提供了用于硬件抽象、设备驱动程序、库、可视化工具、消息传递和软件包管理的工具。
Robotics Library (RL):RL 是一个用于机器人控制和仿真的 C++ 库。它提供了用于运动规划、动力学仿真、机器人建模和感知的工具。
OpenCV:OpenCV 是一个开源的计算机视觉库,可用于机器人的视觉感知和图像处理。
Boost C++ 库:Boost 是一个 C++ 库集合,其中包括许多常用的数据结构、算法和工具,可用于机器人控制系统中的各种任务。
如果您想开始使用 C++ 开发机器人控制系统,建议您首先了解 C++ 的基础知识,然后学习上述库和框架的用法。这些库和框架都有详细的文档和示例代码可供参考。通过练习和实践,您可以逐步提高您的技能,并开发出更高级的机器人控制系统。
C++ 机器人控制系统相关的C++库和框架 详解
在机器人控制系统中,有许多C++库和框架可用于不同的任务,包括运动规划、传感器融合、模拟和仿真等。以下是一些常见的C++库和框架,它们在机器人控制领域具有广泛的应用:
- ROS (Robot Operating System):
- 简介: ROS是一个用于构建机器人软件的开源框架。它提供了一系列工具和库,用于传感器数据的处理、运动规划、通信和控制等方面。
- 特点: 灵活、模块化,支持多语言(包括C++)。
- Robotics Library (RL) - formerly known as SL:
- 简介: Robotics Library是一个用于机器人控制和仿真的C++库。它包括运动学、动力学、传感器处理和运动规划等功能。
- 特点: 面向多关节机器人,提供强大的建模和仿真工具。
- Orocos (Open Robot Control Software):
- 简介: Orocos是一个用于实时控制系统的框架,支持多种硬件平台。它包括组件化的体系结构,适用于工业和研究用途。
- 特点: 实时性能好,支持硬实时和软实时操作系统。
- MoveIt!:
- 简介: MoveIt!是一个用于运动规划的库,特别适用于机器人操作系统(ROS)环境。它支持各种运动规划算法和机器人硬件。
- 特点: 高度集成于ROS,易于使用,支持运动学和动力学规划。
- Eigen:
- 简介: Eigen是一个C++模板库,用于线性代数运算。在机器人控制中,它经常用于进行矩阵和向量运算,例如在运动学和动力学计算中。
- 特点: 高性能,使用模板元编程提高运行时性能。
- Gazebo:
- 简介: Gazebo是一个开源的机器人仿真器,用于模拟机器人在不同环境中的行为。它可以与ROS集成,提供真实感的物理仿真。
- 特点: 适用于仿真和测试,支持多种机器人模型。
这些库和框架在不同的方面提供了丰富的功能,选择取决于你的具体需求和项目要求。在使用这些工具时,请务必查看它们的文档以获取详细的信息和使用说明。
C++ 机器人仿真
在C++中进行机器人仿真涉及到使用适当的库和工具,以模拟机器人在虚拟环境中的行为。以下是一些用于C++机器人仿真的常见库和框架:
- Gazebo:
- 简介: Gazebo是一个功能强大的开源机器人仿真器,它可以用于模拟机器人的行为、传感器和环境。它提供了高度可定制的物理引擎,支持ROS集成。
- 特点: 物理精确度高,支持多机器人模型,提供多种传感器模拟。
- Webots:
- 简介: Webots是一个通用的机器人仿真平台,支持C++编程语言。它可以用于模拟各种机器人,包括地面和飞行器。
- 特点: 提供易于使用的用户界面,支持多种编程语言,有广泛的机器人模型和环境。
- V-REP (CoppeliaSim):
- 简介: V-REP(现在称为CoppeliaSim)是一个跨平台的仿真工具,支持C++编程。它提供了丰富的机器人模型、物理引擎和传感器模拟。
- 特点: 易于集成到自动化系统中,支持远程API调用,适用于各种机器人应用。
- Stage:
- 简介: Stage是ROS的一部分,是一个轻量级的2D机器人仿真器。它使用C++进行开发,适用于模拟地面机器人。
- 特点: 简单易用,适用于快速原型设计,支持多机器人仿真。
- Microsoft AirSim:
- 简介: AirSim是一个由Microsoft开发的开源机器人仿真工具,支持C++和其他编程语言。它专注于模拟无人机和汽车的行为。
- 特点: 高度可定制,支持多种传感器,包括RGB相机和深度相机。
在选择机器人仿真工具时,需要考虑仿真的准确性、易用性、物理引擎质量以及与其他工具和库的集成能力。具体的选择将取决于项目的需求和目标。
简介
- C++ 线程池的应用
boost.asio
- boost::asio::thread_pool类
- 结合mongoose,搭建异步http服务器,示例代码如下:
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
int http_server_with_mongoose() { struct HttpMessage { std::string path; std::string method; std::string body; std::string response_type; std::string response_body; }; class HttpWithMongoose { private: struct mg_mgr mgr; struct mg_connection *connect; boost::asio::thread_pool tp; public: HttpWithMongoose() : tp(8) { mg_mgr_init(&mgr); } virtual ~HttpWithMongoose() { mg_mgr_free(&mgr); } public: void Start() { connect = mg_http_listen(&mgr, "0.0.0.0:9999", handle_event, this); while (true) { mg_mgr_poll(&mgr, 50); } } private: void Dispath(HttpMessage& http_message) { LOG(INFO) << "path: " << http_message.path << "\n" << "method: " << http_message.method << "\n" << "body: " << http_message.body << "\n"; http_message.response_type = "Content-type: application/json"; nlohmann::json reply_data; reply_data["hello"] = "http with mongoose!"; http_message.response_body = reply_data.dump(); } private: static void handle_event(mg_connection *connect, int ev, void *ev_data, void *fn_data) { HttpWithMongoose* this_ptr = static_cast<HttpWithMongoose *>(fn_data); struct mg_http_message* hm = static_cast<struct mg_http_message*>(ev_data); switch (ev) { case MG_EV_HTTP_MSG: { HttpMessage http_message; http_message.path = std::string(hm->uri.ptr, hm->uri.len); http_message.method = std::string(hm->method.ptr, hm->method.len); http_message.body = std::string(hm->body.ptr, hm->body.len); // std::thread tmp_thread = std::thread([](HttpMessage http_message, HttpWithMongoose* this_ptr, struct mg_connection* connect) // { // this_ptr->Dispath(http_message); // mg_http_reply(connect, 200, http_message.response_type.c_str(), http_message.response_body.c_str()); // connect->is_draining = 1; // }, http_message, this_ptr, connect); // tmp_thread.detach(); auto dpcp = [](HttpMessage http_message, HttpWithMongoose* this_ptr, struct mg_connection* connect) { this_ptr->Dispath(http_message); mg_http_reply(connect, 200, http_message.response_type.c_str(), http_message.response_body.c_str()); connect->is_draining = 1; }; boost::asio::post(this_ptr->tp, std::bind(dpcp, http_message, this_ptr, connect)); break; } } } }; HttpWithMongoose http_server; http_server.Start(); return 0; }