Linux开发工具详解(一):软件包管理、Vim、GCC/G++、Makefile与进度条实战
发布时间:2026/10/3 12:47:18 作者:尧图编辑部 阅读量:1,286
:软件包管理、Vim、GCC/G++、Makefile与进度条实战)
Linux开发工具详解一软件包管理、Vim、GCC/G、Makefile与进度条实战 星恒随风个人主页❄️ 个人专栏《指针合集》《C语言基础》《数据结构》《机器学习导论》《前端基础》《python基础》《C从入门到入土》《Linux的学习之旅》✨ 数据即知识压缩即智能文章目录Linux开发工具详解一软件包管理、Vim、GCC/G、Makefile与进度条实战前言Linux开发并不是只会写代码1. 从“写代码”到“完成一个程序”一、Linux软件包管理1. 为什么需要软件包管理器1.1 直接从源码安装有什么问题2. 软件包与包管理器是什么关系2.1 可以类比手机应用商店3. 不同Linux发行版使用什么包管理器3.1 Debian / Ubuntu3.2 CentOS 7等较老的Red Hat系环境3.3 Fedora、RHEL 8/9等较新的系统二、apt / dnf / yum常用操作1. 查询软件1.1 Ubuntu1.2 RHEL / Fedora2. 安装软件2.1 Ubuntu2.2 Fedora / RHEL3. 删除软件4. 更新软件包索引三、什么是软件源1. 软件到底从哪里下载2. 为什么会有镜像源3. 国内常见镜像站四、VimLinux终端中的经典编辑器1. vi与Vim是什么关系1.1 vi1.2 Vim五、Vim最重要的三个模式1. 正常模式 Normal Mode2. 插入模式 Insert Mode3. 末行模式 Command-line Mode六、Vim模式切换必须先掌握1. 最核心切换关系七、Vim正常模式常用命令1. 光标移动1.1 基础移动1.2 按单词移动1.3 行内移动1.4 文件移动2. 删除3. 复制和粘贴4. 撤销与重做八、Vim查找1. 向下查找2. 向上查找九、Vim常用配置1. 用户配置文件2. 一份基础配置十、GCC与G到底是什么1. GCC2. gcc与g十一、一个C程序到底是怎样变成可执行文件的1. 四个阶段十二、第一阶段预处理1. 预处理主要做什么2. 只进行预处理十三、第二阶段编译1. 从C代码到汇编2. 为什么要经过汇编十四、第三阶段汇编十五、第四阶段链接十六、为什么printf不用自己实现十七、静态库与动态库1. Linux中的常见扩展名2. 静态链接3. 动态链接4. 查看程序依赖的动态库5. 查看文件类型十八、GCC/G常用选项1. 输出文件2. 仅编译不链接3. 生成调试信息4. 开启警告5. 优化选项十九、为什么需要Makefile1. 一个文件时还没问题2. 多文件项目开始麻烦二十、make与Makefile是什么关系1. make2. Makefile二十一、Makefile最核心的规则1. 基本语法二十二、make如何判断要不要重新构建1. 基于文件修改时间二十三、一个多文件Makefile二十四、clean为什么要写成.PHONY1. 普通clean2. 潜在问题3. 声明伪目标二十五、Makefile变量1. 定义变量二十六、自动变量二十七、模式规则二十八、一份更实用的Makefile二十九、综合实践Linux终端进度条1. 为什么这个案例值得做三十、\r与\n不是一回事1. \n换行2. \r回车三十一、为什么必须fflush(stdout)1. stdout通常存在缓冲2. 手动刷新三十二、先写一个倒计时三十三、进度条核心代码三十四、进度条为什么能够“动起来”1. 第一次打印2. 下一次重新覆盖三十五、完整开发流程总结总结1. 软件包管理2. Vim3. GCC/G4. Makefile5. 最终形成前言Linux开发并不是只会写代码1. 从“写代码”到“完成一个程序”刚开始学习 C/C 时我们可能只关心写代码 ↓ 点一下运行 ↓ 得到结果IDE 已经帮我们完成了大量工作。但在 Linux 中如果直接使用命令行开发一个程序通常会经历安装开发工具 ↓ 编辑源代码 ↓ 编译 ↓ 链接 ↓ 生成可执行程序 ↓ 运行 ↓ 修改 ↓ 重新构建分别会涉及apt / dnf / yum ↓ 软件包管理 Vim ↓ 代码编辑 GCC / G ↓ 编译与链接 make / Makefile ↓ 自动化构建本文就从这条开发链开始。一、Linux软件包管理1. 为什么需要软件包管理器1.1 直接从源码安装有什么问题理论上Linux 软件完全可以下载源码 ↓ 配置 ↓ 编译 ↓ 链接 ↓ 安装例如./configuremakesudomakeinstall但问题是编译耗时 依赖复杂 版本不好管理 卸载困难因此 Linux 发行版通常会把软件提前编译 打包 建立依赖关系形成软件包然后放入软件仓库 Repository用户通过包管理器完成安装。2. 软件包与包管理器是什么关系2.1 可以类比手机应用商店可以粗略理解为软件包 ≈ App 软件仓库 ≈ 应用商店服务器 包管理器 ≈ 应用商店客户端用户执行sudoaptinstallgit背后实际上可能经历查询仓库 ↓ 找到 git 软件包 ↓ 分析依赖 ↓ 下载依赖 ↓ 下载 git ↓ 安装 ↓ 记录安装信息因此包管理器最重要的价值之一就是自动处理依赖3. 不同Linux发行版使用什么包管理器3.1 Debian / Ubuntu主要使用apt例如sudoaptupdatesudoaptinstallgcc3.2 CentOS 7等较老的Red Hat系环境很多课程环境使用yum例如sudoyuminstallgcc3.3 Fedora、RHEL 8/9等较新的系统现代 Red Hat 系环境核心已经转向dnf例如sudodnfinstallgcc部分系统中的yum主要作为兼容入口继续存在。所以实际使用时应先确定cat/etc/os-release查看自己的发行版。二、apt / dnf / yum常用操作1. 查询软件1.1 Ubuntuaptsearchgit查看详细信息aptshowgit1.2 RHEL / Fedoradnf searchgit也可以dnf infogit旧环境可能使用yum list|grepgit2. 安装软件2.1 Ubuntusudoaptinstall-ygit2.2 Fedora / RHELsudodnfinstall-ygit旧 CentOSsudoyuminstall-ygit其中sudo ↓ 以管理员权限执行 -y ↓ 自动确认安装3. 删除软件UbuntusudoaptremovegitFedora / RHELsudodnf removegit4. 更新软件包索引Ubuntu 常见sudoaptupdate升级sudoaptupgradeFedora / RHELsudodnf upgrade三、什么是软件源1. 软件到底从哪里下载执行sudoaptinstallvim并不是从互联网随机搜索 Vim而是从已经配置好的软件仓库下载。2. 为什么会有镜像源官方软件源可能距离用户较远。于是很多组织建立Mirror 镜像站定期同步官方仓库。这样用户就可以从距离较近的服务器下载软件通常速度更稳定。3. 国内常见镜像站常见的有清华大学 TUNA 中国科学技术大学 USTC 阿里云镜像站 上海交通大学镜像站具体是否需要更换应根据所在网络 发行版 当前下载速度决定。四、VimLinux终端中的经典编辑器1. vi与Vim是什么关系1.1 vivi是 Unix/Linux 中非常经典的多模式文本编辑器1.2 VimVimVi IMproved可以理解成vi 的增强版本增加了很多功能例如语法高亮 更丰富的命令 插件能力 可视模式五、Vim最重要的三个模式1. 正常模式 Normal Mode进入 Vimvimtest.c默认进入正常模式这里主要用于移动 复制 删除 粘贴 查找 执行命令而不是普通地输入文本。2. 插入模式 Insert Mode在正常模式按i即可开始输入。常用方式i ↓ 在当前光标位置前开始输入 a ↓ 在当前光标位置后开始输入 o ↓ 在当前行下方创建新行返回正常模式Esc3. 末行模式 Command-line Mode正常模式输入:进入末行模式。常用于保存 退出 配置 替换 跳转例如:w保存。:q退出。:wq保存并退出。:q!放弃修改并强制退出。六、Vim模式切换必须先掌握1. 最核心切换关系i / a / o Normal ─────────────→ Insert ↑ | └──────── Esc ────────┘ : Normal ─────────────→ Command-line ↑ | └──────── Esc ────────┘如果刚开始使用 Vim 时发现键盘输入出来的不是文字第一反应应该是我现在处于什么模式七、Vim正常模式常用命令1. 光标移动1.1 基础移动h → 左 j → 下 k → 上 l → 右这是 Vim 最经典的一组按键。1.2 按单词移动w ↓ 下一个单词开头 e ↓ 当前/下一个单词结尾 b ↓ 上一个单词开头1.3 行内移动0 ↓ 行开头 ^ ↓ 本行第一个非空白字符 $ ↓ 行末尾1.4 文件移动gg ↓ 文件开头 G ↓ 文件结尾跳到第 50 行50G2. 删除删除当前字符x删除当前行dd删除 5 行5dd3. 复制和粘贴复制当前行yy复制 5 行5yy粘贴p于是经典组合yy p就是复制当前行 ↓ 粘贴4. 撤销与重做撤销u重做Ctrl r八、Vim查找1. 向下查找/main继续寻找下一个n反方向N2. 向上查找?main九、Vim常用配置1. 用户配置文件一般使用~/.vimrc例如vim~/.vimrc2. 一份基础配置syntax on set number set tabstop4 set shiftwidth4 set expandtab set autoindent set smartindent set cursorline其中set number ↓ 显示行号 tabstop ↓ Tab 显示宽度 shiftwidth ↓ 自动缩进宽度 expandtab ↓ 把 Tab 转为空格对于刚开始学习 Linux 开发来说不需要一开始就在 Vim 上安装大量插件。先掌握编辑 保存 查找 复制 删除 模式切换更重要。十、GCC与G到底是什么1. GCCGCC 可以理解为GNU Compiler Collection其中支持C C Fortran ...多种语言。2. gcc与g日常使用中gcc test.c通常用于 C。g test.cpp通常用于 C。对于 C 项目更推荐直接使用g因为它会自动按照 C 的方式完成编译 以及 C 标准库链接十一、一个C程序到底是怎样变成可执行文件的1. 四个阶段从hello.c到hello通常可以分成源文件 .c | | 预处理 v .i 文件 | | 编译 v .s 汇编文件 | | 汇编 v .o 目标文件 | | 链接 v 可执行程序也就是预处理 ↓ 编译 ↓ 汇编 ↓ 链接十二、第一阶段预处理1. 预处理主要做什么例如宏替换 头文件展开 条件编译 去除注释代码#includestdio.h#defineNUM10这些以 # 开头的预处理指令首先由预处理器处理。2. 只进行预处理gcc-Ehello.c-ohello.i其中-E ↓ 完成预处理后停止可以vimhello.i查看展开后的结果。你会发现一个非常小的#includestdio.h最终可能展开出非常多内容。十三、第二阶段编译1. 从C代码到汇编执行gcc-Shello.i-ohello.s得到hello.s这里会进行语法分析 语义分析 中间表示 优化 代码生成最终得到汇编代码2. 为什么要经过汇编C/C 是高级语言CPU 真正能够执行的是机器指令汇编语言可以看作机器指令的人类可读表示所以编译器需要逐步把高级语义转换成底层机器代码。十四、第三阶段汇编执行gcc-chello.s-ohello.o得到hello.o.o文件已经包含机器代码但通常还不能直接运行。因为程序中可能还引用printf malloc 其他源文件中的函数这些符号还没有全部解决。十五、第四阶段链接gcc hello.o-ohello得到hello然后./hello运行。十六、为什么printf不用自己实现假设#includestdio.hintmain(){printf(hello\n);}stdio.h主要提供printf 的声明真正的实现位于C 标准库中。链接器负责把程序中对 printf 的引用与库中的printf 实现联系起来。十七、静态库与动态库1. Linux中的常见扩展名静态库.a动态库.soWindows 常见.lib .dll2. 静态链接静态链接时需要的库代码 ↓ 进入最终可执行文件优点运行时对外部库依赖较少 部署简单缺点可执行文件通常更大 多个程序可能重复包含相同库代码 库升级后通常需要重新链接3. 动态链接动态链接时程序文件 运行时需要的共享库共同完成执行。优点多个程序可共享同一个库 程序体积通常更小 库升级更方便缺点运行环境必须存在兼容的共享库4. 查看程序依赖的动态库ldd ./hello可能看到libc.so.6 libstdc.so.6 ...5. 查看文件类型filehello这是排查ELF 类型 架构 是否动态链接 是否包含调试信息非常常用的命令。十八、GCC/G常用选项1. 输出文件gcc main.c-oapp2. 仅编译不链接gcc-cmain.c-omain.o3. 生成调试信息gcc main.c-g-oapp后面的 GDB 调试依赖-g生成的调试信息。4. 开启警告推荐gcc main.c\-Wall\-Wextra\-oapp需要注意-Wall并不是“开启世界上所有 GCC 警告”。它代表开启一组常用的重要警告而-Wextra会再增加一组检查。对于学习阶段非常建议不要用 -w 把警告全部关掉很多编译器 Warning 正是在提前帮你发现 Bug。5. 优化选项常见-O0 -O1 -O2 -O3 -Og -Os其中-O0 ↓ 基本不优化也是默认等级 -O2 ↓ 常见发布构建优化等级 -O3 ↓ 更加激进的优化 -Og ↓ 兼顾一定优化与调试体验学习 GDB 时可以gcc main.c\-g\-O0\-Wall\-Wextra\-oapp避免过度优化导致变量被优化掉 代码执行顺序和源码差异较大十九、为什么需要Makefile1. 一个文件时还没问题只有main.c时gcc main.c-oapp非常简单。2. 多文件项目开始麻烦假设main.c process.c process.h util.c util.h手工gcc-cmain.c gcc-cprocess.c gcc-cutil.c gcc main.o process.o util.o-oapp如果只修改了 process.c其实只需要重新编译 process.c ↓ 重新链接没有必要重新编译所有文件。于是需要自动分析依赖 增量构建这就是make Makefile二十、make与Makefile是什么关系1. makemake是构建工具2. MakefileMakefile是描述构建规则的文件可以理解成Makefile ↓ 说明项目应该怎么构建 make ↓ 读取这些规则并执行二十一、Makefile最核心的规则1. 基本语法target: prerequisites command注意command前通常必须是Tab不是普通空格。例如app: main.c gcc main.c -o app这里target ↓ app prerequisite ↓ main.c recipe ↓ gcc main.c -o app二十二、make如何判断要不要重新构建1. 基于文件修改时间假设app: main.o gcc main.o -o appmake 会比较main.o 的修改时间 和 app 的修改时间如果main.o 比 app 更新说明依赖发生了变化于是重新执行链接。所以 make 能实现增量构建二十三、一个多文件Makefileapp: main.o process.o gcc main.o process.o -o app main.o: main.c process.h gcc -c main.c -o main.o process.o: process.c process.h gcc -c process.c -o process.o依赖图app / \ / \ main.o process.o | | main.c process.c \ / process.h修改process.c时只重新构建 process.o ↓ 重新链接 app二十四、clean为什么要写成.PHONY1. 普通cleanclean: rm -f *.o app使用makeclean2. 潜在问题如果目录中恰好真的存在clean这个文件make 可能认为目标clean已经存在并且无需构建。3. 声明伪目标.PHONY: clean clean: rm -f *.o app.PHONY的含义不是简单的忽略所有时间比较更准确地说clean不代表一个真正需要生成的文件而代表一个需要执行的动作。因此makeclean会执行对应 recipe而不会把它当普通文件目标处理。二十五、Makefile变量1. 定义变量CC gcc TARGET app使用$(CC) $(TARGET)例如CC gcc TARGET app $(TARGET): main.c $(CC) main.c -o $(TARGET)二十六、自动变量Makefile 中非常重要的一组变量$ ↓ 当前目标 $ ↓ 第一个依赖 $^ ↓ 所有依赖例如app: main.o process.o gcc $^ -o $展开gcc main.o process.o-oapp二十七、模式规则例如%.o: %.c gcc -c $ -o $表示任意 xxx.o 依赖 同名 xxx.c于是main.o ← main.c process.o ← process.c util.o ← util.c都可以复用这一条规则。二十八、一份更实用的MakefileCC gcc CFLAGS -Wall -Wextra -g TARGET processbar SRC $(wildcard *.c) OBJ $(SRC:.c.o) $(TARGET): $(OBJ) $(CC) $^ -o $ %.o: %.c $(CC) $(CFLAGS) -c $ -o $ .PHONY: clean clean: rm -f $(OBJ) $(TARGET)执行make编译。清理makeclean二十九、综合实践Linux终端进度条1. 为什么这个案例值得做它可以一次串联\r 和 \n stdout 缓冲 fflush 数组 字符串 函数拆分 多文件编译 Makefile三十、\r与\n不是一回事1. \n换行printf(hello\n);让输出进入下一行。2. \r回车printf(\r);表示光标回到当前行开头而不一定进入下一行。因此10% 20% 30%如果想让它们一直刷新在同一行就可以利用\r三十一、为什么必须fflush(stdout)1. stdout通常存在缓冲例如printf(hello);sleep(3);你可能发现hello没有立刻显示。原因之一是stdout 缓冲机制2. 手动刷新printf(hello);fflush(stdout);sleep(3);fflush(stdout)要求把当前标准输出缓冲中的内容立即提交进度条之所以能够实时刷新很大程度上依赖fflush(stdout);三十二、先写一个倒计时#includestdio.h#includeunistd.hintmain(void){inti10;while(i0){printf(%-2d\r,i);fflush(stdout);--i;sleep(1);}printf(\n);return0;}关键\r ↓ 不断回到行首 fflush ↓ 立即显示三十三、进度条核心代码#includestdio.h#includestring.h#includeunistd.h#defineWIDTH50voidProgressBar(void){charbar[WIDTH1];memset(bar,0,sizeof(bar));constchar*spinner|/-\\;intspinner_lenstrlen(spinner);for(inti0;i100;i){intcounti*WIDTH/100;memset(bar,,count);bar[count]\0;printf([%-50s] [%3d%%] [%c]\r,bar,i,spinner[i%spinner_len]);fflush(stdout);usleep(50000);}printf(\n);}三十四、进度条为什么能够“动起来”1. 第一次打印[ ] [10%]末尾\r光标回到本行开头。2. 下一次重新覆盖[ ] [20%]并没有打印 100 行而是不断覆盖同一行于是视觉上产生动画效果。三十五、完整开发流程总结现在可以把 Linux 基础开发工具串起来apt / dnf ↓ 安装开发工具 ↓ Vim ↓ 编辑 C/C ↓ GCC / G ↓ 预处理 → 编译 → 汇编 → 链接 ↓ Makefile ↓ 自动化构建 ↓ 运行程序这套流程的意义并不是“以后不用 IDE”而是让我们真正知道IDE 的 Build、Run、Clean、Debug 按钮背后究竟发生了什么。总结这一阶段最重要的不是死记几十条命令而是理解整个 Linux 开发工具链。1. 软件包管理apt dnf yum解决软件安装 依赖处理 升级 卸载2. Vim核心是Normal Insert Command-line三个模式之间的切换。3. GCC/G代码经历.c/.cpp ↓ 预处理 ↓ 编译 ↓ 汇编 ↓ 链接 ↓ 可执行程序4. Makefile本质是描述依赖关系 描述依赖生成方法让重复手工编译变成make5. 最终形成编辑 ↓ 构建 ↓ 运行 ↓ 修改 ↓ 重新构建这才是一个真正的开发循环。