首页 / 资讯中心 / 文章详情

Linux下从源码编译安装muduo网络库全程指南

Linux下从源码编译安装muduo网络库全程指南 ★ FEATURED ARTICLE
刚接触 C 网络编程的时候muduo 是一个绕不开的名字。它是用 C 写的一个基于 Reactor 模式的多线程网络库代码风格干净既能当作学习对象也能直接用于后端服务的底层通信。这篇博文不讲原理只讲最实际的一件事在 Linux 平台下把 muduo 网络库从源码完整编译安装一遍并验证它真的可用。我会从环境准备开始把 CMake 配置、Boost 依赖、编译参数、安装路径、常见报错一路说透。适合手里有一台 Linux 环境、但还没成功编译过 C 第三方库的人。我自己第一次装这套库时卡在版本和链接上将近一下午所以希望这篇文章能帮你把弯路走直。1. 为什么坚持源码编译预编译包、软件源与源码之间的差距1.1 包管理器里其实也能装但版本经常不理想有些 Linux 发行版的软件源里确实带 muduo 相关的包。比如 Debian 系的软件源可以搜到类似 muduo 的名字装上之后头文件和库文件会自动放到系统目录。听起来很省事但实际操作中我很少直接用这种方式原因很简单软件源里的版本通常滞后于上游而且每个发行版的打包策略不同有的把编译选项改了有的去掉了示例代码有的依赖的 Boost 版本和你项目里用的不一致。当你想基于 muduo 再开发自己的服务时一个版本不明、编译选项不明的库很容易成为隐患。更现实的问题是软件源提供的库安装位置是写死的头文件在/usr/include库文件在/usr/lib一旦你同时需要多个项目使用不同版本的 muduo单靠包管理器就很难隔离。源码编译配合自定义安装路径可以完美解决这个问题编译出的库放到一个独立目录项目里通过 CMake 或 Makefile 自行引用互不干扰。1.2 源码编译能换来什么从源码编译最直接的好处是可配置、可控。你可以通过 CMake 的-D参数决定安装目录可以开启或关闭示例代码、单元测试可以编译 Release 版或 Debug 版。树莓派、x86 服务器、容器镜像、不同 CPU 架构的 Linux 环境包管理器里的预编译包往往不通用源码编译才是真正通用的方案。这一点在做项目交付或二次开发时特别重要。另外你还能决定它的编译方式。muduo 默认会同时产出静态库和动态库静态库适合部署到目标机器上不依赖环境动态库适合开发迭代。源码编译时这些都可以通过 CMake 控制而不是接受别人的“定制品”。1.3 编译过程本身就是一次学习很多人在 C 相关面试题目里背过 make 和 CMake 的概念但真正看一次第三方库的 CMakeLists.txt 后理解会完全不同。muduo 的构建脚本并不复杂读完能学到怎么用find_package查找依赖、怎么组织跨目录的头文件、怎么区分静态库和动态库、怎么设置安装规则。这些东西是自己写一个“仿 muduo 库服务器项目”时必然会用到的基础。说白了你装一个库不只是为了得到.a或.so文件更是为了从它的构建系统里偷师。等你自己做项目时大概率也要写 CMakeLists.txt提前看看成熟项目的写法比硬背命令有效得多。2. 环境准备看似简单却最容易忽略的三件事2.1 准备一台干净的 Linux 环境编译 muduo 不需要高配机器普通双核 CPU、4G 内存就够建议磁盘留出 5G 以上。环境可以是虚拟机、云主机或 WSL但要注意如果用 WSL项目文件放在 Linux 文件系统里的性能会比挂在/mnt下的 Windows 盘好很多。这一步看似简单但很多人的问题恰恰出在最开始系统里有残留的旧编译器或者 CMake 版本过低。我建议找一台干净的环境避免乱七八糟的依赖冲突。如果你是在虚拟机上装 Linux安装时选择开发工具相关的软件组后面能省不少事。国内很多教程喜欢用图形界面操作但编译库这件事用命令行反而是最高效的。2.2 安装基础工具链在 Debian/Ubuntu 系统上打开终端执行sudo apt update sudo apt install -y build-essential cmake gitbuild-essential包含 gcc、g、makecmake用来生成构建文件git用来拉取源码。如果是 CentOS/Rocky 用 dnf命令类似sudo dnf install -y gcc-c make cmake git装完之后顺手确认版本g --version cmake --version我一般要求 g 不低于 5.4CMake 不低于 3.10否则后面会碰到各种莫名问题。如果不满足优先考虑升级系统或换环境不要在旧工具链上强行折腾。反正在 Linux 上重装一个系统镜像的成本很低远比跟老工具链死磕划算。2.3 Boost 依赖只装开发包就行muduo 代码里用到了 Boost 的头文件主要是boost::function、boost::bind、boost::noncopyable这类组件。这些都是 header-only 的也就是说不需要安装libboost_system.so这样的预编译二进制库只需要有头文件和 CMake 可以探测到的版本信息。因此在 Debian/Ubuntu 上安装sudo apt install -y libboost-dev如果你需要跑 muduo 自带的单元测试还会用到 boost 单元测试框架这时可以再装libboost-test-dev。但只编译库本体libboost-dev已经够用。这里顺便说一个常见误解有人以为必须手动编译整个 Boost 库动辄等半小时。其实对 muduo 来说没必要只有用到需要链接二进制库的模块时才需要编译安装完整 Boost。别把简单的事做复杂。3. 源码获取与构建文件生成从拉取代码到 CMake 配置3.1 拉取源码与选择版本获取 muduo 源码最简单的方式是 git clonegit clone https://github.com/chenshuo/muduo.git cd muduo git tag # 查看版本 git checkout 某个tag如果不方便访问 GitHub也可以从 gitee 或国内其他代码托管平台的镜像拉取或者直接下载 release 压缩包具体取决于你的网络环境。我这里不深入讨论网络问题只说版本选择的策略。muduo 的 master 分支更新比较活跃如果你想稳定复现建议切到 release tag。用git tag先列出所有 tag挑一个发布时间较新的稳定版。版本选择的原则是尽量选与当前 Boost 兼容的版本。老版本配新编译器常见一堆报警告虽然多数能编译过但会把真正的问题淹没在信息海里排查起来很痛苦。3.2 用 CMake 生成构建文件muduo 使用 CMake 组织构建官方推荐的流程是在源码目录下建 build 目录把生成的临时文件隔离起来避免污染源码树。mkdir build cd build cmake .. -DCMAKE_BUILD_TYPERelease -DCMAKE_INSTALL_PREFIX/usr/local/muduo这里两个参数很关键。CMAKE_BUILD_TYPERelease会开启 O2 优化适合库发布如果你想调试源码里的细节用Debug。CMAKE_INSTALL_PREFIX指定安装前缀默认是/usr/local我这里特意指定成/usr/local/muduo这样库文件集中在一个目录下卸载时直接删目录即可不会污染系统。muduo 的 CMakeLists 中还可能有类似MUDUO_BUILD_EXAMPLES和MUDUO_BUILD_TESTS的开关。默认会编译 examples方便学习示例代码如果只想装库可以关闭cmake .. -DCMAKE_BUILD_TYPERelease -DCMAKE_INSTALL_PREFIX/usr/local/muduo -DMUDUO_BUILD_EXAMPLESOFF不过不同版本里开关名可能略有差异可以先执行cmake -L ..查看当前版本支持哪些选项。这个命令会列出所有可配置的 CMake 变量比翻文档快。3.3 CMake 找不到 Boost 时的处理cmake 执行时如果输出类似Could NOT find Boost (missing: Boost_INCLUDE_DIR)说明系统里没有 Boost 头文件回到 2.3 节安装libboost-dev即可。如果 Boost 装在非标准路径可以用-DBOOST_ROOT指定cmake .. -DBOOST_ROOT$HOME/boost这个参数本质是告诉 CMake 头文件和库文件在哪里适合你自己在本地编译了一个 Boost 的场景。注意如果环境里同时有系统 Boost 和自定义 Boost路径设错会让 CMake 找到旧版本编译时出现莫名其妙的符号错误。4. 实际编译与安装make 背后的细节与产物验证4.1 并行编译的节奏构建文件生成好之后执行make -j4-j4表示 4 个编译任务并行。这个参数不是越大越好C 编译非常吃内存每个编译任务可能会占几百 MB 内存。机器只有 4G 内存时开-j8大概率卡死只能 CtrlC 重来。我的经验是内存 4G 用-j28G 及以上再用-j4。首次编译 muduo 时 examples 很多整体可能要几分钟。不用一直盯着屏幕只要最后没有error就行。输出中会有一些 warning比如类型转换的提示只要不是 error 就可以继续。如果 error 出现在 boost 头文件里通常是 Boost 版本太新导致的兼容问题这个后面单独说。4.2 安装到指定目录与产物检查编译完成后执行sudo make install因为/usr/local/muduo目录需要 root 权限所以要用 sudo。安装完成后看一下产物find /usr/local/muduo -maxdepth 3 -type f | sort正常情况下你能看到include/muduo下的头文件以及lib/目录下的libmuduo_base.a、libmuduo_net.a还有可能是.so动态库。muduo 默认会同时生成静态库和动态库具体看你用的版本和 CMake 配置。为了确认库不是空壳可以用nm工具抽查符号nm /usr/local/muduo/lib/libmuduo_net.a | grep TcpServer | head能看到 TcpServer 相关的符号说明库编译正常。这个习惯很好安装完顺手验证产物比最后运行项目时报链接错误再回头查要省时间。4.3 动态库搜索路径容易被忽略的一步如果你安装的是动态库写代码时头文件能找到但运行时动态链接器不一定知道/usr/local/muduo/lib这个路径。第一种处理方式是临时导出环境变量export LD_LIBRARY_PATH/usr/local/muduo/lib:$LD_LIBRARY_PATH第二种是一劳永逸地写入系统配置echo /usr/local/muduo/lib | sudo tee /etc/ld.so.conf.d/muduo.conf sudo ldconfigldconfig会更新动态链接器缓存。这一步在编译阶段往往看不出问题真正运行可执行文件时会报error while loading shared libraries: libmuduo_net.so: cannot open shared object file到时候再回来配也不迟。为避免这个坑我建议在安装完库之后立刻执行 ldconfig然后再写测试代码。5. 第一个测试程序用编译好的 muduo 跑通 echo 服务器5.1 准备代码安装完成并不代表能用一定要写个程序验证。我建议用一个最简单的 echo 服务器客户端发什么就回什么。先在项目目录建一个文件夹比如muduo-demo里面放main.cc#include muduo/net/TcpServer.h #include muduo/net/EventLoop.h #include muduo/net/InetAddress.h #include muduo/net/Buffer.h #include muduo/net/TcpConnection.h #include string using namespace muduo; using namespace muduo::net; void onMessage(const TcpConnectionPtr conn, Buffer* buf, Timestamp time) { conn-send(buf-retrieveAllAsString()); } int main() { EventLoop loop; InetAddress listenAddr(8888); TcpServer server(loop, listenAddr, EchoServer); server.setMessageCallback(onMessage); server.start(); loop.loop(); }这段代码展示了 muduo 最核心的使用逻辑EventLoop负责事件循环TcpServer负责监听和管理连接setMessageCallback设置消息回调loop.loop()启动运行。回调函数里拿到的是连接对象、缓冲区和时间戳把缓冲区里的字符串原样发送回去就是 echo 服务。如果你用的 IDE 是 VSCode记得把/usr/local/muduo/include配到C/C: Edit Configurations的 includePath 里这样写代码时自动补全和跳转才正常。这个操作不复杂但能让后面写 muduo 代码的体验好很多。5.2 用 CMake 组织项目不建议手动敲 g 长命令直接写个 CMakeLists.txtcmake_minimum_required(VERSION 3.10) project(echo_demo CXX) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) include_directories(/usr/local/muduo/include) link_directories(/usr/local/muduo/lib) add_executable(echo main.cc) target_link_libraries(echo muduo_net muduo_base pthread rt)muduo_net依赖muduo_base所以两个都要写。pthread是线程库rt是实时库muduo 在某些平台会用到。如果你的 muduo 是纯静态库链接时还可能要把 boost 相关头文件对应的库也加进来不过我们只用了 library 本体且它自身的依赖已经内聚这样链接就够了。如果报 boost 符号找不到再补上 boost 相关链接。5.3 编译、运行与验证mkdir build cd build cmake .. make ./echo程序会卡住不要以为崩溃了这是EventLoop在等事件。另开一个终端nc 127.0.0.1 8888输入 hello屏幕上立即回显 hello。这就证明 muduo 编译安装成功而且动态库加载也正常了。如果没有 nc可以用 telnetsudo apt install -y netcat-openbsd如果编译报找不到 muduo 头文件检查include_directories路径是否和你的安装前缀一致如果运行报找不到共享库回到 4.3 节配置LD_LIBRARY_PATH或 ldconfig。一次跑通之后你的 muduo 开发环境就算真正可用了。6. 编译安装中的高频报错与排查思路6.1 找不到 boost 头文件报错fatal error: boost/function.hpp: No such file or directory原因是只装了编译器没装 Boost 开发包。解决办法是安装libboost-dev头文件会出现在/usr/include/boost。如果还是没有多半是系统是精简版再装libboost-all-dev也可以这个包会把 boost 全部开发组件都带进来代价是体积大一些。判断头文件到底装没装直接看目录就行ls /usr/include/boost/function.hpp6.2 CMake 提示 Boost 版本或组件不满足报错可能形如Could NOT find Boost (missing: BOOST_LIBRARYDIR)通常出现在 Boost 没有装到系统默认位置或者装了多个版本。建议先看系统里有哪些 boost 包dpkg -l | grep boost确认版本后如果版本过旧可以升级或手动指定。手动编译安装 Boost 也是可行方案但很费时间只在系统自带版本都满足不了需求时才推荐。有时候问题不是没有 boost而是没有boost/test组件CMake 检查不过这时候装libboost-test-dev就能解决。6.3 新版 g 编译老代码的兼容性报错muduo 是老牌库master 分支会持续适配新编译器但如果你选择了一个很老的 tag再用 g 12 或 13 去编译偶尔会因为 C 标准收紧报一些 error比如构造歧义、类型转换不再允许等。处理办法有三条换一个更新的 tag把编译器降到 g-9 或 g-10在 CMake 中给 CXX_FLAGS 追加-stdc11或-stdc14。看到 error 先确认它出现在哪个文件里。如果是 boost 头文件内部报错优先考虑升级 muduo 或调整编译器版本而不是去改 boost 的源码。强行sed改系统头文件短期能过长期维护就是灾难。6.4 并行编译卡死make 时系统突然没响应交换分区狂转基本就是内存耗尽。C 编译每个任务会吃几百 MB 内存-j16同时跑16G 内存都可能紧张。解决方法是卡死时按 CtrlC 中断重新用make -j2或make -j4。不要在内存小的机器上开高并发这是 C 项目编译的通用教训不止 muduo 适用。6.5 安装路径与权限问题如果你看到Permission denied mkdir /usr/local/muduo说明make install时没有用 sudo。换sudo make install即可。如果之前已经用普通用户创建了 build 目录里面生成的 Makefile 以普通用户身份调用 install再用 sudo 执行时可能会因为文件所有者问题报错这时把 build 目录删掉重新编译一次也行。为了省事我通常在项目目录下全部用当前用户编译只有最后的make install用 sudo。6.6 多版本 muduo 并存怎么管理我用CMAKE_INSTALL_PREFIX把不同版本的 muduo 安装到不同目录比如/opt/muduo-1.0和/opt/muduo-dev。在项目里通过include_directories和link_directories切换。相比直接装在/usr/local这个方法干净得多。卸载时直接rm -rf对应目录就行。最后多提一句编译安装只是起点。muduo 真正值钱的是它的源码结构、事件分发机制、定时器实现建议装完后不要急着关终端打开 examples 目录下的代码读一读。我自己是在写完一个仿 muduo 的 MiniServer 之后才对 Reactor、EventLoop 这些东西真正有了体感。如果你也打算往 C 服务端方向走这条路非常值得走一遍。
阅读完成 · 觉得有帮助?
咨询建站