【Linux】从版本控制到代码调试:Git 入门与 GDB 调试器学习指南 - 详解

 /-----------Linux入门篇------------/

《 Linux 历史溯源与指令入门 》

《 Linux 指令进阶 》

《 Linux 权限管理 》

 /-----------Linux工具篇------------/

《 yum 与 vim 》

《 sudo白名单配置+GCC/G++编译器》

《 自动化构建:make+Makefile 》

《 倒计时+进度条 》

 个人主页< 脏脏a-CSDN博客 >

文章专栏:< Linux >

其他专栏:< C++ > 、< 数据结构 > 、< 优选算法 >

目录

一、Git 版本控制器

1.1 什么是 Git?

1.2 Git 核心概念 

1.3 GitHub 与 Gitee

二、Git 基础操作

1. Git安装

2. 远程仓库与本地仓库联动(以 Gitee 为例)

3. 身份配置

4. 代码提交的 “三板斧”

5. git其他选项

【git log命令】

【git status命令】

【.gitignore文件】

三、gdb前置知识

【readelf指令】

四、gdb相关调试指令

4.1 进入gdb和退出gdb指令

4.2 查看源代码指令

4.3 run/r指令

4.4 断点相关调试指令

4.4.1 break/b指令

4.4.2 d 指令

4.4.3 disable/enable breakpoints 指令

4.4.4 info breakpoints 指令

4.5 next/n 指令

4.6 step/s 指令

4.7 finish 指令

4.8 print/p 指令

4.9 set var指令 

4.10 continue(或 c)指令

4.11 display指令

4.12 undisplay指令

4.13 until指令

4.14 breaktrace (或 bt)指令

4.15 info (i) locals指令


一、Git 版本控制器

1.1 什么是 Git?

Git 是一款分布式版本控制系统,可以理解为代码的 “时光机”—— 它能记录代码的每一次修改,让你随时回退到历史版本,还能支持多人协作开发。和传统的 SVN(svm只能通过服务端查看历史记录) 相比,Git 是 “客户端 - 服务端一体” 的设计,既可以本地单机管理代码,也能通过远程仓库(如 GitHub、Gitee)实现团队协作。

【问题】:什么是分布式?

【分布式的核心概念】:

将整体的任务、数据或功能分散部署到多个独立的节点(如计算机、服务器等)上,使各节点能并行处理任务;每个节点可拥有完整或部分资源,支持独立工作,节点之间通过网络进行协同与同步无需依赖单一中心节点来驱动整个系统运行。

【分布式的典型特点】:

  • 并行执行:多节点可同时处理任务,提升整体效率;
  • 去中心化:无单一故障点,某一节点故障不影响系统整体运行;
  • 弹性扩展:可通过新增节点快速拓展系统能力。

简单类比(以 Git 为例)

Git 作为分布式版本控制系统,核心特点是每个用户的本地都拥有完整的版本库

  • 既可以连接远端服务端(如 GitHub、GitLab)进行代码推送、拉取,实现团队协作;
  • 也能在本地完全独立地完成提交、分支管理、版本回滚等所有版本控制操作;
  • 多个用户可在各自本地 “并行” 开展开发,之后再通过同步操作整合修改,完美体现了分布式 “多节点独立工作 + 协同同步” 的特性。

【问题】:什么是集中式?

【集中式的核心概念】:

将系统的任务、数据或功能集中部署在单一的中心节点(如中央服务器)上,所有操作(如数据存储、任务处理)都依赖该中心节点完成;用户或从属节点需连接到中心节点才能开展工作,本地通常仅保留临时的工作副本,不具备独立处理核心任务的能力。

【集中式的典型特点】:

  • 单一中心:系统依赖唯一的中心节点驱动,逻辑集中、管理便捷;
  • 强依赖性:用户或从属节点必须连接中心节点才能完成核心操作,若中心节点故障,整个系统将无法正常运行;
  • 扩展受限:系统能力受中心节点的硬件、性能上限约束,难以快速弹性扩展。

简单类比(以 SVN 为例)

SVN 作为集中式版本控制系统,核心特点是仅中央服务器保存完整的版本库

  • 用户必须连接到中央服务器,才能执行提交、更新、分支管理等版本控制操作;
  • 本地仅保留当前工作副本,无完整版本库,无法在断网或中心服务器不可用时独立进行版本管理;
  • 多用户需串行或依赖中心节点的调度开展协作,体现了集中式 “单一中心驱动 + 节点强依赖” 的特性。

1.2 Git 核心概念 

  • 仓库(Repository):分为本地仓库(你电脑里的代码库)和远程仓库(如 GitHub 上的共享代码库);
  • 版本控制:像管理实验报告版本一样,Git 能记录代码的每一次修改(比如 “实验报告 v1”“实验报告究极版”),随时回溯;
  • 分布式协作:每个开发者都有完整的代码库,既可以本地开发,又能通过远程仓库同步协作(比如张三、李四可以分别改代码,再合并到同一个远程仓库)。

1.3 GitHub 与 Gitee

Git 是版本控制工具,而 GitHub、Gitee 是基于 Git 的商业化代码托管平台—— 你可以把它们理解为 “代码的社交网络”,既能存代码,又能和全球开发者协作、学习优质项目。类似地,基于 Linux 内核也衍生出了 CentOS、Ubuntu 等商业化操作系统,原理是相通的~

二、Git 基础操作

1. Git安装

  • 安装 Git:在 Linux 上可通过包管理器快速安装,比如 CentOS 用 yum install git,Ubuntu 用 apt install git

2. 远程仓库与本地仓库联动(以 Gitee 为例)

【新建仓库】:

进入gitee后点右上角的+,点击后就能看到新建仓库这一选项,点击后就到了下述界面

在这里,选择你需要的代码语言来保存版本。README文件用于仓库的说明,创建时会自动生成中英文版本。在分支选项中,这里推荐选择单分支是为了简化项目管理,特别是在项目初期或只是用于代码托管时,不需要复杂的分支结构。单分支模式更适合简单的版本控制,避免了多分支带来的合并冲突和管理开销。

【将远端仓库拉到本地】:

点击新建仓库右上角的克隆和下载:

克隆成功!!!

#将远端仓库克隆到本地的命令
git clone 仓库地址

Test仓库此时就在我们的当前目录下,大家使用时尽量放在普通用户下,我这只是示范,所以就没有切换用户了。

3. 身份配置

当我们将远端仓库拉取到本地,为了确保提交的代码身份被Gitee/GitHub正确识别,需要在本地进行相关配置

git config --global user.email "你的邮箱"
git config --global user.name "你的名字"

Git 配置 user.name 和 user.email,是为了给代码提交打上专属身份标签,方便后续查看提交历史、追溯修改责任

4. 代码提交的 “三板斧”

【第一招】:git add—— 把文件加入 “待提交队列”把需要版本管理的文件告知 Git,命令格式:

git add 文件名  # 单个文件
git add .      # 当前目录所有文件

【第二招】:git commit—— 提交修改到本地仓库把 “待提交队列” 的修改保存到本地仓库,同时写提交日志(描述这次改了啥,这块不能随便填写):

git commit -m "提交日志:比如‘新增登录功能’"

【第三招】:git push—— 同步修改到远程仓库把本地仓库的修改推送到 GitHub 等远程仓库,命令:

git push

首次推送可能需要输入 GitHub 账号密码(或配置 SSH 密钥免密,新手不建议配置免密),在圈的地方输入你的账号和密码即可,账号会回显,密码不会回显。

配置免密码提交】:https://blog.csdn.net/camillezj/article/details/55103149

Git 工作流程围绕工作区、暂存区、本地仓库三个区域展开,操作需在仓库目录内进行:

  • 工作区:项目目录中可见的文件(如仓库下的代码文件、文档),修改后仅存于此,未被 Git 跟踪为新版本;
  • 执行 git add 命令,将工作区的修改加入暂存区(可理解为 “待提交清单”);
  • 执行 git commit 命令,将暂存区内容提交到本地仓库.git 目录是本地仓库的核心,提交后形成版本记录并保存);
  • 最后通过 git push 可将本地仓库的变更同步到远程仓库。

5. git其他选项

【git log命令】

  作用:查看历史提交记录     

历史提交记录的信息,包含你的信息、日志、提交时间等等

【git status命令】

  作用:查看当前目录状态

显示当前没有东西可以提交的内容,工作区已清理

【.gitignore文件】

作用是指定 Git 版本控制中需要忽略的文件或目录,让这些文件 / 目录不被纳入版本管理,简单来说就是去掉杂项文件,只保留需要使用的文件 

在 .gitignore 文件中,通过通配符(如 *.exe*.out)可以指定 “匹配该后缀的所有文件都被忽略,不推送到远程仓库”。如果要添加自定义的杂项文件,需要用 *后缀 的格式(比如要忽略所有 .tmp 临时文件,就写 *.tmp);如果只写后缀(如 tmp),Git 会把它当作 “名为 tmp 的文件” 来处理,无法实现批量忽略同后缀文件的效果。

三、gdb前置知识

  1. 程序的发布方式有两种,debug模式和release模式
  2. Linux gcc/g++出来的二进制程序,默认是release模式
  3. 要使用gdb调试,必须在源代码生成二进制程序的时候, 加上 -g 选项
  4. gdb是系统默认安装的,所以可以直接使用

【如何确认gcc/g++默认编译出来的结果是release版本还是debug版本

没带 "-g" 选项编译出来的结果:

带 "-g" 选项编译出来的结果:

可以看到两次编译后的文件大小不同,那么带 "-g" 选项编译后的可执行程序多了什么呢?

多出来的就是调试信息,这样也可以间接证明gcc默认编译出来的结果是release版本

【readelf指令】

  • 语法readelf [选项] [ELF文件]
  • 功能:用于显示 ELF 格式文件(如可执行文件、库文件)的详细信息,可查看文件头、节信息、符号表等内容
  • 常用选项
    • -h 显示 ELF 文件头信息
    • -l 显示程序头表信息
    • -S 显示节头表信息
    • -s 显示符号表信息
    • -d 显示动态段信息
    • -w 显示调试信息
    • --help 查看所有选项说明

在 Linux 系统中,虽然可执行文件是二进制格式,但其内部结构采用了 ELF (Executable and Linkable Format/可执行与可链接格式)格式,可以通过 readELF 工具查看。

四、gdb相关调试指令

4.1 进入gdb和退出gdb指令

  • 进入gdb指令:gdb可执行文件名称
  • 退出gdb指令:quit(q) 或者 ctrl + d

4.2 查看源代码指令

  • list / l 行号:显示指定行号附近的源代码,从上次位置继续向下列,每次列 10 行。
  • list / l 函数名:列出指定函数的源代码。

注意:gbd会默认记录最近一次的指令,可以回车执行最近一次指令。

【用行号查看演示】:

l 5就是显式第五行上面的5行和下面的5行也就是每次显式10行

输入一次指令后,按回车就会自动向下再输出10行,输出完就显示有多少行

这个时候想要再从头看可以输入l 0,一般从头开始的话,只输输入 l的话会自动输出10行

【查看函数演示】:

显式sum函数附近的10行代码,反正也是每次输出10行,按空格就继续输出10行,输出完就显示一共多少行

4.3 run/r指令

作用:运行程序

核心特点:

  • 执行后会直接启动程序并运行至结束(类似 IDE 中的 “运行” 功能,如 VS 的 F5 键);
  • 若程序无断点,会一次性跑完所有逻辑,因此通常需要结合断点指令(如 b 行号 或 b 函数名 来实现调试暂停,进而逐步分析程序流程

没打断点就一次性跑完了,要像vs那样调试的话得必须打断点,确实有点烦人,不好用!!!

4.4 断点相关调试指令

4.4.1 break/b指令

这是 GDB 调试中用于设置断点的 break(简写 b)指令,有四种常见用法:

  • break 行号(或 b 行号:在代码的指定行设置断点,程序运行到该行时会暂停。例如 break 20(或 b 20),会在第 20 行设置断点。
  • break 函数名(或 b 函数名:在指定函数的开头设置断点,程序进入该函数时会暂停。例如 break main(或 b main),会在 main 函数的开头设置断点。
  • break 文件名:行号(或 b 文件名:行号):在指定文件的指定行设置断点。例如 break test.c:20(或 b test.c:20),会在 test.c 文件的第 20 行设置断点。
  • break 文件名:函数名(或 b 文件名:函数名):在指定文件的指定函数开头设置断点。例如 break test.c:sum(或 b test.c:sum),会在 test.c 文件的 sum 函数开头设置断点。
  • 在 GDB 中如果不指定文件名,break 指令会默认在当前调试的主文件(通常是包含 main 函数的文件)中设置断点

分别在20行和main函数处打断点:

如果在第 20 行或main函数入口设置断点时,该行是空行,GDB 会自动定位到下方最近的有效语句处暂停执行,main函数的情况同理。

指定文件打断点:

4.4.2 d 指令

这是 GDB 调试中用于删除断点的 delete breakpoints 指令(简写d),有两种常见用法:

  • :删除所有断点。
  • d  n:删除序号为n的断点。

第一个指令是删除序号为3的断点,第二个指令是删除所有断点,Num下的编号就对应了每个断点的序号

4.4.3 disable/enable breakpoints 指令
功能完整指令简写指令(常用)示例
禁用指定断点disable breakpoints 序号disable 序号 /dis 序号(gdb) dis 1(禁用序号 1 的断点)
禁用所有断点disable breakpointsdisable / dis(gdb) dis(禁用所有)
启用指定断点enable breakpoints 序号enable 序号 /en 序号(gdb) en 1(启用序号 1 的断点)
启用所有断点enable breakpointsenable / en(gdb) en(启用所有)

禁用断点后就不会生效了

4.4.4 info breakpoints 指令

这是 GDB 调试中用于查看断点信息的 info breakpoints(简写 info(或i) breakpoints)指令:

  • info (或 i) breakpoints:参看当前设置了哪些断点,包括断点行号、状态、数量等详细信息。

列名核心含义(图标版)
Num 断点序号(操作标识),序号呈线性增长,前面用过的序号纵使删除当前序号指定的断点,依旧不会用到
Type 断点类型(普通 / 观察等)
Disp⚙️ 处置方式(保留 / 自动删)
Enb 启用状态(y = 启用 /n = 禁用)
Address 内存地址(机器码位置)
What 具体位置(文件 + 行号 / 函数)

4.5 next/n 指令

这是 GDB 调试中用于单条执行代码的 next(简写 n)指令:

  • n 或 next:单条执行(逐行执行,跳过函数内部调用,将函数调用视为 “一行” 执行)。

当前运行到第一个断点(main 函数处),输入n执行两次后却进入了sum函数,按理next不该进入函数才对?

这是因为 sum函数内部设置了断点next执行sum函数调用时,会在函数的断点处暂停(此为断点触发的暂停,并非next主动进入函数内部单步)。

4.6 step/s 指令

这是 GDB 调试中用于逐语句执行代码的 step(简写 s)指令:

  • s 或 step:进入函数调用(若当前行是函数调用,会进入函数体内部逐句执行)。

4.7 finish 指令

这是 GDB 调试中用于执行到函数返回的 finish 指令:

  • finish:执行到当前函数返回,然后停下来等待命令。

main函数中用finish,没有实际调试作用,不能这么用~

在sum函数中用会直接跳到函数调用处

4.8 print/p 指令

打印表达式值支持修改变量(修改后并打印)调用函数

p 变量 是其简写形式,用于快速打印单个变量值,二者功能本质一致,p 是更简洁的常用操作方式。

GDB 中$1$2这类序号,是同一个 GDB 调试会话内 **print/p操作的临时计数标记,按执行顺序线性递增;只有完全退出 GDB 并重新启动调试会话时,序号才会从$1重新开始;仅重新运行程序(不退出 GDB)时,序号会持续累加,不会重置。**

临时修改变量值:

直接调用函数,不论函数是否被执行,都会打印该函数的结果

4.9 set var指令 

set var 变量名=新值(例如 set var a=20,纯修改变量值,执行后无自动输出)

4.10 continue(或 c)指令

c(例如输入 c,程序从当前断点位置继续连续执行,直到遇到下一个断点或程序结束)

在运行后,输入c,无断点就直接运行结束了

4.11 display指令

display 变量名(例如 display a,跟踪查看变量 a,程序每次暂停时都会自动显示其当前值)

相当于vs监视窗口

4.12 undisplay指令

undisplay 序号(例如 undisplay 1,取消序号为 1 的变量跟踪;若要取消所有,可多次执行或用 delete display 系列指令)

4.13 until指令

until 行号(例如 until 50,程序从当前位置连续执行到第 50 行后暂停,适合快速跳过循环等场景),只能往后跳

4.14 breaktrace (或 bt)指令

bt(例如输入 bt,查看当前程序的函数调用栈,包括各级函数的调用关系、参数和返回地址等)

这张图完美展示了函数调用栈(栈帧)随程序执行的动态变化过程

  • 阶段 1(add 函数中):调用栈有 3 层(main→sum→add),因为 add 还在执行,没返回 sum
  • 阶段 2(sum 函数中)add 执行完返回,调用栈变为 2 层(main→sum),因为 sum 还在执行,没返回 main
  • 阶段 3(main 函数中)sum 执行完返回,调用栈只剩 1 层(main),因为所有子函数都已返回。

每一次函数调用会 “压栈”(新增栈帧),函数返回会 “出栈”(移除栈帧),这就是调用栈的动态变化逻辑~

4.15 info (i) locals指令

info locals(或 i locals,例如输入 info locals,查看当前栈帧中所有局部变量的名称和值)

 类似于调试器的 **“自动监视窗口”,它会实时显示当前栈帧(即当前执行的函数)中所有局部变量的名称和值 **,只聚焦于程序运行到 “当前代码块” 时的变量状态,非常方便查看函数内局部变量的实时变化。

 不是常显示的,运行到下一句就不会显示,需要你再次手动操作

posted @ 2025-12-15 18:29  clnchanpin  阅读(21)  评论(0)    收藏  举报