ARTICLE DETAIL

资讯详情

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

Linux C++服务器开发完整指南:从网络并发到调试部署全流程

Linux C++服务器开发完整指南:从网络并发到调试部署全流程 在Linux上做C服务器端开发跟我刚开始学编程时写的那种“跑完就退出”的控制台程序完全是两回事。你要面对的是长时间运行的进程、高并发的网络连接、频繁的内存分配与释放、以及随时可能出现的段错误和死锁。这套流程里涉及的工具链、编程模型和调试手段如果没人带着梳理一遍靠自己在网上零散搜索效率低不说还容易踩坑。这篇总结我想把自己在Linux下用C做服务器端开发的完整流程串一遍从环境搭建、构建工具、网络并发模型到性能分析、单元测试和部署最后再分享一些实际排障中总结的技巧。内容覆盖我这些年真实用过的工具和踩过的坑目的只有一个让刚接触服务器端开发的同学能少走弯路让已经入门的同行能对照查漏补缺。1. 服务器端开发的核心问题与整体流程1.1 服务器端开发到底在解决什么问题服务器端程序跟普通软件最大的区别在于它要持续提供服务。一个线上服务的进程可能几个月都不重启一次需要同时接受成千上万个客户端的连接请求处理各种协议解析、业务计算、数据存取。这背后核心挑战有三个高并发下的资源利用效率、长时间运行下的稳定性、以及复杂业务下的可维护性。C在服务器端之所以还有不可替代的地位就是因为它能在这些问题上给出极致控制力。你可以自己管理内存布局可以精确控制线程数量可以对网络I/O进行最细粒度的调度。像一些对延迟和吞吐要求极高的场景——比如量化交易、游戏服务器、分布式存储引擎、数据库内核——C依然是主力语言。很多初学者容易陷入一个误区一上来就研究某个框架的用法却忽略了基础。实际上服务器端开发的底层能力就那么几块Linux操作系统的基本操作与进程管理、TCP/IP网络编程模型、并发与同步机制、内存管理。框架永远在变这些底层能力才是你真正的竞争力。1.2 一整套开发流程的宏观认识我这里说的“开发流程”不只指敲代码那一步而是从环境准备、编码调试、验证优化到部署上线的全过程。我一般把流程分成六个阶段环境准备安装Linux系统、配置编译器、搭建开发环境。编码实现使用编辑器或IDE写代码借助构建系统管理工程。调试定位通过GDB、日志等手段排查崩志、死锁、逻辑错误。性能优化用profiler找出热点优化CPU、内存、网络I/O。测试验证编写单元测试、集成测试保证功能正确性。部署运维打包发布、配置守护进程、监控告警。流程里的每一环都有一堆工具可以用而工具选型直接影响到你的开发效率和线上稳定性。建议刚入门的时候优先把一条主线打通GCC CMake VSCode GDB GTest。这一套组合免费、轻量、社区资料丰富足够覆盖大多数场景。2. 开发环境编译器、构建系统与编辑器2.1 编译器选型GCC还是ClangLinux下C编译器件默认首选就是GCCGNU Compiler Collection因为它是系统自带工具链的核心兼容性最好。几乎所有Linux发行版都预装了GCC用g命令就能直接编译。Clang的优势在于编译速度快、错误提示更加友好对代码静态检查的能力也更强。现在很多项目会同时用两种编译器做交叉验证因为两者对C标准的实现细节存在差异能提前暴露一些未定义行为和移植性问题。我自己平时主力用GCC但在做跨平台库开发时会特意用Clang再编一遍。另外一个值得注意的点是编译器版本如果要用C17甚至C20的新特性比如std::filesystem、std::string_view、协程就需要较新版本的GCC至少8.0以上推荐10.0以上。可以通过两条命令快速查看和安装g --version sudo apt install g # Debian/Ubuntu系列 sudo yum install gcc-c # CentOS/RHEL系列2.2 CMake与Make构建系统怎么选构建系统解决的是“一堆源文件如何编译链接成可执行文件”的问题。最底层的工具是Make通过Makefile描述编译规则。但手写Makefile非常痛苦尤其是大型工程依赖关系复杂之后维护成本极高。所以现在事实上的标准是CMake。CMake并不是直接编译代码的工具它生成构建脚本默认生成Makefile或Ninja文件再由底层工具去执行编译。写CMakeLists.txt比手写Makefile简单直观得多。一个最基础的服务器工程CMake配置大概长这样cmake_minimum_required(VERSION 3.16) project(MyServer LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) add_executable(server src/main.cpp src/network.cpp src/thread_pool.cpp ) target_link_libraries(server pthread)然后是经典的构建步骤mkdir build cd build cmake .. make -j$(nproc)在CMake配置里我强烈建议开启一个选项-DCMAKE_BUILD_TYPERelWithDebInfo。它会在编译优化O2的同时保留调试信息线上排障时作用非常大。如果完全用Release模式往往崩溃后GDB看到的都是乱码非常被动。2.3 用VSCode快速搭一套C开发环境服务器端开发不一定非要厚重IDEVSCode配合插件组合手感非常接近现代IDE。我推荐的插件组合是C/C微软官方出品、CMake Tools、Remote - SSH。其中Remote - SSH插件特别适合一个场景你本机是Windows或Mac但开发目标是一台Linux服务器。直接在VSCode里远程连接服务器插件会自动在远端装上服务器端这样你本地编辑、远程编译、远程调试体验和本地几乎无差别。配置调试的时候需要生成.vscode/launch.json。这里有一个常见的坑默认配置里program路径不对会导致F5按下去启动不了程序。建议在CMake Tools插件里用命令“CMake: Set Launch Target”选定目标再自动生成配置不容易出错。还要说一个细节就是C的代码跳转和智能提示依赖compile_commands.json这个文件。用CMake时只要在配置命令里加一行cmake -DCMAKE_EXPORT_COMPILE_COMMANDSON ..然后在.vscode/c_cpp_properties.json里指定这个文件的路径VSCode的代码补全、跳转就会准确很多尤其是工程里用了一些第三方库头文件的时候。2.4 GDB调试定位问题的基本功说到调试GDB是Linux后端开发绕不开的核心工具。很多新手不习惯命令行调试觉得不如IDE可视化方便。但GDB有两个核心优势是IDE替代不了的可以挂在线上进程上直接看现场以及能在任意复杂条件下设置条件断点。最基本的GDB用法要熟练gdb ./server break main # 打断点 run # 运行 bt # 查看调用堆栈 info threads # 查看线程信息 thread N # 切换线程 frame N # 切换栈帧 print var # 打印变量 next / step # 单步跳过 / 单步进入实际工作中碰到程序崩溃最常做的操作就是跑一次gdb看堆栈。如果你的程序是守护进程或线上服务记得先ulimit -c unlimited开启核心转储崩溃后系统会在当前目录生成core文件然后gdb ./server core这个命令能直接告诉你崩溃时程序停在哪一行。把core文件保留下来反复分析能解决大量疑难crash问题。3. 核心编程模型网络与并发3.1 socket编程与TCP服务端的基本套路服务器端开发最绕不开的就是网络编程而TCP服务端的编程模型其实非常固定。核心流程可以概括为socket()创建套接字、bind()绑定地址、listen()开始监听、accept()接受连接然后收发数据。一个基本的TCP服务端骨架如下// 创建 socket int listen_fd socket(AF_INET, SOCK_STREAM, 0); // 设置地址复用解决 TIME_WAIT 问题 int opt 1; setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, opt, sizeof(opt)); // 绑定 struct sockaddr_in addr; addr.sin_family AF_INET; addr.sin_addr.s_addr htonl(INADDR_ANY); addr.sin_port htons(8080); bind(listen_fd, (struct sockaddr*)addr, sizeof(addr)); listen(listen_fd, 128); while (true) { int conn_fd accept(listen_fd, nullptr, nullptr); // 处理新的连接 }这段代码看起来简单但每个环节都有讲究。比如SO_REUSEADDR这个选项刚做开发的同学经常忽略。如果服务器主动重启大量连接处于TIME_WAIT状态时bind会失败报“Address already in use”设置了这个选项能缓解大部分重启场景的问题。还有一个新手容易踩的坑socket收发缓冲区与TCP粘包问题。TCP是字节流协议一次read()可能读到半个应用层包也可能读到多个包。这个不是代码bug是TCP的本质特性。需要在应用层定义消息边界常用方案有固定长度报文头 消息体、特殊分隔符、TLVType-Length-Value格式这一层不在业务代码里处理好线上就是各种灵异问题。3.2 epoll多路复用从select到epoll的演进最早的服务器模型是“一个连接用一个进程/线程处理”这在高并发场景下完全不可行因为线程和进程创建销毁的开销太大了。后来开始使用多路复用技术也就是让一个线程同时监听大量文件描述符是否有事件到来。Linux平台先后有select、poll但它们都有一个问题每次调用都要把大量fd拷贝进内核内核线性扫描判断哪些fd就绪连接数上去之后效率直线下降。epoll的出现解决了这些痛点它通过三个接口工作int epfd epoll_create(1); struct epoll_event ev; ev.events EPOLLIN; // 监听可读事件 ev.data.fd listen_fd; epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, ev); while (true) { struct epoll_event events[1024]; int n epoll_wait(epfd, events, 1024, -1); for (int i 0; i n; i) { // 处理就绪的事件 } }当某个fd就绪时内核会直接把它放入就绪列表应用程序从epoll_wait返回后就只处理真正就绪的fd不需要扫描全部连接。而且epoll支持**边缘触发ET和水平触发LT**两种模式。LT是默认模式只要有数据就会一直通知ET则是只在状态变化时通知一次读取必须一次性把数据读完否则会丢失事件。我的建议是新手先用LT模式把业务逻辑跑通再考虑ET模式。ET模式虽然效率高但对代码要求极其严格读缓冲区大小没算好、循环没写对就会出现大量数据滞留的bug排查起来相当痛苦。3.3 并发模型线程池与Reactor有了epoll之后还需要考虑业务处理模型。最常见的高性能服务架构是Reactor模式主线程只负责监听和接受连接通过epoll把I/O事件分发给线程池中的工作线程处理。线程池的核心价值在于避免线程频繁创建销毁的开销同时限制并发数量防止线程过多导致上下文切换开销反超收益。一个简单的线程池需要这几个要素任务队列、互斥锁、条件变量、固定数量的工作线程。我写过一个极简的线程池核心逻辑大概就是std::mutex mtx; std::condition_variable cv; std::queuestd::functionvoid() tasks; std::vectorstd::thread workers; // 工作线程循环 while (true) { std::unique_lockstd::mutex lock(mtx); cv.wait(lock, []{ return !tasks.empty() || stop; }); if (stop tasks.empty()) break; auto task std::move(tasks.front()); tasks.pop(); lock.unlock(); task(); }这里一个高频踩坑点是任务队列的长度控制。如果生产速度远大于消费速度队列会无限膨胀最终导致内存耗尽。我一般在任务入队前做一次队列长度检查超过阈值就返回错误或者执行背压策略。这个细节在教科书上很少写但线上系统一定会遇到。另外C11之后std::thread基本取代了pthread_create。但如果用std::thread记得要join()或detach()否则析构时会直接std::terminate。这个坑我在早期开发时踩过好多次程序一退出就崩溃排查老半天才发现是线程对象没处理。3.4 内存池与对象生命周期管理服务器端程序对内存分配的要求很高原因是malloc/new的默认实现存在两个问题一是频繁调用会有系统调用和锁竞争开销二是会产生内存碎片。因此很多高性能服务会在业务层做内存池。内存池的原理是预先申请一大块内存按固定大小切分成小块用链表串起来管理。分配时从空闲链表头部取一块释放时插回链表。因为所有分配的都是固定大小不会有碎片问题而且操作只在用户态完成速度非常快。但我必须提醒一句内存池不是银弹。如果你的服务并不是性能瓶颈型用了反而复杂化。我见过很多项目业务逻辑都没写利索先装了一堆内存池、对象池结果内存池本身管理出bug线上崩得体无完肤。合理的原则是先用系统默认分配跑通用profiler确认瓶颈确实在内存分配上再针对性优化。从现代C的角度讲比内存池优先级更高的是智能指针的正确使用。std::shared_ptr、std::unique_ptr这些是现代C工程的基础设施。在服务器端对象经常跨线程传递裸指针的生命周期很难管理但共享所有权、弱引用这些语义用智能指针表达非常自然。内部实现上引用计数会有原子操作开销但换来的是安全性绝大多数场景都值得。3.5 异步日志服务器必备的基础设施线上服务器不可能靠GDB跟进程绝大多数问题的第一线索都是日志。但服务器端日志跟我们平时printf完全不同同步写磁盘在I/O压力大的时候反而成为性能瓶颈。正确做法是异步日志业务线程只把日志文本写入内存缓冲区后台专门线程负责批量写入磁盘。异步日志里有一个容易忽略的关键指标日志写入延迟峰值。平时平均写入可能只有几毫秒但磁盘I/O抖动时日志线程跟不上会导致缓冲堆积。我处置过一起线上事故高峰期业务线程正常但日志缓冲被撑爆后台线程忙不过来日志线程先崩接着业务逻辑因为日志写入失败连环出问题。所以异步日志必须设计丢弃策略超过阈值就主动丢日志优先保证主流程不中断。常用的日志库是spdlog它是一个纯头文件的C日志库性能很好支持异步模式和多种输出目标。如果只是中小型项目直接用spdlog能省掉很多自己造轮子的时间。我一般会封装一个日志模块接口底层可以用spdlog未来不满意随时换业务代码不感知。4. 性能分析、测试与部署4.1 perf性能剖析找出CPU热点服务器上线之前性能摸底是必须做的。Linux自带的perf工具是性能分析的利器用法不复杂perf record -g -p pid # 对运行中的进程采样-g 记录调用栈 perf report # 生成报告查看热点perf record的原理是基于采样它周期性打断CPU记录当前正在执行的函数地址统计每个函数被采样到的次数百分比基本就反映出了CPU时间分配。perf report打开后按h键能看操作帮助按上下箭头移动按enter展开调用栈。我归纳使用perf时最容易犯的错误直接对生产环境进程采样且采样时间过长导致收集到海量数据报告反而没法看。正确做法是分组采样每次采10秒左右多做几组结合起来看。另外要确保编译时加了-g选项在RelWithDebInfo模式下自动满足否则perf只能显示地址而看不到函数名。优化CPU热点时我的排查顺序一般是先看有没有明显的不合理写法比如热点在字符串拷贝、隐式类型转换再看数据结构和算法复杂度确定无误后才考虑加锁优化、内存池这些更进阶的招。4.2 Valgrind与AddressSanitizer内存问题排查C开发中最头疼的莫过于内存问题越界、使用已释放内存、内存泄漏、重复释放。如果靠代码review去发现效率很低。这里有两个工具非常关键Valgrind和AddressSanitizerASan。Valgrind是一个重量级工具通过模拟执行来检测内存错误只需要用内存报告就能快速定位问题。用法valgrind --leak-checkfull ./server输出会清楚地告诉你在哪一行分配的内存泄漏了、哪一行发生了非法读写。Valgrind最大的缺点就是慢程序运行速度会下降几十倍。所以它适合在测试环境跑不适合接线上。AddressSanitizer则是Google推出的编译器插桩方案性能开销比Valgrind小很多大约2倍左右。用法是在编译时打开sanitizer选项g -fsanitizeaddress -g -O1 main.cpp -o server运行程序后如果发生内存错误会直接打印出错位置和调用栈实用性非常高。我们在CI上就常年开启ASan跑一遍测试内存问题大多能在合并前暴露。我的建议是日常开发用ASan核心模块验收前再用Valgrind扫一遍泄漏组合起来覆盖全面。4.3 GTest与单元测试服务器端项目代码量大依赖关系复杂如果没有自动化测试每次改动都感觉像在踩地雷。Google的GTest是C社区最流行的测试框架配合pthread即可轻松运行一个测试先写测试代码比如#include gtest/gtest.h int add(int a, int b) { return a b; } TEST(AddTest, Positive) { EXPECT_EQ(add(1, 2), 3); }然后编译时链接gtest库g -stdc17 test.cpp -lgtest -lgtest_main -lpthread -o test ./test这里要提醒一个容易踩的坑测试代码必须和被测代码以相同编译选项编译。如果被测代码用了Release模式而测试代码是Debug模式二者对某些内存布局、宏定义的期望不一致测试结果会不可信。写了单元测试后我强烈建议把它接入CI持续集成流程。GitHub Actions、GitLab CI都是常用选择每次push代码自动跑测试能极大减少线上回归事故。服务器端项目的CI里至少要包含三步编译、跑单测、用ASan再跑一遍。4.4 部署Docker与守护进程管理部署环节C服务器程序最常见的形态就是静态编译的可执行文件加配置文件。现代部署首选Docker容器好处是环境隔离避免了“在我机器上能跑”的经典问题。一个极简的Dockerfile可以写成FROM ubuntu:22.04 RUN apt-get update apt-get install -y libstdc6 WORKDIR /app COPY ./server /app/server COPY ./conf /app/conf ENTRYPOINT [./server]编译时尽量用-static-libstdc -static-libgcc把C运行时静态链接进二进制这样对基础镜像的依赖更小。进程管理上不能用nohup ./server 这种原始方式。推荐用systemd管理服务。配置一个service文件放在/etc/systemd/system/下就能实现开机自启、崩溃自动重启、日志统一管理。最常用的systemd命令systemctl start myserver # 启动服务 systemctl restart myserver # 重启 systemctl status myserver # 查看状态 journalctl -u myserver -f # 查看实时日志systemd的好处是可以配置Restartalways进程意外退出后自动拉起这在线上是刚需。很多资深开发者的经验是线上服务一定要设置守护进程自动重启但重启策略要配合轻微的延迟比如RestartSec5避免因故障快速抖动导致重启风暴。5. 常见问题与排查技巧实录5.1 常见问题速查表在开发Linux C服务器时我整理了一张高频问题排查表每一条都是实战验证过的现象常见原因排查命令 / 方法服务启动时报Address already in use端口被占用或TIME_WAIT状态netstat -tlnp查看端口占用检查SO_REUSEADDR程序崩溃但看不到任何日志可能访问了野指针或栈溢出ulimit -c unlimited用core文件gdb看堆栈高并发下吞吐上不去锁竞争严重、大量上下文切换perf top查看热点vmstat观察上下文切换次数内存只涨不降疑似内存泄漏长时间跑Valgrind或用top观察RES内存趋势CPU使用率异常高死循环或忙轮询top找出进程perf record采样定位函数连接断开、数据错乱TCP粘包/半包问题检查应用层报文设计是否有消息边界处理日志文件越来越大没有配置轮转使用logrotate或日志库的轮转能力这里面每一个现象都值得展开讲但我最想说的一条经验是排查问题之前先做一次“现场采集”——进程状态、内存概况、网络连接状态、日志尾部用几条命令一起抓下来然后再分析。很多人一出问题就慌先重启服务结果现场全没了再想排错就难了。5.2 几个实战排错案例第一个案例是死锁导致的“假死”。现象是服务对外表现正常但某个接口的响应时间逐渐变高最终完全无响应。排查时用pstack pid打印所有线程的堆栈发现两个线程分别持锁等待对方释放。找到了堆栈就很好定位最后是通过调整锁的获取顺序解决的。pstack在排查死锁时几乎是必杀技。第二个案例是内存泄漏。服务运行一周后内存从200MB慢慢涨到2GB用top很容易观察到。但定位泄漏点比较麻烦因为泄漏发生在各种业务路径上。我的做法是先通过Valgrind的--leak-checkfull跑一小段集成测试把泄漏点暴露出来如果无法复现就用gdb在malloc处下条件断点配合计数逻辑统计分配热点。实际经验告诉我大多数内存泄漏都出在对象被放入容器后忘记清理或者裸指针在不同模块间转手时所有权不清。第三个案例是网络性能瓶颈。压测时发现并发到2000后性能线性下降perf top显示热点在一个锁的获取函数上。后来发现是连接池里的互斥锁竞争太严重把这个锁改成分片锁多个桶、每桶一把锁性能立刻提升了一倍。这个案例给我的启发是高并发场景下锁的粒度设计比算法本身更重要。5.3 给新手的几条避坑建议如果让我把几年经验浓缩成几句给新手的话我会说下面几条第一编译选项要养成固定习惯。永远用-Wall -Wextra开启警告永远不要用-O0跑线上性能测试。-O2和-O3之间差距通常不大但-O2的稳定性更好。第二代码提交前自己过一遍静态检查。可以装clang-tidy或cppcheck它们能发现大量的逻辑隐患比如未初始化的变量、可能为空的裸指针、越界访问。很多安全事故静态检查工具早就能查出来。第三日志格式要统一。我见过最崩溃的日志就是每个模块各写各的格式排障时grep出来根本对不上。建议日志统一包含时间、线程ID、日志级别、模块名、关键字字段这样后续用脚本分析会非常省力。第四修改线上代码前先备份和压测。别觉得麻烦上线前的压测能暴露80%的隐患这比线上事故后再救火划算得多。很多人说压测浪费时间但实际上一次线上事故的损失是压测成本的十倍百倍。6. 一些工具和资源的补充介绍6.1 Linux常用命令在服务器开发中的角色服务器开发离不开Linux命令行有些命令几乎是每天都要用的。ps、top、netstat、ss、lsof、df、du、free、tail这些命令构成你的日常运维工具箱。比如ss -tnp比旧版netstat更推荐用来查看连接状态非常高效free -h看内存使用df -h看磁盘空间快满的时候及时清理日志避免磁盘写满导致服务异常。有一个很实用的命令组合排查一个端口是否被监听ss -lntp | grep 8080如果怀疑某个进程占用了大量fd用ls /proc/pid/fd | wc -l查看进程打开的文件描述符数量超过系统默认上限通常是1024就会连接失败。这个fd耗尽问题在生产环境高频出现知道这几条命令可以快速定位。6.2 善用STL与C11以上特性服务器开发中STL用得好能节省大量时间。std::vector适合连续随机访问场景std::map和std::unordered_map各自适合有序查找和快速查找。但有一个常见问题是新手把std::map当作万能容器插入频繁的场景应该用std::unordered_map需要保持插入顺序的场景可以考虑自己用vector或list实现。容器选型错了在高并发下就是性能灾难。C11之后的几个特性我特别推荐在服务器端大量使用std::shared_ptr管理资源生命周期、std::function和lambda简化回调、std::thread与std::mutex标准并发、auto类型推导减少冗余。C17的std::filesystem在处理配置文件路径、日志轮转时会非常方便。这些现代C特性比你手写一套自造方案要可靠得多。我在实际开发中还有一个体会值得用std::atomic的地方就不要用std::mutex。很多“读多写少”的计数器场景原子变量就够了完全没有必要引入锁。当然复合操作check-then-act还是需要锁这个度要靠经验和测试把握。6.3 原理钻研从“能用”到“理解”很多人写完服务能跑就不再深究了。但如果你想在服务器开发这条路上走得更远原理层面的东西必须补。比如你一直在用epoll但有没有想过epoll的epoll_event里存的是用户态回调还是内核态的什么结构为什么epoll_ctl的EPOLL_CTL_MOD代价很高这些理解决定了你在遇到复杂问题是能不能快速给出方案。我建议的学习路线是先吃透《UNIX环境高级编程》APUE里的进程、线程、I/O模型再看《UNIX网络编程》UNP里的网络编程模型最后结合内核相关书籍理解epoll、调度器到底做了什么。这个路线虽然硬核但能真正让你从“调API”上升为“设计系统”的层面。网上零散的文章很多系统性地啃一两本书效率反而更高。如果你喜欢边实践边学可以尝试去复现一些开源项目的小模块比如自己写一个简版的Reactor框架、一个内存池、一个异步日志库。把这些轮子造一遍比自己去看理论书更容易内化成能力。我在带新人时就要求他们先实现一个小型echo server然后逐步加线程池、加日志、加协议解析一套走下来基础就扎实了。6.4 环境和调试细节补充最后补充一些系统层面的配置细节。开发Linux服务器时很多“奇怪”的性能问题其实源于系统参数。最先要检查的有三个ulimit -n # 最大文件描述符数默认1024服务器建议65535 cat /proc/sys/net/ipv4/tcp_tw_reuse # 是否允许TIME_WAIT复用 cat /proc/sys/net/ipv4/ip_local_port_range # 本地端口范围ulimit -n这个参数如果不调大高并发场景下连接数一上来就报“Too many open files”这是最经典的新手坑。可以在/etc/security/limits.conf里设置* soft nofile 65535 * hard nofile 65535如果你在一台配置较低的机器上开发启动多个进程后发现内存不够排查顺序一般是free -h看总量top看进程占用然后看有没有不需要的进程可以释放。特别是嵌入式Linux开发场景资源有限更需要这种系统级的全局观。我个人的切身体会是服务器端开发这个方向能学会一套工具链的用法并不难难的是在真实出问题时还能冷静地用工具链定位问题根源。希望这篇总结能帮你把整个流程串起来少踩一些我已经踩过的坑。如果你刚入门建议今天就装一台Linux虚拟机照着上面的流程把一个小型服务从编译到跑通走一遍这个过程比看十篇文章都有用。
返回列表