
1. 写在前面为什么我坚持让你自己编译一遍muduo做C后端开发的几乎没有人没听过muduo这个名字。陈硕大佬开源的这套基于Reactor模式的高性能C网络库可以说是国内C网络编程领域绕不开的经典作品。无论是看《Linux多线程服务端编程》这本书还是想搞懂epoll、事件循环、线程池这些底层机制muduo都是极好的学习素材。而要把muduo用起来、读进去第一步就是把它在你的Linux环境下成功编译安装。很多人会问既然apt/yum里可能有现成的包为什么非要自己从源码编译一遍我个人的观点很直接学习型项目必须源码编译。一方面源码编译能让你清楚地看到muduo依赖什么、由哪些模块组成、头文件和库文件分别装到哪里另一方面muduo的代码风格非常值得精读你编译完之后顺手打开~/muduo目录翻一翻源码比任何教程都管用。更现实的原因是muduo的master分支一直在迭代系统仓库里的版本往往偏老自己编译才能拿到最新的特性和修复。这篇分享我会完整走一遍从环境准备、源码下载、CMake配置、编译安装到验证可用的全过程并且把我在多台Linux服务器上编译muduo时踩过的坑一并整理出来。内容适合刚接触muduo的C学习者也适合要给项目引入muduo但卡在编译环节的工程师。跟着操作一遍你有很大概率能顺利用起来。2. 环境准备编译前先把这些底子打好2.1 系统与编译器版本要求muduo虽然年代久远但代码维护得很勤快对现代Linux发行版的兼容性相当不错。我实测过的环境包括Ubuntu 18.04/20.04/22.04、CentOS 7/8以及Debian 11都能顺利编译。不过有一点要提醒CentOS 7自带的编译器版本过低需要优先升级gcc否则会在编译muduo时遇到C11标准支持不完整的问题。具体来看版本要求操作系统任何主流的64位Linux发行版内核版本其实无所谓只要glibc版本别太老。32位系统我建议直接放弃muduo的原子操作和内存模型优化都是为64位环境设计的。编译器GCC 4.8以上即可但强烈建议GCC 7及以上。muduo源码大量使用C11特性老编译器虽然能过但编译速度慢、警告多。我用GCC 9.4编译时全程零警告体验非常好。CMake必须3.0以上否则无法正确解析muduo的CMakeLists.txt。Ubuntu 18.04自带的CMake 3.10就能用但如果你的系统太老建议去CMake官网下载预编译二进制别用源码折腾。构建工具make是标配一般系统都有。如果没有记得先装上build-essentialUbuntu系或Development ToolsCentOS系。在动手之前先花一分钟检查一下环境uname -a gcc --version g --version cmake --version make --version看到输出都正常再继续往下走。这一步能帮你提前发现很多问题不至于编译途中突然报错然后手忙脚乱。2.2 依赖库Boost是唯一硬性要求muduo的依赖非常克制核心依赖只有Boost库而且主要是Boost的boost::function、boost::bind这些组件不是Boost.Asio那种重量级网络组件。另一个可选的依赖是Google Protobuf只有当你需要用到muduo自带的protobuf编解码器时才需要安装。先说Boost的安装。这里有一个非常经典的坑muduo仓库的master分支需要Boost 1.52以上版本而某些老教程会告诉你任意版本都行结果你用系统自带的libboost-dev装出来的老版本编译时报一堆找不到头文件的错误。我在Ubuntu 18.04上就踩过这个雷系统源里的Boost是1.65已经能用了但如果你用CentOS 7系统源里的Boost只有1.53勉强够用编译时可能会有偶发问题。推荐直接用apt或yum安装# Ubuntu / Debian sudo apt update sudo apt install -y libboost-dev libboost-system-dev libboost-filesystem-dev # CentOS / RHEL sudo yum install -y boost-devel如果你非要最新版Boost自己去boost.org下载源码编译也行但说实话没必要。muduo对Boost版本的依赖其实很宽松系统源里的版本足够稳定省下的时间不如多看两页源码。Protobuf这边我的建议是先不要装跟着本文走完编译安装等确实需要protobuf编解码功能时再回来补装。因为muduo的CMake会自动检测系统里有没有protobuf有就开启相关示例的编译没有就跳过并不会导致整个编译失败。先把门槛降到最低把一个最小可用的muduo跑起来后面再逐步加功能。3. 源码获取从GitHub拉取最新代码3.1 分支选择master还是cpp11muduo的GitHub仓库地址是https://github.com/chenshuo/muduo当然如果你访问GitHub不方便也可以从国内的一些代码托管平台的镜像拉取搜索“muduo mirror”就能找到。这里要讲一个重要的背景。muduo历史上经历过一次重大分支调整早期版本同时维护了master和cpp11两个分支cpp11分支是用C11重写过的版本去掉了对Boost的依赖。后来陈硕把cpp11分支合并成了新的master所以现在你从仓库拉下来的master分支实际上就是C11版本不再依赖Boost。这一点很关键——很多教程还在教你先装Boost再编译那是因为它们的年代太久远还在用老分支。所以现在的推荐做法是cd ~ git clone https://github.com/chenshuo/muduo.git拉下来之后master分支就是最新代码。如果你想看看老版本长什么样可以切到v1.0或v2.0这样的tag去考古但对于学习和使用直接master就好。仓库体积不大几十MB几秒钟就能拉完。如果你不想用git也可以直接去GitHub仓库页面点“Code”按钮下载zip包效果一样。只不过我个人更喜欢git clone的方式因为后续你想更新代码、切分支、查看提交历史都非常方便——学习muduo源码时git log和git blame是很好的工具。3.2 仓库结构速览分清楚每个目录是干什么的拉完代码后先别急着编译花几分钟把目录结构看清楚。muduo的仓库布局非常清晰一看就懂muduo/ ├── CMakeLists.txt # 顶层CMake配置 ├── build.sh # 一键编译脚本 ├── examples/ # 丰富的示例程序 ├── muduo/ │ ├── base/ # 基础库日志、线程、时间戳等 │ ├── net/ # 网络库核心EventLoop、TcpServer等 │ └── ... ├── tests/ # 单元测试 ├── VERSION # 版本号 └── ...muduo/base是底层基础库包含线程封装、日志、时间戳、原子操作等组件这部分和网络无关但可以被任何C项目复用。muduo/net才是核心里面有EventLoop事件循环、ChannelIO通道、Pollerepoll封装、TcpServerTCP服务器、TcpConnectionTCP连接、Buffer缓冲区、InetAddress网络地址等都是网络编程中耳熟能详的组件。examples目录我个人非常推荐多翻翻里面有几个经典示例echo回显服务器、chat聊天室、pingpong性能测试、httpHTTP服务器等。这些示例代码量不大但浓缩了muduo的核心用法是你从编译安装走向实际编程最好的跳板。4. 正式编译一步步把muduo装进你的系统4.1 使用官方构建脚本一键编译muduo仓库根目录提供了一个build.sh脚本这是最简单的编译方式。在仓库根目录执行cd ~/muduo chmod x build.sh ./build.sh这个脚本的逻辑是用CMake生成Makefile然后make编译全部代码默认情况下编译结果会放在build/release目录下如果开启Debug模式则在build/debug目录下。脚本里还包含了解析编译参数的能力支持传入debug表示编译Debug版本。整个编译过程需要几分钟取决于你的机器性能。结束后你会看到类似下面的输出提示说明编译成功了[100%] Built target muduo_base [100%] Built target muduo_net如果看到的是Error字样那就说明编译过程中出了问题。别慌第5节我会专门整理常见问题和解决办法。4.2 手动CMake编译为了把每个选项握在手里如果你是那种喜欢把每一步都搞清楚、不想被脚本隐藏细节的人或者你需要在编译时定制一些选项比如指定安装路径那手动执行CMake流程会更适合你。完整流程我写在这里同时会解释每条命令在干什么cd ~/muduo mkdir -p build/release cd build/release cmake -DCMAKE_BUILD_TYPERelease -DCMAKE_INSTALL_PREFIX/usr/local ../.. make -j$(nproc)分步拆解mkdir -p build/release cd build/release创建并进入构建目录。强烈建议不要直接在源码根目录下编译因为编译过程中会产生大量中间文件污染源码目录让你之后想git status查看源码改动时满屏都是未跟踪文件烦不胜烦。CMake的out-of-source构建就是为了解决这个问题。cmake -DCMAKE_BUILD_TYPERelease -DCMAKE_INSTALL_PREFIX/usr/local ../..配置构建系统。CMAKE_BUILD_TYPE决定是Release还是Debug版本。Debug版包含调试信息更适合用GDB跟踪源码学习Release版做了优化性能好但没有调试信息。如果你是学习阶段我建议先编Debug版后面阅读源码、断点调试都方便。CMAKE_INSTALL_PREFIX决定最终安装路径默认是/usr/local我觉得一般不需要改除非你没有root权限那可以指定到自己的目录比如-DCMAKE_INSTALL_PREFIX$HOME/muduo-install。make -j$(nproc)启动并行编译$(nproc)会自动获取CPU核心数作为并行任务数。这个优化很重要默认的make是单线程编译在8核机器上可能要等好几分钟而make -j8能把时间缩短到一分钟左右。不过要注意如果在编译CentOS 7上跑老版本muduo并行编译偶尔会触发依赖顺序问题遇到报错就退回make单线程编译不影响使用。编译完成之后输出目录里会出现两个核心库文件libmuduo_base.a和libmuduo_net.a。这是静态库后缀.a。muduo默认只生成静态库这在C网络库中很常见——静态库部署简单不需要考虑动态库的运行时依赖问题。4.3 安装到系统目录编译完成并不意味着安装完成。CMake世界里的安装指的是把头文件、库文件复制到系统约定的位置让其他项目能自动找到它们。这一步同样需要执行sudo make install如果你在CMake配置时指定的安装路径是/usr/local那么执行完这条命令后muduo的头文件会安装到/usr/local/include/muduo库文件会安装到/usr/local/lib。如果CMake配置时用了自定义路径那安装位置就跟着自定义路径走但后续编译器可能找不到需要在编译你自己项目时手动加上-I和-L参数。这里有个小细节如果你编译时改了CMAKE_INSTALL_PREFIX而后续其他项目要引用muduo需要在环境变量或者CMake配置里显式指定路径。为了省心我强烈建议使用默认的/usr/local安装路径作为普通用户虽然没有写/usr/local/include的权限但加上sudo就解决了这也是绝大多数Linux软件的标准安装方式。4.4 验证安装是否成功安装完成后不能高兴得太早要验证一下muduo是否真的可用。最简单的验证方式是写一个muduo版的Hello World——一个最小化的Echo服务器。这里给出一段极简代码#include muduo/net/TcpServer.h #include muduo/net/EventLoop.h #include muduo/base/Logging.h #include iostream using namespace muduo; using namespace muduo::net; class EchoServer { public: EchoServer(EventLoop* loop, const InetAddress listenAddr) : server_(loop, listenAddr, EchoServer) { server_.setConnectionCallback( std::bind(EchoServer::onConnection, this, std::placeholders::_1)); server_.setMessageCallback( std::bind(EchoServer::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); } void start() { server_.start(); } private: void onConnection(const TcpConnectionPtr conn) { if (conn-connected()) { LOG_INFO New connection established; } else { LOG_INFO Connection closed; } } void onMessage(const TcpConnectionPtr conn, Buffer* buf, Timestamp time) { std::string msg(buf-retrieveAllAsString()); LOG_INFO Received msg.size() bytes; conn-send(msg); } TcpServer server_; }; int main(int argc, char* argv[]) { EventLoop loop; InetAddress listenAddr(8888); EchoServer server(loop, listenAddr); server.start(); loop.loop(); return 0; }将代码保存为echo.cc然后编译g -stdc11 -o echo echo.cc -lmuduo_net -lmuduo_base -lpthread注意几个链接参数-lmuduo_net -lmuduo_base链接muduo的两个静态库-lpthreadmuduo依赖POSIX线程必须链接pthread不需要额外加-lboost_*因为新版master分支已经去掉了Boost依赖如果编译通过运行./echo再用telnet 127.0.0.1 8888连上去输入什么就回显什么那muduo就彻底装好了、能用了。5. 编译踩坑实录把我这几台机器上踩过的坑都告诉你5.1 常见编译错误与解决办法不管你是用build.sh还是手动CMake编译都会大概率遇到下面几个问题。我把它们整理成表格另外附上我当时的排查思路报错信息原因分析解决方案fatal error: boost/...: No such file or directory系统没装Boost或版本过老新master无需Boost若确认是老版本代码安装libboost-devCMake Error: CMake 3.x is requiredCMake版本过低下载CMake预编译包并配置PATH或用cmake3命令error: shared_ptr was not declared in this scope编译器标准未设为C11在CMakeLists.txt或编译命令中显式加-stdc11undefined reference to pthread_atfork链接时缺少pthread库编译命令末尾加-lpthreadcollect2: error: ld returned 1 exit status缺少某个依赖或库路径不对检查-L路径是否正确用ldd查看动态库依赖上面表格里第三个问题值得展开说一下。如果你的编译器默认标准是C98那即使代码里写了#include memoryshared_ptr等C11类型依然不可见。新版muduo的CMakeLists.txt里已经设置了C11标准但如果你是自己写测试代码去引用muduo头文件记得手动加-stdc11。我在CentOS 7上用老版本gcc时踩过这个坑当时编译一直报错加了这个参数瞬间就好了。还有一个很隐蔽的问题是LOG_INFO在旧版muduo中的用法。新版muduo采用流式日志语法即LOG_INFO message而老版本的日志API可能完全不一样。如果你参考的是网上很久以前的教程编译时可能会报日志相关错误。建议以源码examples目录为准那才是最权威的用法参考。5.2 关于静态库和动态库的选择muduo编译产出是静态库静态链接进你的可执行程序后运行时不再依赖muduo的库文件部署非常方便。但静态链接也有个缺点如果你的服务器上跑好几个用muduo写成的服务每个可执行文件都会内嵌一份muduo代码磁盘占用会稍稍增加。对于现代服务器来说这个增加完全可以忽略不计。如果你希望用动态库的方式需要自己修改CMake配置把add_library的STATIC改成SHARED。但我不建议新手去动这个因为muduo官方只保证静态库的编译和测试覆盖动态库可能遇到隐藏的链接问题没必要给自己找麻烦。我个人的习惯是学习阶段用Debug版静态库编译出来的库包含完整符号信息用GDB调试时可以追溯到库内部的每一行代码——这是学习网络库源码的利器。等以后真正上线项目了再切到Release版追求最好的运行性能。5.3 不同Linux发行版的差异提醒虽然muduo的跨平台性做得很好但不同Linux发行版上编译仍有些细微差异我统一整理一下Ubuntu/Debian系整体是最顺利的apt源里依赖齐全gcc版本也新基本上下载源码直接编译就能过。唯一要留意的是新版Ubuntu默认gcc已经是11或12了某些老版本muduo代码比如你checkout到老tag在高版本gcc下会报告一些额外警告但基本不影响编译通过。CentOS/RHEL系的系统要稍微关注CentOS 7的默认gcc是4.8.5尽管能用C11但对新标准的支持比较拙计建议yum install -y devtoolset-9-gcc-c进行升级。装完后需要执行scl enable devtoolset-9 bash才能在当前shell里使用新gcc。切到新gcc后我实测编译很流畅。CentOS 8的gcc是8.x已经可以流畅编译muduo不用额外折腾。CentOS系里如果找不到libboost-dev这样的包名也别慌那说明发行版用的是libboost-devel这样的命名风格yum search boost搜一下就行。Arch Linux/Manjaro系Arch的软件包版本都很新一般不会遇到版本过老的问题。直接装boost和protobuf就行。如果你使用Arch系的ARM版本比如树莓派上的Arch Linux ARM编译可能耗时更长但成功率和x86_64版本是一样的。5.4 编译通过但运行时报错的排查方向编译通过只是第一步运行时问题更隐蔽。我最常遇到的两类运行时报错及排查思路报错1找不到动态库。如果你未来把muduo改成了动态库或你的程序链接了动态muduo库运行时会报error while loading shared libraries: libmuduo_net.so: cannot open shared object file。原因是动态库安装位置不在系统默认搜索路径。解决办法是把/usr/local/lib加入动态库搜索路径echo /usr/local/lib | sudo tee /etc/ld.so.conf.d/muduo.conf sudo ldconfig执行完后再运行程序就能找到动态库了。报错2端口被占用。运行示例程序时报bind: Address already in use或Address already available说明端口没释放。muduo默认设置SO_REUSEADDR所以连续重启服务一般没问题但如果你之前有个进程还挂着被占用的端口自然绑不上。排查方式很传统lsof -i:8888 kill -9 PID然后再启动你的程序就好。5.5 一个最容易被忽略的问题磁盘空间muduo源码编译产生的中间文件不少完整编译Release和Debug两套版本build目录加起来可能占用几个GB的空间。对于云服务器或树莓派这种磁盘紧张的环境这个问题不可忽视。我自己的习惯是只编译一个版本要么Release要么Debug不要两个都要编译完成后如果需要清理空间可以删除build目录安装好的库和头文件不受影响定期git pull拉取最新代码的同时旧build目录可以一并清掉重新编译另外编译muduo对内存要求不算高1GB内存的机器完全能扛住但如果你的机器内存特别小比如512MB可以不使用make -j并行编译而是顺序编译降低内存峰值占用。6. 编译安装之后我给新手的三点深挖建议装好muduo只代表迈过了一道小坎真正的考验在后面。根据我自己从“装好muduo”到“能用muduo写项目”的过渡经验我建议你按下面的顺序继续深挖。第一先跑通再改源码。运行一下examples目录下的echo、chat、pingpong这些示例感受一下muduo的事件循环和非阻塞IO到底是什么体验。然后试着改动其中几行代码比如修改日志输出、改变消息处理逻辑观察运行结果的变化。这个“动手改代码”的循环是最快理解框架的方式。第二带着问题读源码。muduo的源码写得很克制读起来并不难但前提是你得知道自己在找什么。比如你可以带着“EventLoop是如何实现事件循环的”“Channel是怎么分发事件的”“Buffer为什么能高效处理粘包”这样的问题去源码里找答案。源码路径分别是muduo/net/EventLoop.cc、muduo/net/Channel.cc、muduo/net/Buffer.cc对照《Linux多线程服务端编程》这本书来看效果最好。第三用muduo写一个自己的小项目。回显服务器只是入门我建议你尝试写一个简单的HTTP服务器、一个聊天室或者一个简易RPC服务。只有真正开始写业务代码你才会发现muduo的线程模型、连接管理、定时器这些API设计的精妙之处。这种发现的乐趣是看任何教程都体会不到的。我在实际学习过程中最大的体会是muduo这套代码是越读越有味道的。一开始看可能觉得有点绕但当你理解了Reactor模式的本质、明白了“one loop per thread”的线程模型为什么会成为高并发服务器的经典范式再看其他网络库时就会有一种豁然开朗的感觉。而这一切的起点就是今天这篇编译安装教程。希望你能亲手完成整个流程把这个强大的工具真正装进自己的武器库。