Linux 开发环境与工具链

从Linux基础工具到编译调试、版本控制,一套完整的开发环境与工具链,是入门系统编程的必备基石。


1. 软件包管理器

1.1 核心概念

软件包:提前编译好的程序,类似 Windows 的 .exe 安装包。

包管理器:用于查找、下载、安装、卸载软件包的工具,类似手机应用商店。

CentOS/RHEL:yum (Yellowdog Updater, Modified)

Ubuntu/Debian:apt (Advanced Package Tool)

操作系统生态:决定系统易用性,包含社区论坛、官网文档、软件体系、维护更新速度、系统本身、用户群体。

1.2 软件包依赖

软件之间存在依赖关系(如你的软件依赖 libc.solibc.so 又依赖 ssl.so),包管理器会自动解决依赖。

1.3 yum/apt 常用操作

查看软件包

bash 复制代码
# CentOS
yum list | grep lrzsz
# Ubuntu
apt search lrzsz
apt show lrzsz

安装软件

bash 复制代码
# CentOS
sudo yum install -y lrzsz
# Ubuntu
sudo apt install -y lrzsz

卸载软件

bash 复制代码
# CentOS
sudo yum remove [-y] lrzsz
# Ubuntu
sudo apt remove [-y] lrzsz

注意事项:安装需要 sudo 或 root 权限,同一时间只能运行一个 yum/apt 进程。必须保证网络畅通(可 ping www.baidu.com 验证)。

1.4 安装源配置

CentOS:源文件路径 /etc/yum.repos.d/

Ubuntu:源文件路径 /etc/apt/sources.list 和 /etc/apt/sources.list.d/

安装扩展源(CentOS):sudo yum install -y epel-release

2. 编辑器 Vim

2.1 三种核心模式

命令模式 (Normal mode):默认进入,用于控制光标、删除、复制、切换模式。

插入模式 (Insert mode):用于输入文字,按 Esc 回到命令模式。

底行模式 (Last line mode):用于保存、退出、查找、替换,按 Shift + ; 进入。

2.2 模式切换

命令模式 → 插入模式:i (光标前) / a (光标后) / o (下一行)

插入模式 → 命令模式:Esc

命令模式 → 底行模式:Shift + ; (输入 :)

2.3 命令模式常用命令

光标移动

h/j/k/l 左/下/上/右移一格

G 跳转到文件末尾

gg 跳转到文件开头

$ 跳转到行尾

^ 跳转到行首

w 跳转到下一个单词开头

b 跳转到上一个单词开头

nG 跳转到第 n 行(如 15G)

文本操作

x 删除光标后一个字符 X 删除光标前一个字符

dd 删除当前行 ndd 删除 n 行

yy 复制当前行 nyy 复制 n 行

p 粘贴复制的内容

u 撤销上一步操作 Ctrl + r 恢复撤销

r 替换单个字符 R 持续替换直到 Esc

2.4 底行模式常用命令

:w 保存文件

:q 退出 Vim

:wq 保存并退出

:q! 强制退出(不保存)

:set nu 显示行号

:n 跳转到第 n 行(如 :15)

/keyword 向后查找关键字 ; ?keyword 向前查找 ; n 查找下一个

2.5 Vim 配置

系统配置:/etc/vimrc

用户私有配置:~/.vimrc

常用配置项:

bash 复制代码
syntax on       " 语法高亮
set nu          " 显示行号
set shiftwidth=4 " 缩进为4个空格

常用插件:TagList、WinManager(用于文件浏览和函数列表)

3. 编译器 gcc/g++

3.1 编译流程(4 阶段)

  1. 预处理:宏替换、去注释、头文件展开、条件编译 → .i 文件

  2. 编译:生成汇编语言 → .s 文件

  3. 汇编:生成机器码 → .o 目标文件

  4. 链接:生成可执行文件或库文件

3.2 核心编译选项

|-----------------|----------------------|-------------------------------------|
| 选项 | 作用 | 示例 |
| -E | 仅预处理,生成 .i 文件 | gcc -E hello.c -o hello.i |
| -S | 编译到汇编,生成 .s 文件 | gcc -S hello.i -o hello.s |
| -c | 汇编到目标代码,生成 .o 文件 | gcc -c hello.s -o hello.o |
| -o | 指定输出文件名 | gcc hello.o -o hello |
| -g | 生成调试信息(供 gdb 使用) | gcc -g hello.c -o hello |
| -static | 静态链接(将库代码打包进可执行文件) | gcc -static hello.c -o hello_static |
| -shared | 动态链接(默认,依赖系统动态库) | gcc -shared hello.c -o libhello.so |
| -O0/-O1/-O2/-O3 | 优化级别(O0 无优化,O3 最高优化) | gcc -O2 hello.c -o hello |
| -Wall | 生成所有警告信息 | gcc -Wall hello.c -o hello |
| -w | 不生成任何警告信息 | gcc -w hello.c -o hello |

3.3 静态链接 vs 动态链接

静态链接:

优点:运行时不依赖系统库,可移植性强,运行速度快

缺点:可执行文件体积大,库更新后需重新编译

动态链接:

优点:可执行文件体积小,库更新无需重新编译

缺点:运行时依赖系统动态库,移植性稍差

库文件后缀:

Linux:静态库 .a,动态库 .so

Windows:静态库 .lib,动态库 .dll

查看依赖库:ldd hello(查看可执行文件依赖的动态库)

3.4 静态库安装(CentOS)

bash 复制代码
sudo yum install glibc-static libstdc++-static -y

4. 自动化构建 make/Makefile

4.1 核心概念

make:命令工具,解释 Makefile 中的规则,实现自动化编译。

Makefile:文本文件,定义文件依赖关系和编译命令。

优势:一次编写,多次使用,自动判断哪些文件需要重新编译,提升开发效率。

4.2 基础 Makefile 示例

bash 复制代码
# 目标文件: 依赖文件
myproc:myproc.c
    gcc -o myproc myproc.c  # 注意:命令前必须是 Tab 键,不能是空格

.PHONY:clean  # 声明 clean 为伪目标
clean:
    rm -f myproc

依赖关系:myproc 依赖 myproc.c,若 myproc.c 比 myproc 新,则重新编译。

伪目标 .PHONY:避免和同名文件冲突,确保 make clean 总是执行。

4.3 多阶段依赖推导

bash 复制代码
myproc:myproc.o
    gcc myproc.o -o myproc
myproc.o:myproc.s
    gcc -c myproc.s -o myproc.o
myproc.s:myproc.i
    gcc -S myproc.i -o myproc.s
myproc.i:myproc.c
    gcc -E myproc.c -o myproc.i

.PHONY:clean
clean:
    rm -f *.i *.s *.o myproc

make 会递归查找依赖,从 .c 开始,依次生成 .i → .s → .o → 可执行文件。

4.4 高级 Makefile 语法(变量与通配)

bash 复制代码
# 定义变量
BIN=proc.exe
CC=gcc
SRC=$(wildcard *.c)  # 获取当前目录所有 .c 文件
OBJ=$(SRC:.c=.o)     # 将 .c 替换为 .o,生成目标文件列表
LFLAGS=-o
FLAGS=-c
RM=rm -f

# 生成可执行文件
$(BIN):$(OBJ)
    $(CC) $(LFLAGS) $@ $^  # $@=目标文件名, $^=所有依赖文件
    @echo "linking ... $^ to $@"

# 模式规则:生成所有 .o 文件
%.o:%.c
    $(CC) $(FLAGS) $< -o $@  # $<=第一个依赖文件
    @echo "compling ... $< to $@"

.PHONY:clean test
clean:
    $(RM) $(OBJ) $(BIN)
test:
    @echo $(SRC)
    @echo $(OBJ)

4.5 make 工作原理

  1. 查找当前目录下的 Makefile 或 makefile。

  2. 以第一个目标文件为最终目标。

  3. 递归检查依赖文件的修改时间,判断是否需要重新编译。

  4. 若依赖文件不存在,先生成依赖文件。

  5. 执行编译命令生成最终目标。

5. Linux 系统程序:进度条与行缓冲区

5.1 行缓冲区现象

带 \n 的 printf:printf("hello bite!\n"); → 立即输出(\n 刷新缓冲区)。

不带 \n 的 printf:printf("hello bite!"); → 缓冲区满或程序结束才输出。

手动刷新缓冲区:fflush(stdout); → 强制输出缓冲区内容。

5.2 倒计时程序示例

cpp 复制代码
#include <stdio.h>
#include <unistd.h>

int main()
{
    int i = 10;
    while(i >= 0)
    {
        printf("%-2d\r", i);  // \r 回车不换行,覆盖当前行
        fflush(stdout);       // 强制刷新
        i--;
        sleep(1);
    }
    printf("\n");
    return 0;
}

5.3 进度条完整代码

process.h

cpp 复制代码
#pragma once
#include <stdio.h>

void process_v1();
void FlushProcess(double total, double current);

process.c

cpp 复制代码
#include "process.h"
#include <string.h>
#include <unistd.h>

#define NUM 101
#define STYLE '='

// 版本1:简单进度条
void process_v1()
{
    char buffer[NUM];
    memset(buffer, 0, sizeof(buffer));
    const char *lable="|/-\\";
    int len = strlen(lable);

    int cnt = 0;
    while(cnt <= 100)
    {
        printf("[%-100s][%d%%][%c]\r", buffer, cnt, lable[cnt%len]);
        fflush(stdout);
        buffer[cnt] = STYLE;
        cnt++;
        usleep(50000);
    }
    printf("\n");
}

// 版本2:可传参进度条
void FlushProcess(double total, double current)
{
    char buffer[NUM];
    memset(buffer, 0, sizeof(buffer));
    const char *lable="|/-\\";
    int len = strlen(lable);
    static int cnt = 0;

    int num = (int)(current*100/total);
    int i = 0;
    for(; i < num; i++)
    {
        buffer[i] = STYLE;
    }
    double rate = current/total;
    cnt %= len;
    printf("[%-100s][%.1f%%][%c]\r", buffer, rate*100, lable[cnt]);
    cnt++;
    fflush(stdout);
}

main.c

cpp 复制代码
#include "process.h"
#include <stdio.h>
#include <unistd.h>

double total = 1024.0;
double speed = 1.0;

void DownLoad()
{
    double current = 0;
    while(current <= total)
    {
        FlushProcess(total, current);
        usleep(30000);  // 模拟下载耗时
        current += speed;
    }
    printf("\ndownload %.2lfMB Done\n", current);
}

int main()
{
    DownLoad();
    return 0;
}

Makefile

bash 复制代码
SRC=$(wildcard *.c)
OBJ=$(SRC:.c=.o)
BIN=processbar

$(BIN):$(OBJ)
    gcc -o $@ $^
%.o:%.c
    gcc -c $<

.PHONY:clean
clean:
    rm -f $(OBJ) $(BIN)

6. 版本控制器 Git

6.1 核心概念

版本控制:记录文件每次修改,可回退到历史版本,支持多人协作。

Git:分布式版本控制系统,适合管理大型项目和非线性开发。

6.2 安装 Git

bash 复制代码
# CentOS
yum install git
# Ubuntu
sudo apt install git
# 验证版本
git --version

6.3 GitHub 项目创建

  1. 注册 GitHub 账号并登录。

  2. 点击 New repository 创建新项目。

  3. 填写项目名称、描述,选择公开/私有,点击 Create repository。

  4. 复制项目链接(HTTPS/SSH)。

6.4 核心操作(三板斧)

  1. 克隆项目到本地:git clone 项目URL

  2. 添加文件到暂存区:
    git add 文件名 # 添加单个文件
    git add . # 添加当前目录所有文件

  3. 提交到本地仓库:git commit -m "提交日志(描述本次修改)"

  4. 推送到远程仓库:git push

6.5 其他常用命令

git status:查看文件状态

git log:查看提交历史

git pull:拉取远程仓库最新代码

.gitignore:指定不需要 Git 管理的文件(如日志、临时文件)

配置用户信息:

bash 复制代码
git config --global user.name "Your Name"
git config --global user.email "you@example.com"

7. 调试器 gdb/cgdb

7.1 编译调试版程序

必须添加 -g 选项生成调试信息:

bash 复制代码
gcc -g mycmd.c -o mycmd  # 生成带调试信息的可执行文件

7.2 启动与退出 gdb

bash 复制代码
gdb [可执行文件名]  # 启动 gdb
Ctrl + d 或 quit    # 退出 gdb

7.3 核心调试命令

|--------------|-------------|------------------------|
| 命令 | 作用 | 示例 |
| list/l | 显示源码 | l main / l 10 |
| run/r | 运行程序 | r |
| next/n | 单步执行(不进入函数) | n |
| step/s | 单步执行(进入函数) | s |
| break/b | 设置断点 | b 24 / b main |
| info break/b | 查看断点信息 | info b |
| print/p | 打印变量/表达式 | p result / p start+end |
| set var | 修改变量值 | set var flag=1 |
| continue/c | 继续执行 | c |
| finish | 执行到函数返回 | finish |
| backtrace/bt | 查看函数调用栈 | bt |
| until | 执行到指定行 | until 14 |
| watch | 监视变量变化 | watch result |

7.4 调试技巧

watch 变量:当变量值变化时,gdb 会暂停程序并提示,适合排查变量被意外修改的问题。

set var 修改变量:在调试时临时改变变量值,验证问题原因。

cgdb:带界面的 gdb,更直观查看代码和调试信息,安装命令:

bash 复制代码
# Ubuntu
sudo apt-get install -y cgdb
# CentOS
sudo yum install -y cgdb

7.5 调试示例(排查结果为 0 的问题)

cpp 复制代码
// mycmd.c
#include <stdio.h>

int flag = 0;  // 错误:flag 初始为 0
int Sum(int s, int e)
{
    int result = 0;
    for(int i = s; i <= e; i++)
    {
        result += i;
    }
    return result*flag;  // 导致结果为 0
}

int main()
{
    int start = 1;
    int end = 100;
    int n = Sum(start, end);
    printf("result: %d\n", n);
    return 0;
}

调试步骤:

  1. gdb mycmd 启动调试。

  2. b 24 在 int n = Sum(start, end); 处设置断点。

  3. r 运行程序,触发断点。

  4. s 进入 Sum 函数,n 单步执行,p result 查看累加结果。

  5. p flag 发现 flag=0,set var flag=1 修改后,c 继续执行,结果恢复正常。

8. 补充知识点

条件编译应用场景:根据不同平台/环境编译不同代码(如 #ifdef linux)。

为什么要把语言变成汇编:汇编是机器码的文本表示,是 CPU 能直接执行的指令集,编译器将高级语言翻译成汇编,再转为机器码。

编译器自举:用编译器本身编译自己的源码,证明编译器能正确处理自身代码,是编译器成熟的标志。

9. 回车与换行

回车 \r:光标回到行首,不换行(老式打字机的"回车"动作)。

换行 \n:光标移到下一行(老式打字机的"换行"动作)。

Windows:\r\n 表示换行;Linux/macOS:\n 表示换行。


掌握这些Linux开发工具与核心知识点,不仅能搭建起高效的开发环境,更能帮你理解程序从编写到运行的完整逻辑,为后续系统编程与项目开发打下扎实根基。

相关推荐
不会C语言的男孩26 分钟前
C++ Primer Plus 第8章:函数探幽
开发语言·c++
都在酒里27 分钟前
Linux字符设备驱动开发(十):综合实例——I2C传感器 + LED智能控制与进阶指南
linux·运维·服务器·驱动开发·交互
William_wL_30 分钟前
【C++】模板进阶
c++
MC皮蛋侠客8 小时前
Google Test 单元测试指南
c++·单元测试·google test
2301_809051148 小时前
Linux 网络编程 学习笔记
linux·网络·学习
wanhengidc8 小时前
服务器租用有何优点
运维·服务器·安全·web安全
ZGi.ai8 小时前
人工审查节点:让自动化工作流多一步人工把关
运维·人工智能·自动化·人机协同·智能体工作流·人工审查
坤昱8 小时前
cfs调度类深入解刨——最新内核细节分析2
linux·服务器·cfs·cfs调度·eevdf调度·eevdf·kernel 7.1
艾莉丝努力练剑8 小时前
【Linux:文件】Ext系列文件系统进阶
linux·运维·服务器·c++·文件系统·文件io·ext
海市公约8 小时前
Linux核心基础命令与权限管理实战指南
linux·运维·服务器·vim·权限管理·系统监控·命令行