尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Linux平台下C++ muduo网络库源码编译安装实战指南

Linux平台下C++ muduo网络库源码编译安装实战指南 C muduo网络库知识分享01 - Linux平台下muduo网络库源码编译安装做C后端开发的迟早会碰到muduo这个库。不管你是校招准备面经、社招想补一补网络编程的底子还是在公司里要用它做高并发服务陈硕的muduo几乎都是绕不开的一站。网上聊muduo源码分析的文章一抓一大把但很多人第一步就卡住了——库都编译不过去后面全是纸上谈兵。这篇文章就专注解决一件事在Linux平台上把muduo从源码编译安装跑通。我会把环境准备、源码获取、编译选项、安装路径、验证方法全部过一遍中间会把我在实际编译中踩过的坑、查过的错误、翻过的源码都交代清楚。文章面向的是准备入门muduo、或者编译失败正在找解决办法的朋友已经能熟练编译各种C库的老手可以重点看后面几节的坑和验证思路。1. 编译前的环境准备别急着clone代码我在很多技术群里见过一种现象拿到一个开源项目二话不说先git clone然后./build.sh一把梭报错就开始搜error: xxx。这种做法不是不行只是效率太低。编译muduo之前有几个前置问题值得先搞清楚。1.1 muduo的版本差异与依赖关系muduo在GitHub上有两个主要分支master和c11。master分支是陈硕早期基于C03标准写的原始版本c11分支则是在其基础上做了现代化改造用到了C11的特性。这里有一个关键点很多人不清楚c11分支其实是社区维护的版本陈硕本人在GitHub上明确说过master分支不再维护推荐用户使用c11分支。但国内很多教程、面经、书籍讲解的都是master分支的代码这就导致了一个尴尬——你照着书上的代码写clone下来却是另一个分支编译选项和代码结构都略有不同。我个人的建议是学原理看master分支的书和文章实际编译安装用c11分支。原因不复杂c11分支修复了很多老代码在较新编译器GCC 7以上下的编译问题比如一些隐式类型转换、模板推导的变化。如果你用的是Ubuntu 20.04以上系统自带的GCC 9/10/11编译老版本master分支大概率会碰到各种各样的编译报错光是修这些错误就够你喝一壶的反而不利于聚焦学习网络库本身。依赖方面muduo主要依赖两个库boostmuduo大量使用了boost库包括boost::bind、boost::function、boost::noncopyable等。虽然后期版本减少了对boost的依赖但编译仍然需要引导头文件。cmakemuduo的构建系统基于CMake需要3.0以上版本。zlib如果启用HTTP服务相关功能可能需要zlib开发包。1.2 我用的是什么环境这篇文章的实际编译环境是项目版本操作系统Ubuntu 22.04 LTS内核5.15.xGCC11.4.0CMake3.22.1Boost1.74.0muduo分支c11这里要单独说一下很多人在编译失败后会怪环境太新觉得旧代码就该配旧编译器。其实muduo c11分支在GCC 11下编译是没有问题的如果你在更新的环境比如GCC 12/13、Boost 1.80下遇到问题大概率是其他原因后面我会专门讲。在Ubuntu/Debian系系统上安装依赖就三行命令sudo apt update sudo apt install -y cmake g git sudo apt install -y libboost-dev libboost-test-devCentOS/RHEL系的话把apt换成yum/dnf包名略有不同sudo dnf install -y cmake gcc-c git sudo dnf install -y boost-devel boost-test安装完成后可以用g --version和cmake --version确认版本号确保基础环境没问题。提示不要跳过我说的依赖安装直接clone编译。muduo的CMakeLists里虽然会对boost做检测但缺头文件时报错信息是boost/noncopyable.hpp: No such file or directory一类容易让人误以为是源码本身的问题其实只是系统里没装boost开发包。2. 克隆源码与分支选择一次到位不折腾依赖装好后接下来就是获取源码。这一步看似简单但分支选错了会直接影响后续所有操作。2.1 从GitHub拉取代码的具体操作在正式命令之前先说说为什么我不建议直接git clone --depth1。muduo这个项目的历史提交里有非常详细的commit记录和代码演进过程对于学习者来说git log、git diff是理解设计思路的好工具。如果你--depth1浅克隆就丢失了这些历史信息。当然如果你只是急着编译用浅克隆确实更快这个完全看个人需求。我推荐的做法是# 在你的工作目录下执行 git clone https://github.com/chenshuo/muduo.git cd muduo git branch -a # 看一下所有本地和远程分支 git checkout c11 # 切换到c11分支如果你的网络环境访问GitHub比较慢可以用镜像站或者代理方式加速这里不做展开。clone完成后git log --oneline -5看一下最新的提交确认你拿到的是最近维护的版本。2.2 master分支与c11分支的编译差异这两条分支不只是代码风格的区别CMake配置层面也有不同。master分支用的是老式CMakeLists写法支持直接在项目根目录下./build.sh这个脚本会编译并在build/release/目录下生成库文件。而c11分支更新了CMake组织方式你可以使用标准的CMake流程mkdir build cd build cmake .. make -j$(nproc)这里有一个容易踩的坑c11分支默认编译参数里带了-Werror也就是说编译器警告会被当作错误处理。在高版本GCC下muduo的某些代码可能产生新的警告比如C17之后引入的-Wnoexcept-type、-Wdeprecated-copy等这些警告在老编译器下不存在于是就会直接编译失败。如果你碰到了这类报错不要慌解决办法有两个在CMake时手动关掉Werrorcmake -DCMAKE_CXX_FLAGS-Wno-error ..修改CMakeLists.txt找到-Werror相关行注释掉。我个人更推荐第一种因为不改源码后续想恢复更容易。不过需要说明的是当前muduo c11最近的代码在GCC 11下编译是干净的理论上你不一定会碰到这个坑但一旦碰到知道解法总比到处搜答案强。2.3 目录结构速览先知道代码在哪编译前花两分钟认识一下目录这对后续自己找头文件、链接库非常有帮助muduo/ ├── CMakeLists.txt # 根CMake配置定义了整体构建 ├── build.sh # master分支的构建脚本 ├── muduo/ # 核心代码目录 │ ├── base/ # 基础库Timestamp、Logging、Thread等 │ ├── net/ # 网络库核心EventLoop、TcpServer等 │ ├── net/http/ # HTTP服务相关的封装 │ ├── net/inspect/ # 内建监控接口 │ └── net/protorpc/ # RPC相关实现 ├── examples/ # 各种示例程序 │ ├── asio/chat/ # 聊天室例子 │ ├── fastcgi/ # FastCGI例子 │ ├── filetransfer/ # 文件传输 │ ├── ... # 还有不少不一一列举 └── tests/ # 单元测试muduo/base是底层基础设施不依赖网络功能muduo/net才是大家常说的网络库。编译时整个项目会一起构建但你在写自己的程序时只需要链接muduo_net和muduo_base两个库后面第三节会具体讲。3. 编译安装全过程从cmake到make install环境就绪、源码到位接下来就是重头戏——编译。这一节我不会只给命令还会解释每个步骤在干什么这样即使CMake报错你也知道该去哪看。3.1 执行CMake配置在muduo项目根目录下mkdir build cd build cmake ..命令执行后CMake会读取项目根目录的CMakeLists.txt检查系统环境、找到boost头文件、确定编译器、生成Makefile。执行成功的输出末尾大致长这样-- Configuring done -- Generating done -- Build files have been written to: /home/yourname/muduo/build如果报错最常见的就是找不到boost相关的头文件错误形如CMake Error at CMakeLists.txt:xx (message): Could NOT find Boost这时候回到第一节把libboost-dev装好重新执行cmake ..即可。另外需要注意一点CMake配置阶段的警告信息不要完全无视。比如我在某次编译时看到过CMake Warning at muduo/net/CMakeLists.txt:xx (add_library): Cannot generate a safe runtime search path for target muduo_net because files in some directories may conflict with those in directories...这个警告一般不影响编译结果可以忽略。但如果出现了红色的CMake Error那就必须解决。3.2 编译库文件配置成功后执行编译make -j$(nproc)-j$(nproc)是利用多核并行编译nproc命令会返回你机器的CPU核心数。如果机器内存不太够可以减半用-j$(expr $(nproc) / 2)避免编译时内存耗尽导致OOM。整个muduo编译过程在一般配置的电脑上大约1~3分钟属于比较轻量的项目。编译完成后在build/目录下会生成若干子目录主要库文件在build/lib下。用ls build/lib查看你会看到类似这样的文件libmuduo_base.a libmuduo_net.a libmuduo_http.a libmuduo_inspect.a这些.a文件就是静态库。如果你只想要最核心的网络库功能其实只要libmuduo_base.a和libmuduo_net.a就够了。3.3 安装到系统目录按标准的CMake流程接下来是安装sudo make install默认安装路径是/usr/local头文件会被拷贝到/usr/local/include/muduo/库文件拷贝到/usr/local/lib下。这样我们后续自己写代码时编译器会默认搜索这些路径不需要额外指定-I和-L参数。不过这里有个问题值得提很多Linux发行版尤其是Ubuntu 22.04的默认库搜索路径并不包含/usr/local/lib。在较老版本里/usr/local/lib默认不在/etc/ld.so.conf的配置中或者说gcc默认不会在这个目录下查找动态库。muduo默认编译的是静态库影响不大但如果你之后自己修改CMake选项编出了动态库.so运行程序时可能遇到error while loading shared libraries: libmuduo_net.so: cannot open shared object file: No such file or directory解决办法是执行sudo ldconfig或者把/usr/local/lib写进/etc/ld.so.conf.d/local.conf然后执行sudo ldconfig。另外在你自己的CMake工程中使用muduo时CMake默认找库路径也未必包含/usr/local/lib可能需要手动设置link_directories(/usr/local/lib)3.4 用自带例子验证编译结果验证编译成功最直接的方式是编译运行muduo自带的example。我推荐先跑一个最经典的回显服务器echo server代码在examples/asio/chat/目录下。cd ../examples/asio/chat ls你会看到server.cc和client.cc两个源文件。要编译它们需要链接muduo库。以server.cc为例g -o server server.cc -I/usr/local/include -L/usr/local/lib -lmuduo_net -lmuduo_base -lpthread然后运行./server正常的话程序会在监听端口默认为2019上等待连接终端不会输出太多内容。再开一个终端用系统自带的telnet或nc测试nc localhost 2019输入任意一行字符、回车你会看到同样的内容被回显出来。这说明服务器已经正常工作了。如果这一步通了那你的muduo就没有白装。提示如果你在链接阶段报错说找不到libmuduo_net.a先确认是否执行过sudo make install以及/usr/local/lib下是否有这几个.a文件。也可以用find / -name libmuduo_net.a 2/dev/null全局查一下库文件的真实位置。4. 编译过程中的常见报错与对应解法这一节专门解决问题。我把编译中实际遇到和网上高频出现的问题汇总成一张表再说说排查的思路。很多人说muduo老了、编译不过去其实绝大概率是下面某一类原因。4.1 报错快速定位表报错信息根本原因解决方案fatal error: boost/noncopyable.hpp: No such file or directory缺少boost开发包sudo apt install libboost-devCould NOT find Boost (missing: Boost_INCLUDE_DIR)boost装错位置/未装检查/usr/include/boost是否存在重新安装error: ‘shared_ptr’ does not name a typeC版本不对未开启C11检查CMakeLists中编译器选项或手动加-stdc11undefined reference topthread_create链接时缺少pthreadg命令末尾加-lpthreadcannot find -lmuduo_net库未安装或路径未指定确认/usr/local/lib下存在库文件或指定-L/usr/local/libError: required C standard is C11CMake版本过老或编译器过老升级GCC到5.0以上CMake 3.1以上error: ISO C forbids declaration of ‘xxx’ with no type编译器版本过老升级编译器避免使用远古GCC 4.xundefined reference toboost::system::...链接时缺少boost_system在链接参数中加-lboost_system老版本需要表中的第二类Could NOT find Boost需要展开多说几句。Ubuntu 22.04的libboost-dev安装后头文件在/usr/include/boost这个路径是CMake默认搜索路径所以理论上不会找不到。但如果你用的是手动编译安装的boost到/usr/local/ssl之类的非标准路径CMake就找不到这时需要cmake -DBOOST_ROOT/path/to/boost ..或者用-DCMAKE_INCLUDE_PATH告诉CMake头文件所在目录。4.2 最隐蔽的一个坑多个GCC版本共存我在实际编译时踩过最隐蔽的坑是系统里同时存在多个版本GCC导致的。Ubuntu 22.04自带GCC 11但你可能因为某些项目装了GCC 9并改了update-alternatives的默认版本。这时候CMake找到的编译器和实际运行的可能不是你预期的那一个。症状是照网上教程一步步做就是编译失败而且报错位置飘忽不定。排查方法很简单which gcc g gcc --version cmake .. | grep -i compiler # 看CMake实际使用的编译器如果发现问题要么把/usr/bin/gcc软链指到期望版本要么在CMake时明确指定cmake -DCMAKE_C_COMPILER/usr/bin/gcc-11 -DCMAKE_CXX_COMPILER/usr/bin/g-11 ..这个问题不仅发生在muduo上编译任何C项目都值得先确认环境推测问题上玄学化。4.3 高版本GCC下的编译警告变错误问题前面提到了-Werror的问题这里补充一个我看到过很多次的报错实例cc1plus: warning: ‘templateclass class std::auto_ptr’ is deprecated [-Wdeprecated-declarations] error: ‘std::auto_ptr’ is deprecated: use std::unique_ptr instead [-Werrordeprecated-declarations]这是比较典型的老代码在GCC 11下编译失败的场景。muduo c11分支中已经用std::unique_ptr替换了大部分auto_ptr但个别示例代码可能遗留了。报错明确指出了文件和行号但我的建议是不要急着改源码先加-Wno-error把它跳过去。原因很简单你现在的首要目标是把整个库编译通、能跑起来理解代码之后再去修正警告也不迟。改源码可能引入新的问题反而让排查难度上升。4.4 链接时找不到pthread的坑muduo的链接在不少例子里需要加-lpthread。GCC从5.x版本开始-pthread不是一个单独的库链接而是编译和链接选项同时起作用。如果你编译时用了-lpthread但漏了-pthread某些老版本编译器也可能出问题。建议统一在g命令中写g -stdc11 -pthread -o server server.cc -lmuduo_net -lmuduo_base还有一点muduo静态库的依赖顺序很重要。在链接时如果你写成-lmuduo_base -lmuduo_net编译器会报一堆undefined reference因为静态库的符号解析是单遍的。正确的做法是先写依赖别人的库再写被依赖的库即先net后base。5. 编译完之后的体系建设头文件、链接配置与CLion/VSCode编译安装成功不是终点咱们的目的是能用它写代码。很多朋友在命令行下编译能过但一打开IDE就找不到头文件这里分享几个环境配置经验。5.1 头文件路径与Makefile/CMake的标准写法如果只是命令行编译最简单的方式是g -stdc11 -I/usr/local/include -L/usr/local/lib -lmuduo_net -lmuduo_base -pthread -o myapp main.cpp如果你用CMake写自己的工程推荐在CMakeLists.txt里维护一个干净的查找逻辑cmake_minimum_required(VERSION 3.10) project(MyMuduoApp CXX) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) include_directories(/usr/local/include) link_directories(/usr/local/lib) add_executable(myapp main.cpp) target_link_libraries(myapp muduo_net muduo_base pthread)这样做比较傻瓜但也有不足之处直接写死路径换台机器可能就跑不通。更规范的方案是写一个CMake find module或通过pkg-config管理不过对于学习阶段上面这份足够直接了。5.2 VSCode配置C/C扩展会碰到的问题用VSCode写C的人很多这里有一个非常经典的问题VSCode的C/C插件默认不会自动找/usr/local/include下的头文件。结果就是你在命令行能编译通过VSCode里却满屏红波浪线。解决方法是创建.vscode/c_cpp_properties.json在includePath中加上/usr/local/include{ configurations: [ { name: Linux, includePath: [ ${workspaceFolder}/**, /usr/local/include ], defines: [], compilerPath: /usr/bin/g, cStandard: c11, cppStandard: c11, intelliSenseMode: linux-gcc-x64 } ], version: 4 }如果是用tasks.json做编译任务也需要把-I/usr/local/include和-L/usr/local/lib加进去。很多初学者把命令行编译和IDE编译当成两件事其实IDE底层还是会调用g只是参数配置藏在配置文件里。5.3 确认安装是否成功的终极测试在写完这些配置之后建议做一次终极验证写一个最小的muduo程序编译运行。比如使用TcpServer的回调打印#include muduo/net/TcpServer.h #include muduo/net/EventLoop.h #include muduo/base/Logging.h using namespace muduo; using namespace muduo::net; int main() { EventLoop loop; TcpServer server(loop, InetAddress(8888), TestServer); server.setMessageCallback([](const TcpConnectionPtr conn, Buffer* buf, Timestamp time) { conn-send(buf-retrieveAllAsString()); }); server.start(); loop.loop(); return 0; }编译时使用之前提到的g命令或CMake流程运行后访问8888端口发送什么回显什么说明整个muduo已经从源码变成可用状态了。这一步通了接下来再深入源码、读EventLoop的runInLoop逻辑就有踏实的基础了。6. 安装成功后的调试思路与路上建议整体流程走完你在muduo上的第一关就算过了。不过我想多说几句关于编译安装这个环节的心态认知因为它会直接影响你后面读源码的效率。6.1 读懂编译日志比背命令重要我见过不少人编译失败之后的操作复制报错最后一行去搜索引擎然后试人家给的命令不行再复制下一行再来一轮。这种做法偶尔有效但治标不治本。正确的方式是这样遇到报错先看是哪个文件哪一行然后往回翻编译日志找到第一条出错信息而不是最后一条。因为编译器常常会在第一个错误之后产生连锁且混乱的后续报错真正的病根往往在最前面。比如上文提到boost头文件缺失报错可能不止一条但第一条一定是boost/noncopyable.hpp: No such file or directory。理解了这一点你就知道这不是muduo自身的问题而是系统的头文件路径不完整。学会顺藤摸瓜之后大部分编译问题都能自己解决不用再去各种社区求助。6.2 构建目录和源码目录分离的小习惯在muduo根目录直接make出来的产物会和源码混在一起搜索文件时格外不舒服。我更喜欢把build目录建在源码目录外比如mkdir ~/build/muduo cd ~/build/muduo cmake ~/src/muduo make -j$(nproc)这样源码目录保持干净以后想重新编译、换分支、改选项直接删掉build目录重新来一遍完全不污染源码。6.3 安装后如何卸载什么时候需要重装有些朋友安装了muduo之后又改了源码想重新编译结果编译出来还是旧版本。这个问题的本质是安装路径里已经有一份库文件而新编译的库在build目录下没有覆盖过去。如果你改了源码想覆盖安装cd build sudo make install如果想彻底卸载sudo rm -rf /usr/local/include/muduo sudo rm -f /usr/local/lib/libmuduo_*.*在开发阶段我不太建议频繁install直接把build/lib路径通过CMake的link_directories指过去就够了。这样每次改动源码后只需要重新make不用重复安装。6.4 几个值得继续深入的路线编译装好只是认识muduo的开始。根据我的经验后续的学习路线大致是这样先跑通TcpServer和EventLoop的例子从TcpConnection收发数据开始看接下来看一下EventLoop的poll/epoll事件分发机制理解one loop per thread再深入TcpServer的连接管理、定时器TimerQueue的实现最后有余力可以研究buffer的设计和日志库的异步落盘逻辑。网络编程的核心概念非阻塞IO、事件驱动、反应器模式都会在源码里具象化装好库只是把舞台搭好而已。说实话muduo的编译安装这件事本身技术含量不算高但它确实是一道门槛——跨过去后面的代码分析才有物质基础。你在编译过程中遇到的问题越多、排查得越仔细对编译器、CMake、库链接这套流程的理解就越深。很多看起来是在浪费时间的报错排查其实都是变相在给自己加经验值。如果这篇文章能帮你少踩几个坑那就值了。
返回列表