ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

libipc: 一款轻量级、跨平台的 C++ 进程间通信(IPC)库

libipc: 一款轻量级、跨平台的 C++ 进程间通信(IPC)库 目录1.简介2.安装2.1.vcpkg 一键安装2.2.源码编译安装3.使用示例4.核心设计思路与方案选型4.1 为什么选择共享内存作为底层传输机制4.2.无锁Lock-Free与轻量级同步的艺术4.3.核心抽象 route 与 channel5.项目的目录结构及介绍6.遇到的问题7.适合的项目场景1.简介https://github.com/mutouyun/cpp-ipc国内的镜像https://github.com/mutouyun/cpp-ipchttps://gitee.com/kanster/cpp-ipclibipc是一款轻量级、跨平台的C 进程间通信IPC库封装了管道、共享内存、消息队列等底层 IPC 机制提供简洁的现代 C API适配 Windows 和 Linux 系统。设计理念极简 API所有功能封装在ipc命名空间下无嵌套、无复杂继承调用成本极低RAII 资源管理所有 IPC 对象共享内存、管道、信号量等都遵循 RAII 原则自动创建 / 释放资源无内存泄漏、句柄泄漏风险零冗余封装底层直接调用系统原生 IPC 接口无多余中间层性能几乎与原生 API 持平强类型 错误处理内置is_valid()等状态校验接口错误信息直观无需手动处理系统错误码纯 C 实现无 C 语言兼容层完美支持 C17/20 特性可无缝结合协程、多线程。核心特点推荐支持C17的编译器msvc-2017/gcc-7/clang-4除STL外无其他依赖无锁lock-free或轻量级spin-lock底层数据结构为循环数组circular arrayipc::route支持单写多读ipc::channel支持多写多读【注意目前同一条通道最多支持32个receiversender无限制】默认采用广播模式收发数据支持用户任意选择读写方案不会长时间忙等重试一定次数后会使用信号量进行等待支持超时2.安装2.1.vcpkg 一键安装如果你是 Windows 开发者且安装了vcpkg 包管理器C 最常用的包管理器一行命令完成安装自动配置 VS 的头文件 / 库文件路径无需手动配置任何环境变量VS 会自动识别 libipc编译无任何报错这是 Windows 下最省心的安装方式# 打开【管理员模式】的 Powershell / CMD执行以下命令 vcpkg install libipc:x64-windows补充如果没装 vcpkg可先执行这行命令安装 vcpkggit clone https://github.com/microsoft/vcpkg.git cd vcpkg .\bootstrap-vcpkg.batvcpkg: 一款免费开源的C包管理器2.2.源码编译安装1) windows平台# 如果是 VS2019执行这条 cmake .. -G Visual Studio 16 2019 -A x64 -DCMAKE_INSTALL_PREFIXC:/libipc # 如果是 VS2022执行这条 cmake .. -G Visual Studio 17 2022 -A x64 -DCMAKE_INSTALL_PREFIXC:/libipcC:/libipc是安装路径可自定义比如D:/libipc记住这个路径即可。源码编译cmake --build . --config Release --target ALL_BUILD -j 4安装到路径cmake --install . --config Release安装完成后libipc 的文件自动部署到C:/libipc头文件C:/libipc/include/libipc/库文件C:/libipc/lib/(静态库ipc.lib 动态库ipc.dll)或者直接用vs2019或vs2022打开构建编译即可。2Linux平台# 步骤1克隆官方源码国内访问稳定无墙 git clone https://github.com/meetanthony/libipc.git cd libipc mkdir build cd build # 步骤2cmake配置Release模式安装到系统默认路径 /usr/local cmake .. -DCMAKE_BUILD_TYPERelease # 步骤3编译源码-j后接CPU核心数加速编译比如-j4 make -j$(nproc) # 步骤4安装到系统核心步骤需要sudo权限自动部署头文件和库文件 sudo make install # 【Linux必加】刷新系统动态链接库缓存防止cmake/运行时找不到库 sudo ldconfig安装完成后libipc 的文件自动部署到头文件/usr/local/include/libipc/库文件/usr/local/lib/(动态库libipc.so 静态库libipc.a)3.使用示例以下是libipc 结合共享内存实现跨进程大文件传输的完整可运行代码包含服务端文件发送、客户端文件接收和跨平台 CMake 构建脚本适配 WindowsVS2019和 Linux 环境。前提条件安装libipc可通过 官方仓库 下载源码或用包管理器安装确保编译器支持 C17 及以上VS2019 需开启/std:c171. 共享内存数据结构定义ipc_shm_def.h用于统一服务端和客户端的内存布局与同步标志#pragma once #include cstdint #include string // 共享内存数据块大小建议 4MB可根据内存调整 constexpr size_t SHM_BLOCK_SIZE 4 * 1024 * 1024; // 共享内存通信结构体 struct ShmFileTransfer { std::int64_t total_file_size; // 文件总大小 std::int64_t current_block_idx; // 当前传输块索引 std::int64_t current_block_size; // 当前块实际大小最后一块可能小于 SHM_BLOCK_SIZE bool transfer_finished; // 传输完成标志 char data[SHM_BLOCK_SIZE]; // 数据缓冲区 char file_name[256]; // 待传输文件名 }; // 共享内存名称和信号量名称跨平台区分 #ifdef _WIN32 const std::string SHM_NAME Local\\LibIPC_FileTransfer_Shm; const std::string SEM_NAME Local\\LibIPC_FileTransfer_Sem; #else const std::string SHM_NAME /libipc_filetransfer_shm; const std::string SEM_NAME /libipc_filetransfer_sem; #endif2.服务端代码server.cpp- 读取文件并写入共享内存#include ipc_shm_def.h #include libipc/shm.h #include libipc/sem.h #include fstream #include iostream #include filesystem namespace fs std::filesystem; int main(int argc, char* argv[]) { if (argc ! 2) { std::cerr Usage: argv[0] file_path std::endl; return 1; } std::string file_path argv[1]; if (!fs::exists(file_path)) { std::cerr Error: File not exists - file_path std::endl; return 1; } // 1. 创建共享内存大小为 ShmFileTransfer ipc::shm shm(SHM_NAME, sizeof(ShmFileTransfer), ipc::shm::CREATE | ipc::shm::RW); if (!shm.is_valid()) { std::cerr Error: Create shared memory failed! std::endl; return 1; } // 2. 创建信号量用于进程同步初始值 0客户端等待服务端写入 ipc::sem sem(SEM_NAME, 0, ipc::sem::CREATE); if (!sem.is_valid()) { std::cerr Error: Create semaphore failed! std::endl; return 1; } // 3. 映射共享内存到进程地址空间 ShmFileTransfer* shm_data static_castShmFileTransfer*(shm.map()); if (shm_data nullptr) { std::cerr Error: Map shared memory failed! std::endl; return 1; } // 4. 初始化共享内存参数 shm_data-total_file_size fs::file_size(file_path); shm_data-current_block_idx 0; shm_data-transfer_finished false; strncpy(shm_data-file_name, fs::path(file_path).filename().c_str(), 255); // 5. 打开文件并分块写入共享内存 std::ifstream file(file_path, std::ios::binary); if (!file.is_open()) { std::cerr Error: Open file failed! std::endl; return 1; } std::cout Start transfer file: file_path (size: shm_data-total_file_size bytes) std::endl; while (true) { // 读取数据到共享内存缓冲区 file.read(shm_data-data, SHM_BLOCK_SIZE); shm_data-current_block_size file.gcount(); if (shm_data-current_block_size 0) { break; } // 发送信号量通知客户端读取 sem.post(); // 等待客户端读取完成信号量减 1 sem.wait(); // 更新块索引 shm_data-current_block_idx; std::cout Transferred block: shm_data-current_block_idx (size: shm_data-current_block_size bytes) std::endl; } // 6. 标记传输完成 shm_data-transfer_finished true; sem.post(); // 最后一次发送信号通知客户端退出循环 // 7. 清理资源 file.close(); shm.unmap(shm_data); std::cout File transfer completed! std::endl; return 0; }3.客户端代码client.cpp- 从共享内存读取并写入文件#include ipc_shm_def.h #include libipc/shm.h #include libipc/sem.h #include fstream #include iostream #include filesystem namespace fs std::filesystem; int main() { // 1. 打开共享内存只读 ipc::shm shm(SHM_NAME, sizeof(ShmFileTransfer), ipc::shm::RW); if (!shm.is_valid()) { std::cerr Error: Open shared memory failed! (Please start server first) std::endl; return 1; } // 2. 打开信号量 ipc::sem sem(SEM_NAME); if (!sem.is_valid()) { std::cerr Error: Open semaphore failed! std::endl; return 1; } // 3. 映射共享内存到进程地址空间 ShmFileTransfer* shm_data static_castShmFileTransfer*(shm.map()); if (shm_data nullptr) { std::cerr Error: Map shared memory failed! std::endl; return 1; } // 4. 创建输出文件 std::string output_path recv_ std::string(shm_data-file_name); std::ofstream file(output_path, std::ios::binary | std::ios::trunc); if (!file.is_open()) { std::cerr Error: Create output file failed! std::endl; return 1; } std::cout Start receive file: output_path (total size: shm_data-total_file_size bytes) std::endl; // 5. 循环读取共享内存数据 while (true) { // 等待服务端写入数据 sem.wait(); // 检查传输是否完成 if (shm_data-transfer_finished) { break; } // 写入数据到文件 file.write(shm_data-data, shm_data-current_block_size); // 发送信号量通知服务端继续写入 sem.post(); std::cout Received block: shm_data-current_block_idx 1 (size: shm_data-current_block_size bytes) std::endl; } // 6. 清理资源 file.close(); shm.unmap(shm_data); std::cout File receive completed! Saved to: output_path std::endl; return 0; }4.跨平台 CMake 构建脚本CMakeLists.txt适配 Windows VS2019 和 Linux自动查找libipc库cmake_minimum_required(VERSION 3.15) project(LibIPC_FileTransfer) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 查找 libipc 库 find_package(libipc REQUIRED) # 头文件目录 include_directories(${CMAKE_CURRENT_SOURCE_DIR}) # 服务端可执行文件 add_executable(ipc_file_server server.cpp) target_link_libraries(ipc_file_server PRIVATE libipc::libipc) # 客户端可执行文件 add_executable(ipc_file_client client.cpp) target_link_libraries(ipc_file_client PRIVATE libipc::libipc) # Windows 平台特殊配置VS2019 if(WIN32) # 启用大文件支持 target_compile_definitions(ipc_file_server PRIVATE _LARGEFILE64_SOURCE _FILE_OFFSET_BITS64) target_compile_definitions(ipc_file_client PRIVATE _LARGEFILE64_SOURCE _FILE_OFFSET_BITS64) # 设置 VS 启动项可选 set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ipc_file_server) endif()4.核心设计思路与方案选型4.1 为什么选择共享内存作为底层传输机制在深入代码之前我们必须理解cpp-ipc最根本的设计选择共享内存。这背后是一系列权衡后的结果。性能是首要驱动力。与其他IPC机制相比管道/匿名管道数据需要在内核缓冲区中中转存在从用户态到内核态再从内核态到用户态的拷贝开销。消息队列同样是内核对象消息的传递需要经过内核并且有队列长度和消息大小的限制。Socket功能强大且跨网络但协议栈即使是本地回环带来的开销在本地IPC中显得非常不经济。共享内存一旦映射成功通信双方直接读写同一块物理内存区域这是最快的数据交换方式因为完全避免了数据的拷贝和系统调用。但共享内存的挑战在于同步。多个进程同时读写同一块内存如果没有正确的同步机制会导致数据竞争、损坏后果是灾难性的。传统的做法是使用互斥锁、信号量等同步原语但这些操作本身也可能涉及系统调用甚至可能引起进程阻塞和上下文切换从而抵消共享内存带来的性能优势。4.2.无锁Lock-Free与轻量级同步的艺术cpp-ipc的精华就在于它如何解决共享内存的同步问题。它宣称“无锁或轻量级自旋锁”这并非噱头而是针对不同场景的精细设计。1.无锁数据结构库的核心数据结构是一个 循环数组 。无锁编程的目标是确保多个线程或进程在访问共享数据时不需要等待锁从而避免死锁、优先级反转和锁竞争带来的性能断崖。它通常通过原子操作如 compare_and_swap 来实现。对于“单生产者-单消费者”或“单生产者-多消费者”这种典型IPC模式无锁队列是非常高效的。 ipc::route 单写多读很可能就采用了这种无锁设计写入者和读取者通过原子变量操作头尾指针实现高效、确定性的数据传递。2.轻量级自旋锁在“多生产者”的场景下实现真正的无锁算法复杂度会急剧上升。 ipc::channel 支持多写多读这里“轻量级自旋锁”就可能登场了。自旋锁的特点是当一个进程尝试获取锁失败时它不会立刻放弃CPU进入睡眠像互斥锁那样而是在一个循环中不断尝试“自旋”。这在锁持有时间非常短的场景下效率很高因为避免了进程上下文切换的开销。但如果锁竞争激烈或持有时间过长就会白白浪费CPU周期。 cpp-ipc 的“轻量级”可能体现在其自旋策略和回退机制上。3.混合策略忙等与信号量的平衡 库文档中提到的“不会长时间忙等重试一定次数后会使用信号量进行等待”是工程上的一个亮点。这是一种降级策略。先尝试无锁或自旋这种“乐观”的低开销方式如果短时间内无法成功说明资源竞争激烈就优雅地退回到使用信号量让进程等待从而释放CPU资源。这既保证了低竞争下的极致性能又避免了高竞争下的CPU空转是一种非常务实的做法。4.3.核心抽象 route 与 channel理解了底层机制再看它的上层抽象就清晰了ipc::route对应“单写多读”模式。想象成一个电视台一个发送者向所有电视机多个接收者广播节目。数据流是单向的发送者无需知道有多少接收者这种模式简单、高效常用于日志广播、状态发布等场景。ipc::channel对应“多写多读”模式。想象成一个群聊多个发送者和多个接收者。任何成员都可以发言所有成员都能收到。这更灵活但同步也更复杂适用于多对多的任务分发、事件通知等。这种设计让开发者可以根据通信模式选择最合适的抽象而不是被迫使用一个“万能”但可能低效的接口。5.项目的目录结构及介绍C IPC 库cpp-ipc的目录结构如下cpp-ipc/ ├── .github/ # GitHub 工作流程目录 │ └── workflows/ ├── 3rdparty/ # 第三方库目录 ├── demo/ # 示例代码目录 ├── include/ # 头文件目录 │ └── libipc/ # IPC 库头文件 ├── libipc/ # 库实现源文件目录 ├── src/ # 源代码目录 ├── test/ # 测试代码目录 ├── .gitignore # Git 忽略文件 ├── CMakeLists.txt # CMake 配置文件 ├── LICENSE # 许可证文件 ├── README.md # 项目说明文件 └── performance.xlsx # 性能数据文件主要目录和文件说明.github/workflows/: 存放 GitHub Actions 的工作流程文件用于自动化项目管理任务。3rdparty/: 如果项目依赖于其他开源库会在此目录下进行存储。demo/: 包含示例代码演示如何使用 C IPC 库。include/libipc/: 包含项目的所有公共头文件。libipc/: 包含库实现的源文件。src/: 包含主要的源代码文件。test/: 包含单元测试和性能测试的代码。CMakeLists.txt: 用于配置 CMake 构建系统的文件。README.md: 包含项目的基本介绍、安装和使用说明。performance.xlsx: 记录了项目的性能测试数据。项目的配置文件介绍项目的配置文件主要是CMakeLists.txt它用于配置 CMake 构建系统。以下是配置文件的基本结构cmake_minimum_required(VERSION 3.10) project(cpp-ipc) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG) if(NOT MSVC) set(CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE} -O2) endif() include_directories(${CMAKE_SOURCE_DIR}/include) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) add_subdirectory(src) set(GOOGLETEST_VERSION 1.10.0) add_subdirectory(3rdparty/gtest) add_subdirectory(test) add_subdirectory(demo/chat) add_subdirectory(demo/msg_que)在CMakeLists.txt中首先设定了项目所需的 CMake 版本和项目名称然后设置了 C 标准为 C17。接着添加了库搜索路径、源文件以及链接了 IPC 库。最后添加了测试目录以便进行单元测试和性能测试。6.遇到的问题使用过程中报错fail: send, there is no receiver on this connection.只要报这个错通信就断了也不知道为什么还在解决中。。。7.适合的项目场景桌面端 C 应用的进程间通信服务器端多进程协作如主进程管理子进程子进程间数据共享大文件传输、结构化指令下发、消息广播跨平台 C 项目的 IPC 模块开发。参考阅读Lock-Free 编程 - sangmado - 博客园
返回列表