🦄 个人主页: 小米里的大麦-CSDN博客
🎏 所属专栏: Linux_小米里的大麦的博客-CSDN博客
🎁 GitHub主页: 小米里的大麦的 GitHub
⚙️ 操作环境: Visual Studio 2022

在这里插入图片描述

文章目录

  • Linux 开发工具(下)
    • Linux 项目自动化构建工具 —— `make` / `Makefile`
      • 1. 什么是 `make` 和 `Makefile`?
        • 为什么需要 `make`?
      • 2. `make` 的基本工作原理
        • 基本逻辑
        • 规则格式
      • 3. `make` 的工作流程
      • 4. 伪目标 `.PHONY` 与放置位置的讨论
        • 关于 `.PHONY: clean` 的放置位置
      • 5. 常见符号与自动变量的使用
        • `@` —— 命令隐藏符
        • 自动变量 `$^` 与 `$@`
      • 6. 进阶技巧(了解)
        • 变量定义与使用
        • 模式规则
      • 传道解惑
        • ==文件 = 文件内容 + 文件属性==
    • Linux 下第一个小程序——进度条
      • 1. 换行 vs 回车:键盘上的时光机
      • 2. 行缓冲区:快递员的打包习惯
        • 实验验证与现象分析
        • Shell 提示符的输出行为
        • 为什么覆盖?
      • 3. 倒计时实现
      • 4. 由于进度条相关文章内容过长,详见下一篇文章!
    • `git ` 的使用
      • 一、Git 的本质:时间管理大师的 "时光机"
      • 二、Linus Torvalds:被逼出来的创新
      • 三、Git 的革命性突破
      • 四、Git 的现代发展
      • 五、Git 改变了软件开发的方式
      • 六、`git` 的使用([Git 教程](https://liaoxuefeng.com/books/git/introduction/index.html))
        • Git 的核心概念
        • Git 的工作流程
        • 1. 安装 Git
        • 2. 配置 Git
        • 3. 创建/初始化仓库
        • 4. 克隆远程仓库
        • 5. 查看仓库状态
        • 6. 添加文件到暂存区
        • 7. 提交更改
        • 8. 查看提交历史
        • 9. 查看远程仓库
        • 10. 添加/连接远程仓库
        • 11. 推送本地分支到远程仓库
        • 12. 拉取远程仓库的更新
        • 13. 撤销工作区的修改
        • 14. 撤销暂存区的修改
        • 15. 撤销提交
        • 16. 创建标签
        • 17. 查看标签
        • 18. 推送标签到远程仓库
        • 19. 查看差异
        • 20. 查看远程分支
        • 21. 删除远程分支
      • 1. Git 只会记录已添加到暂存区(staging area)的修改
      • 2. Git 忽略某些文件:`.gitignore`
      • 3. Git 配置文件的管理
    • Linux 调试器 —— `gdb` 的使用
      • 一、背景知识
      • 二、Windows IDE 对应功能
      • 三、`gdb` 常用命令
        • 1. 查看源代码
        • 2. 运行程序
        • 3. 单步执行
        • 4. 设置断点
        • 5. 删除断点
        • 6. 继续执行
        • 7. 查看和修改变量
        • 8. 跟踪变量
        • 9. 查看函数调用栈
        • 10. 查看局部变量
        • 11. 跳转到指定行
        • 12. 退出函数
    • 共勉

Linux 开发工具(下)

Linux 项目自动化构建工具 —— make / Makefile

1. 什么是 makeMakefile

在大型软件项目中,源代码通常散布在多个文件和目录中。为了高效地管理这些文件之间的依赖关系并实现自动化编译,Linux 提供了非常强大的工具 —— makemake 是一个自动化构建工具,它通过读取 Makefile 文件中的规则来决定如何编译和链接程序。Makefile 是一个文本文件,其中定义了项目的依赖关系和构建规则。

为什么需要 make
  • 自动化构建:手动编译多个源文件并管理它们之间的依赖关系非常繁琐,make 可以自动化这一过程。
  • 增量编译make 只会重新编译那些被修改的文件及其依赖项,从而节省编译时间。
  • 跨平台兼容Makefile 可以在不同的平台上使用,只需稍作修改。

2. make 的基本工作原理

make 的核心功能是根据文件的修改时间自动判断哪些文件需要重新编译。具体来说,它会根据文件间的依赖关系,确保只有修改过的部分被重新编译,避免无谓的重复编译。

基本逻辑
  • 目标文件与依赖关系
    make 通过比较目标文件与依赖文件的修改时间来决定是否需要重新编译。如果目标文件不存在,或者依赖文件较新,make 就会重新执行对应的编译命令。

  • 举例说明:假设我们有如下的依赖关系:

    • hello 依赖于 hello.o
    • hello.o 依赖于 hello.s
    • hello.s 依赖于 hello.i
    • hello.i 依赖于 hello.c

    hello.c 被修改后,make 会检查文件的修改时间,并按照依赖关系从 hello.c 开始,逐层更新直到最终目标 hello

规则格式
目标文件:依赖文件命令

例如:

hello: hello.ogcc hello.o -o hello

如果 hello.o 发生了变化,make 会执行 gcc hello.o -o hello 来生成目标文件 hello


3. make 的工作流程

Makefile 中的依赖关系往往构成一个“栈式”结构,即从最终目标开始,逐层向下寻找依赖,直到最初的源文件。

  1. 查找文件make 会在当前目录下查找名为 Makefilemakefile 的文件。
  2. 确定目标文件:找到文件后,它会查找文件中的第一个目标文件(如示例中的 hello)并将其作为最终的目标文件。
  3. 检查依赖关系:如果目标文件不存在,或者其依赖文件的修改时间比目标文件更新,则执行相应的命令生成目标文件。
  4. 递归处理依赖:如果目标文件的依赖文件(如 hello.o)不存在,则会进一步在当前文件中查找该依赖文件的规则,并依此进行生成。
  5. 完成编译:按照依赖关系一层一层地处理,直到最终生成第一个目标文件。
  6. 错误处理:如果在依赖关系的查找过程中出现错误(如最后被依赖的文件找不到),make 会直接退出并报错;对于命令执行的错误或编译不成功的情况,make 不会进行处理。

例如下面这个 Makefile 片段展示的完整依赖链:

# 目标文件 hello 依赖于 hello.o
hello: hello.o# gcc 用于编译链接生成可执行文件 (Executable)gcc hello.o -o hello# 目标文件 hello.o 依赖于 hello.s
hello.o: hello.s# -c 表示只编译生成目标文件,不进行链接gcc -c hello.s -o hello.o# 目标文件 hello.s 依赖于 hello.i
hello.s: hello.i# -S 表示生成汇编代码 (Assembly code)gcc -S hello.i -o hello.s# 目标文件 hello.i 依赖于 hello.c
hello.i: hello.c# -E 表示只预处理生成中间文件gcc -E hello.c -o hello.i
  • 依赖判断:当修改了 hello.c 后,hello.i 的时间戳就会落后于 hello.c,从而触发后续所有目标的重新编译。
  • 自动化依赖推导make 会根据目标与依赖之间的时间比较,自动“回溯”整个依赖链,直到确定哪些文件需要重新生成。

4. 伪目标 .PHONY 与放置位置的讨论

定义:伪目标(如 clean)一般用于清理工程中的目标文件,它们没有被第一个目标文件直接或间接关联。可以通过命令(如 make clean)显式执行其后的命令。伪目标的特性是 总是被执行,不会因为文件的存在而被忽略。

例如在 Makefile 中,我们经常会定义一些不对应实际文件的辅助目标,clean 目标通常用于删除编译产生的中间文件。为避免与同名文件产生冲突,我们使用 .PHONY 声明该目标总是需要执行:

.PHONY: clean
clean:@rm -f hello.i hello.s hello.o hello
# 使用 `.PHONY` 声明后,`make clean` 会始终执行清理命令,即使当前目录下存在名为 `clean` 的文件。
关于 .PHONY: clean 的放置位置

放在开头或结尾: 无论将 .PHONY: clean 放在 Makefile 的开头还是结尾,其功能是相同的,都会告诉 make “clean” 不是一个真实存在的文件。然而,从 代码可读性维护性 的角度来看,通常建议将辅助目标(如 clean)放在文件的末尾。这样做可以:

  • 保持主构建规则的集中:主要的目标和依赖关系放在上面,便于开发者快速了解构建流程。
  • 逻辑分明:清理等辅助目标作为附加功能放在末尾,形成明显的区分。

5. 常见符号与自动变量的使用

在编写 Makefile 时,合理使用一些特殊符号和自动变量能使文件更加简洁与灵活。

@ —— 命令隐藏符

在规则的命令前加上 @ 符号,可以在执行时不将该命令打印到终端。例如:

clean:@rm -f hello.i hello.s hello.o hello
  • 作用:使得执行 make clean 时不会在终端中显示 rm -f ... 这一行命令,从而使命令输出更干净,只显示必要的信息。
自动变量 $^$@
  • $@:代表规则中的目标文件(Target)。
  • $^:代表规则中所有的依赖文件(Prerequisites),通常用来减少重复输入依赖文件列表。

示例: 假设有多个依赖文件构成目标文件,我们可以这样写:

hello: hello.o util.o# $^ 表示所有依赖,即 "hello.o util.o"(:右边部分,若有多个,用空格隔开)# $@ 表示目标,即 "hello"(:左边部分)gcc $^ -o $@

好处:使用自动变量能让规则更灵活且易于维护,尤其当依赖项较多时,避免重复书写目标和依赖文件名称。

6. 进阶技巧(了解)

变量定义与使用

Makefile 中,我们可以定义变量来简化重复的代码。例如:

CC = gcc
CFLAGS = -Wall -O2myprogram: main.o utils.o$(CC) $^ -o $@main.o: main.c utils.h$(CC) $(CFLAGS) -c main.c -o main.outils.o: utils.c utils.h$(CC) $(CFLAGS) -c utils.c -o utils.o
  • CC:定义编译器为 gcc
  • CFLAGS:定义编译选项为 -Wall -O2
模式规则

当项目中有多个相似的文件需要编译时,可以使用模式规则来简化 Makefile。例如:

%.o: %.c$(CC) $(CFLAGS) -c $< -o $@
  • %.o:表示所有以 .o 结尾的目标文件。
  • %.c:表示所有以 .c 结尾的源文件。
  • $<:表示第一个依赖文件。

传道解惑

文件 = 文件内容 + 文件属性

在计算机系统中,文件不仅仅是包含数据内容的容器,它还具有一些属性,描述了文件的元数据(metadata)。这些属性包括文件的权限、修改时间、访问时间、所有者、大小等。

我们可以将文件的概念表示为:文件 = 文件内容 + 文件属性

1. 文件内容(File Content)

文件内容是文件实际存储的数据。对于文本文件,它通常是可读的字符串;对于二进制文件,它可以是任意的数据,如图像、音频、视频或程序代码等。文件内容是文件的核心部分,用户创建、编辑和删除文件时,主要涉及文件内容的操作。

例如,一个文本文件 hello.txt 的内容可能如下:

Hello, World!

2. 文件属性(File Attributes)

文件属性是描述文件元信息的数据,通常是系统自动管理的。这些属性并不直接涉及文件的内容,但它们在文件的管理、访问和权限控制中起着至关重要的作用。常见的文件属性包括:

  • 文件权限(Permissions):指定哪些用户或用户组可以读取、写入或执行文件。
  • 所有者(Owner):文件的创建者或拥有者。
  • 创建时间(Creation Time):文件首次创建的时间。
  • 修改时间(Modification Time, mtime):文件内容最后一次修改的时间。
  • 访问时间(Access Time, atime):文件最后一次被访问的时间(无论是读取、执行还是其他)。
  • 改变时间(Change Time, ctime):文件的元数据(如权限、所有者、位置)最后一次修改的时间。

3. 文件属性详解

  • 修改时间(mtime:修改时间表示文件内容上次修改的时间。每次文件内容改变时,mtime 就会更新。例如,如果你编辑并保存一个文本文件,那么该文件的 mtime 就会更新为当前时间。

  • 访问时间(atime:访问时间表示文件上次被访问的时间。访问可以是读取文件内容、执行程序文件等操作。每当文件被读取时,atime 就会更新。需要注意的是,有些操作系统会进行优化,使得访问文件时不更新 atime,从而减少磁盘 I/O。例如,使用 cat 命令读取文件内容会更新 atime

  • 改变时间(ctime:改变时间表示文件的元数据(如文件权限、文件名、所有者等)最后一次改变的时间。ctime 仅在文件的属性发生变化时更新,而不是文件内容。例如,如果改变了文件的权限或文件的所有者,ctime 会发生变化。

4. 文件操作与属性变化

  • 修改文件内容: 当我们修改文件内容时,文件的 mtime 会更新,而 atimectime 通常不变,除非文件本身被移动、重命名或修改其他元数据。例如,使用 echo "new content" > file.txt 命令修改 file.txt 文件内容时,文件内容发生了变化,因此 mtime 会更新。

  • 访问文件: 当我们访问文件(例如读取文件内容)时,文件的 atime 会更新,但 mtimectime 不会发生变化。例如,使用 cat file.txt 读取文件内容时,文件的 atime 会更新,表示文件被访问了。

  • 修改文件属性: 当我们修改文件的权限、所有者等属性时,文件的 ctime 会更新。这个时间是文件元数据的变化标志,而不是文件内容本身的变化。例如,使用 chmod 修改文件权限时,ctime 会更新,但 mtimeatime 不会发生变化。

5. 文件属性的查看与修改

在 Linux 系统中,可以使用 ls 命令查看文件的基本属性(如权限、所有者、时间等):

ls -l file.txt

输出例子:

-rw-r--r-- 1 user user 128 Feb 7 10:15 file.txt

其中:

  • -rw-r--r-- 是文件的权限。
  • 1 是硬链接数量。
  • user 是文件的所有者。
  • user 是文件的所属用户组。
  • 128 是文件的大小。
  • Feb 7 10:15 是文件的 mtime(修改时间)。

要查看文件的 atimectime,可以使用 stat 命令:

stat file.txt

输出例子:

File: file.txt
Size: 128       Blocks: 8          IO Block: 4096   regular file
Device: 803h/2051d   Inode: 12345678  Links: 1
Access: 2025-01-01 10:00:00.000000000
Modify: 2025-01-01 10:00:00.000000000
Change: 2025-01-01 10:00:00.000000000
  • Accessatime(访问时间)
  • Modifymtime(修改时间)
  • Changectime(更改时间)

Linux 下第一个小程序——进度条

1. 换行 vs 回车:键盘上的时光机

  • 换行(Line Feed, \n:光标移动到 下一行,但水平位置不变 。在一些系统中,换行符是 \n,也有的系统中(如 Windows)回车和换行会一起使用。
  • 回车(Carriage Return, \r:光标回到 当前行的行首,不换行 。在很多操作系统中,回车是用来将光标重置到行首的位置。换行符通常是 \r

生活场景比喻:想象你用打字机写文章,打完一行字后需要做两个动作:

  • 换行(Line Feed):把纸向上推一行(对应 \n
  • 回车(Carriage Return):把打印头移回最左侧(对应 \r

2. 行缓冲区:快递员的打包习惯

行缓冲区(Line Buffering)是指输出流的数据并不是直接输出到屏幕或终端,而是先存储在缓冲区中。当缓冲区满时,数据才会被输出。这种方式提升了效率,减少了对系统资源的频繁访问。在 C 语言中,printf() 是行缓冲的典型例子。它的工作原理是:当你调用 printf() 打印一行文本时,数据并不是立刻显示,而是先被放到缓冲区中,直到遇到换行符 \n 时,系统才会将缓冲区的内容真正输出到屏幕上。

生活场景比喻:快递员不会每收到一个小物件就立刻送货,而是攒满一车再出发。 行缓冲区 就像这个 “攒满一车” 的规则:

  • 遇到 \n 换行符时立刻 “送货”(刷新缓冲区)
  • 缓冲区满时自动刷新
  • 程序正常结束时也会自动刷新

标准输出(stdout)在终端中是 行缓冲 的,如果没有换行符 \n,缓冲区不会自动刷新,导致内容被覆盖或丢失。

实验验证与现象分析
  • 代码 1

    printf("123456\rAB");
    
    • 现象:只显示 AB。实测 windowvs 2022 显示 AB 456 应该是属于 vs 的“个人行为”。
    • 原因\r 将光标移回行首,但缓冲区未刷新,内容被覆盖或未输出。
  • 代码 2

    printf("123456\rAB\n");
    
    • 现象:终端显示 AB3456
    • 原因\n 触发了缓冲区的刷新,AB 覆盖了 12,然后换行。

  • 代码 3

    printf("123456\rAB");
    fflush(stdout); // 手动刷新缓冲区
    
    • 现象:终端显示 AB3456。细节流程:
      • 123456 被输出到终端,光标停留在 6 后面。
      • \r 将光标移动到行首(即 1 的位置)。
      • AB 被输出,覆盖了前两个字符 12,此时终端内容为 AB3456,光标停留在 3 的位置。
    • 原因fflush(stdout) 强制刷新缓冲区,AB 覆盖了 12

    为什么我看到的只有 AB

    我们不妨修改代码,运行后,3 秒内会看到 AB3456,随后提示符会进行覆盖,这也就是为什么我们看到的只有 AB 了:

    int main()
    {printf("123456\rAB");fflush(stdout);sleep(3); // 暂停3秒,观察输出return 0;
    }
    
Shell 提示符的输出行为
  • 当程序运行结束后,Shell 会立即在 当前光标位置 输出提示符(如 [damai@VM-16-11-centos coding]$)。
  • 由于程序结束时,光标停留在 3 的位置,Shell 提示符会从 3 的位置开始输出,覆盖 掉后面的内容 3456
为什么覆盖?
  • 终端的工作机制:终端是一个字符设备,它会严格按照光标位置输出内容。如果光标不在行尾,新输出的内容会从光标位置开始覆盖已有的内容。
  • Shell 提示符的输出:Shell 提示符不会主动换行,而是从当前光标位置开始输出。因此,如果程序没有将光标移动到行尾或换行,提示符就会覆盖程序输出的内容。

知道了这些内容,我们就可以尝试看看下面代码运行的结果了:

#include <stdio.h>
#include <unistd.h>int main()
{// 实验1:无换行符,无刷新printf("123456\rAB");sleep(2); 			// 等待2秒观察现象printf("\n"); 		// 换行以刷新缓冲区// 实验2:有换行符printf("123456\rAB\n");sleep(2); 			// 等待2秒观察现象// 实验3:手动刷新printf("123456\rAB");fflush(stdout); 	// 手动刷新sleep(2); 			// 等待2秒观察现象printf("\n"); 		// 换行return 0;
}

在来一遍,深刻理解

  1. 现象 1:没有换行符 \n 的情况下调用 printf()

    #include <stdio.h>int main()
    {printf("hello Makefile!");sleep(3);return 0;
    }
    

    现象:这时 “hello Makefile!” 会先存入缓冲区,不会立刻显示在屏幕上。直到程序结束,缓冲区中的内容才会被显示出来。所以程序在运行完 sleep(3) 后,才会看到输出结果。

  2. 现象 2:加上换行符 \n

    #include <stdio.h>int main()
    {printf("hello Makefile!\n");sleep(3);return 0;
    }
    

    现象:这时,换行符 \n 会强制刷新缓冲区,所以 “hello Makefile!” 会立即输出,等待 3 秒后程序结束。

  3. 现象 3:调用 fflush(stdout) 强制刷新缓冲区:

    #include <stdio.h>
    #include <unistd.h>
    int main()
    {printf("hello Makefile!");fflush(stdout);  // 强制刷新缓冲区sleep(3);return 0;
    }
    

    现象:调用 fflush(stdout) 后,printf() 输出的内容会立即显示,无论是否遇到换行符,缓冲区中的内容都会被强制刷新到屏幕上,等待 3 秒后程序结束。


3. 倒计时实现

理解了回车换行和行缓冲区的概念后,我们再来做一个倒计时的实现,相信下面的代码很容易理解:

int main()
{int cnt = 10;while (cnt >= 0){printf("%d\r", cnt);fflush(stdout);cnt--;sleep(1);}return 0;
}

运行一下,会发现倒计时的显示效果是:10908070……,而不是预期的 1098 → ……。数字宽度不一致, 当 cnt 为两位数 10 时,输出 10;而当 cnt 变为一位数(如 9)时,输出的是 9。因此,当从两位数输出变为一位数输出时,上一轮输出中的多余字符(例如 10 中的 1)依然残留在屏幕上。我们做以下修改即可:

printf("%2d\r", cnt);	// 调整输出格式:如果 cnt 只有一位数,则会在数字前补空格

再次运行会发现,倒计时站两个字符,前一个字符空出,只有后一个字符在变化,显得不太正常,我们接着做修改:

printf("%-2d\r", cnt);	// 修改对其规则:左对齐

于是,我们的倒计时就实现了:

#include <stdio.h>
#include <unistd.h> // 包含 sleep 函数int main()
{int cnt = 10; // 初始化倒计时值while (cnt >= 0) // 循环直到倒计时结束{printf("%-2d\r", cnt); // 格式化输出,左对齐,固定宽度为2,并覆盖之前输出中多余的字符fflush(stdout);        // 强制刷新缓冲区,确保立即输出cnt--;                 // 倒计时减1sleep(1);              // 暂停1秒}printf("倒计时结束!\n"); // 倒计时完成后输出提示信息return 0;
}

4. 由于进度条相关文章内容过长,详见下一篇文章!


git 的使用

这里会简单叙述 Git 的诞生与核心价值,然而关于 git 远远不止于此,需要大量内容才能将 git 讲清楚,所以,未来我会专门出一个专题来对 git 进行详细的讲解!

一、Git 的本质:时间管理大师的 “时光机”

要理解 Git,我们可以想象一个科幻场景:假设你正在写一本小说,每天创作的新章节都会生成一个独立的时间胶囊。某天你发现主角设定出了问题,只需打开对应日期的胶囊就能恢复原貌。Git 本质上就是这样一个分布式时光机。它通过记录文件的 “快照” 而非差异(如 SVN),让每个开发者电脑都保存完整的版本库。例如:

  • 张三改实验报告:李四作为 “版本管家”,每次收到修改都存档并标注(git commit
  • 多人协作写代码:就像乐队分声部排练,Git 确保所有乐谱修改能精准合并(git merge

二、Linus Torvalds:被逼出来的创新

Linux 之父林纳斯·托瓦兹(Linus Torvalds)与 Git 的渊源,堪称技术界的 “复仇爽文”:

在 2002 年以前,lucubrate Linux 的庞大社区还处于一种极度原始的代码管理状态,依靠着手工合并代码来维系项目 Progress。这无疑给参与其中的开发者们带来了巨大的不便与低效,令人不禁联想起原始部落艰难求生的画面。

随后,商业工具 BitKeeper 的引入仿佛曙光初现,为 Linux 内核的代码管理带来了短暂的清朗时期。却不曾想,因社区成员尝试逆向工程这一敏感行为,(BitKeeper 开发团队)愤然剥夺了 Linux 社区的使用授权。正如同好不容易借到的宝藏工具突然被夺走,开发者们再度陷入了深深的困境,这无疑是压垮骆驼的最后一根稻草。

在这一刻,Linus Torvalds,Linux 之父,心中愤怒与不甘的火焰熊熊燃烧。他深知,若不能寻得有效的解决之道,Linux 项目的未来将满是荆棘。于是,两周,仅仅两周的时间,他凭借着过人的智慧与毅力,完成了 Git 原型的编写。而一个月内,Linux 内核便成功迁移到这个自主开发的全新系统上,整个过程仿若一场奇迹。

" 就像被房东赶走的窘迫之人,在绝望之中亲手盖出了一栋更豪华的别墅。" —— 网友评价

三、Git 的革命性突破

对比传统版本控制工具,Git 实现了三大飞跃:

特性集中式(如 SVN)Git 分布式
网络依赖必须联网提交本地即可完成所有操作
数据安全中央服务器故障即丢失历史每个副本都是完整备份
分支管理创建/合并分支耗时轻量级分支秒级切换

四、Git 的现代发展

Git 自诞生以来,逐渐在全球范围内普及并成为开发人员的标准工具之一。以下是 Git 现代发展的几个关键点:

1. GitHub 的崛起

2008 年,GitHub 上线,它为 Git 提供了一个强大的托管平台,使得开发者可以方便地在线托管代码,并且与其他开发者协作。GitHub 通过引入 Pull Request(PR)功能,让 Git 的协作模式更加高效,推动了开源社区的快速发展。很多知名的开源项目(如 jQuery、Node.js 等)都迁移到了 GitHub。

2. 中国化适配:Gitee 等本土平台

由于 GitHub 在中国大陆访问受到一定限制,本土平台如 Gitee 等逐渐兴起。Gitee 不仅解决了访问速度的问题,还提供了针对中国开发者的多种本地化功能,使得 Git 在国内的普及更加顺利。

3. 可视化工具的普及

随着 Git 的普及,越来越多的可视化工具如 GitKraken、SourceTree 等应运而生。这些工具通过图形界面简化了 Git 的操作,使得即使是没有命令行经验的开发者也能轻松上手。Git 的复杂操作变得更加直观和易用,降低了学习成本。

4. 版本控制的精确性和回溯功能

Git 的最大优势之一就是其强大的回溯能力。当开发者说“我回滚一下”,实际上就是“导演喊‘Cut!’重拍第 3 幕”。每个 commit 就像电影中的一个镜头,都可以精确回溯、查看和恢复,确保每一步的修改都能被追溯和复原。

五、Git 改变了软件开发的方式

从 Linus Torvalds 的灵感到 Git 成为全球开发者的标准工具,Git 无疑已经成为软件开发的核心技术之一。Git 的核心价值不仅仅在于它如何管理版本,更在于它如何改变了开发者的工作方式、提高了协作效率,并且推动了开源软件的发展。

正如 Linus 所说:“Talk is cheap. Show me the code.” Git 用代码改变了世界,它的出现不仅仅是一个工具的革命,更是一种工作方式、协作方式和思想方式的革命。Git 的设计思想和实践,使得开发者能够更加专注于代码的创作,而不必过度担心版本管理的问题。

Git 不仅是一个技术工具,更是一种创新精神的体现——快速响应变化、追求效率、关注团队协作、并持续优化。它的成功,也象征着开源社区强大的生命力和创新能力。

六、git 的使用(Git 教程)

Git 的核心概念
  1. 仓库(Repository):Git 仓库是项目的核心,包含了项目的所有文件和历史记录。每个开发者都有一个完整的仓库副本。
  2. 提交(Commit):每次提交都是项目的一个快照,记录了文件的更改和提交信息。
  3. 分支(Branch):分支是开发中的独立线路,允许开发者在不同的分支上并行工作。
  4. 合并(Merge):将不同分支的更改合并到一起,确保代码的一致性。
  5. 克隆(Clone):从远程仓库复制一个完整的仓库到本地。
  6. 拉取(Pull):从远程仓库获取最新的更改并合并到本地分支。
  7. 推送(Push):将本地的更改上传到远程仓库。
Git 的工作流程
  1. 初始化仓库:使用 git init 命令创建一个新的 Git 仓库。
  2. 添加文件:使用 git add 命令将文件添加到暂存区。
  3. 提交更改:使用 git commit 命令将暂存区的更改提交到仓库。
  4. 创建分支:使用 git branch 命令创建一个新的分支。
  5. 切换分支:使用 git checkout 命令切换到不同的分支。
  6. 合并分支:使用 git merge 命令将不同分支的更改合并到一起。
  7. 查看历史:使用 git log 命令查看提交历史。

我们在 Linux CentOS 7 云服务器上使用 Xshell 连接并操作 Git 时的一些常用的 Git 命令:

1. 安装 Git

首先,确保你的 CentOS 7 系统上已经安装了 Git。如果没有安装,可以使用以下命令进行安装:

# 更新 yum 软件包
sudo yum update -y# 安装 Git
sudo yum install -y git

安装完成后,可以通过以下命令检查 Git 版本,确认安装成功:

git --version
2. 配置 Git

第一次使用 Git 之前,需要配置用户名和邮箱,这些信息会出现在每次提交的记录中。

git config --global user.name "your_name"   # 设置用户名
git config --global user.email "your_email@example.com"  # 设置邮箱

通过以下命令查看当前的 Git 配置:

git config --list
3. 创建/初始化仓库

在当前目录下创建一个新的 Git 仓库,使用以下命令(这会在当前目录下生成一个 .git 目录,用于存储 Git 的版本控制信息):

git init
4. 克隆远程仓库

如果想从远程仓库克隆一个项目到本地,可以使用 git clone 命令,这会将远程仓库的内容克隆到当前目录下的一个新文件夹中:

git clone https://github.com/username/repository.git
5. 查看仓库状态

使用以下命令可以查看当前仓库的状态,包括哪些文件被修改、哪些文件被暂存等:

git status
6. 添加文件到暂存区

在对文件进行修改后,需要将文件添加到暂存区(Stage),以便后续提交:

git add file_name	# 例如:git add temp.txt ,添加文件到暂存区

如果想添加所有修改过的文件,可以使用:

git add .
7. 提交更改

将暂存区的文件提交到本地仓库,-m 选项后面跟的是本次提交的描述信息。描述信息就是提交日志,尽量提交有意义的信息!:

git commit -m "Your commit message"		# 例如:git commit -m "第一次提交" ,提交到本地仓库
8. 查看提交历史

使用以下命令可以查看当前仓库的提交历史:

git log

还可以通过 --oneline 选项简化输出:

git log --oneline
9. 查看远程仓库

查看当前配置的远程仓库:

git remote -v
10. 添加/连接远程仓库

添加/连接一个新的远程仓库:

git remote add origin 远程版本库的URL
# 例如:git remote add origin https://github.com/your_username/your_repo.git
11. 推送本地分支到远程仓库

将本地分支的提交推送到远程仓库:

git push origin 分支名称
git push -u origin master		# 将 master 分支推送,并设置默认上游分支(常用)
12. 拉取远程仓库的更新

从远程仓库拉取最新的更改并合并到当前分支:

git pull origin 分支名称
git pull origin master		# 拉取远程仓库的最新代码并合并
13. 撤销工作区的修改

撤销工作区中某个文件的修改,恢复到最近一次提交的状态:

git checkout -- 文件名称
# 例如:git checkout -- file.txt	丢弃工作区修改,恢复到最后一次提交状态
14. 撤销暂存区的修改

将某个文件从暂存区撤回到工作区:

git reset HEAD 文件名称
15. 撤销提交

撤销上一次提交,只回退 commit,保留代码修改:

git reset --soft HEAD~1

撤销最近一次提交,并将更改放回工作区:

git reset --soft HEAD^

如果你想撤销提交并丢弃更改,可以使用:

git reset --hard HEAD^
16. 创建标签

创建一个新的标签:

git tag 标签名称
17. 查看标签

查看所有标签:

git tag
18. 推送标签到远程仓库

将本地标签推送到远程仓库:

git push origin 标签名称
19. 查看差异

查看工作区与暂存区的差异:

git diff

查看暂存区与最新提交的差异:

git diff --cached
20. 查看远程分支

查看远程仓库的所有分支:

git branch -r
21. 删除远程分支

删除远程仓库的指定分支:

git push origin --delete 分支名称

至于分支管理、多人协作和冲突管理等操作暂时用不到,放到以后再说,有兴趣可自行百度。


除了前面提到的基本操作,Git 在使用时还存在一些常见的注意事项(这里提到部分内容,如有其他问题需勤于百度):

1. Git 只会记录已添加到暂存区(staging area)的修改

Git 不会自动跟踪所有修改。它只会跟踪你手动添加到暂存区的修改,即 默认记录修改部分(工作区和暂存区)。也就是通过 git add 添加的文件或更改。

  • 工作区(Working Directory):对文件的任何修改都首先体现在工作区。
  • 暂存区(Staging Area):通过 git add 命令将修改暂存到 Git 中的暂存区。
  • 本地仓库(Repository):通过 git commit 命令,将暂存区的修改提交到本地仓库。

关键点(工作流程): 只有 git add 命令添加到暂存区的文件才会被 git commit 提交(修改文件 → git add 添加到暂存区 → git commit 提交到仓库)。

示例:

echo "This is a test" > file.txt      # 修改文件
git status                            # 查看状态,file.txt 会显示为修改状态
git add file.txt                      # 添加到暂存区
git commit -m "Updated file.txt"      # 提交修改

忘记 git add 的后果

如果修改了文件但没有执行 git add,然后直接 git commit,Git 是不会将这些修改提交的。因此,Git 只会记录已经添加到暂存区的修改。

2. Git 忽略某些文件:.gitignore

如果不希望某些文件被 Git 跟踪(如编译出来的二进制文件、IDE 配置文件等),可以使用 .gitignore 文件来指定不需要跟踪的文件或文件夹。

创建 .gitignore

# 例如,忽略所有 *.log 文件
echo "*.log" >> .gitignore

然后 git add .gitignore 提交 .gitignore 文件!!!

关键点: .gitignore 文件是 Git 跟踪文件的方式之一。

3. Git 配置文件的管理

Git 配置文件分为三种层级:

  • 系统级:影响系统上所有用户的配置,通常位于 /etc/gitconfig
  • 全局级:影响当前用户的配置,通常位于 ~/.gitconfig
  • 仓库级:只对当前仓库生效,位于仓库的 .git/config

可以通过以下命令查看和修改 Git 配置:

git config --global user.name "Your Name"     # 设置全局用户名
git config --global user.email "you@example.com"  # 设置全局邮箱

查看当前配置:

git config --list

Linux 调试器 —— gdb 的使用

一、背景知识

在程序开发过程中,为了有效定位和修复代码中的错误,调试工具起着至关重要的作用。程序的发布方式主要有两种:debug 模式和 release 模式。

  • debug 模式:此模式下编译生成的程序包含了丰富的调试信息,例如变量名、函数名、源代码行号等,方便开发者借助调试工具进行错误排查。不过,debug 模式通常会增加程序的体积,并且在一定程度上降低运行效率。
  • release 模式:旨在提供高性能的可执行程序,编译时会进行各种优化,剔除不必要的调试信息,以确保程序运行速度和资源利用率最大化。在 Linux 系统中,使用 gcc 或 g++ 编译器编译出来的二进制程序,默认是 release 模式。

若要使用 gdb(GNU Debugger)对程序进行调试,必须在源代码生成二进制程序时,加上 -g 选项。这样,编译器会在生成的可执行文件中嵌入调试所需的信息,使得 gdb 能够准确地关联程序运行状态与源代码。

编译生成调试信息

假设有一个源文件 temp.c-g 告诉编译器生成调试信息,-o temp 指定输出文件名。可以通过以下命令编译程序并生成调试信息:

gcc -g temp.c -o temp

启动 gdb

gdb 启动时需要提供一个已经编译好的二进制文件。假设我们有一个名为 temp 的程序文件,通过以下命令启动 gdb 准备进行调试:

gdb temp

退出 GDB

退出 GDB 的方式有两种:

  • 输入 quitq 命令(常用)。
  • 按下 Ctrl + D

二、Windows IDE 对应功能

对于 Windows 用户习惯的 IDE 调试工具(如 Visual Studio),gdb 也提供了类似的功能。例如:

  • 设置断点:在源代码中设置断点,并在程序执行时停止。
  • 单步调试:支持单步执行,可以选择逐行执行代码,或者进入函数内调试。
  • 变量观察:支持查看和修改变量的值,并可以在调试过程中动态修改它们。
  • 堆栈跟踪:查看当前调用栈以及每个函数的参数。

与 Windows 中的 IDE 相比,gdb 是命令行工具,因此需要通过命令行输入调试命令,但功能是非常强大的。


三、gdb 常用命令

GDB 提示符的作用

  • 当你启动 GDB 并加载程序后,GDB 会进入交互模式,显示 (gdb) 提示符。
  • 这个提示符表示 GDB 正在等待你输入命令。
  • 你只需要在 (gdb) 后面输入命令,按回车执行。

例如:

(gdb) break main	# (gdb) 是 GDB 的提示符,只需要输入 break main 并按回车即可
1. 查看源代码

listl:查看源代码。

  • list <行号>:从指定行号开始显示源代码。
  • list <函数名>:显示指定函数的源代码。
  • 默认每次显示 10 行,按回车键继续显示后续内容。
(gdb) list 10      # 显示从第 10 行开始的源代码
(gdb) list         # 显示当前行及接下来的 10 行
(gdb) l main       # 显示 main 函数的源代码
2. 运行程序

runr:从头开始运行程序。

  • 如果程序需要参数,可以在 run 后面加上参数:
(gdb) run           	# 启动程序的执行,不带任何命令行参数
(gdb) run arg1 arg2   	# 启动程序的执行,并传递两个命令行参数:arg1 和 arg2
3. 单步执行
  • nextn:单步执行(不进入函数内部)。
  • steps:单步执行(进入函数内部)。
(gdb) next		# 单步执行
(gdb) step		# 进入函数内部
4. 设置断点

breakb:设置断点。

  • break <行号>:在指定行设置断点。
  • break <函数名>:在函数入口处设置断点。
  • info breakpoints:查看当前设置的所有断点及其状态。
(gdb) break 20				# 在第 20 行设置断点
(gdb) break main			# 在 main 函数的开头设置断点
(gdb) info breakpoints    	# 查看所有断点信息
5. 删除断点
  • delete breakpoints:删除所有断点。
  • delete breakpoints n:删除序号为 n 的断点。
  • disable breakpoints:禁用所有断点,使其在下次调试时不起作用。
  • enable breakpoints:启用所有已禁用的断点。
(gdb) delete breakpoints     # 删除所有断点
(gdb) delete breakpoints 1   # 删除序号为 1 的断点
(gdb) disable breakpoints    # 禁用所有断点
(gdb) enable breakpoints     # 启用所有断点
6. 继续执行

continuec:从当前位置继续执行,直到遇到下一个断点或程序结束。

(gdb) continue    # 继续执行程序
7. 查看和修改变量

printp:打印变量的值。

  • print <变量名>:打印变量的值。
  • print <表达式>:计算并打印表达式的值。
(gdb) print x			# 打印变量 x 的值
(gdb) print x + y

set var 变量名=值:修改变量的值。

(gdb) set var x=10    # 将变量 x 的值修改为 10
8. 跟踪变量
  • display <变量名>:每次程序暂停时,自动打印变量的值。
  • undisplay <编号>:取消对变量的跟踪。
(gdb) display x     # 每次停止时显示变量 x 的值
(gdb) undisplay     # 取消所有跟踪变量
9. 查看函数调用栈

backtracebt:查看当前函数的调用栈(包括参数和调用位置)。

(gdb) backtrace      # 查看函数调用栈
10. 查看局部变量

info locals:查看当前函数的局部变量。

(gdb) info locals	# 查看当前栈帧的局部变量
11. 跳转到指定行

until <行号>:跳转到指定行。

(gdb) until 30		# 跳转到第30行
12. 退出函数

finish:执行完当前函数并暂停。

(gdb) finish	# 执行完当前函数并暂停

共勉

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/88164.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/88164.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/88164.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前缀和题目:连续的子数组和

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;连续的子数组和 出处&#xff1a;523. 连续的子数组和 难度 5 级 题目描述 要求 给定一个整数数组 nums \texttt{nums} nums 和一个整数 k \tex…

队的简单介绍

队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出 FIFO(First In First Out)的特点。 入队列&#xff1a;进行插入操作的一端称为队尾。 出队列&#xff1a;进行删除操作的一端称为队头。 入队列…

AI-Sphere-Butler之如何将豆包桌面版对接到AI全能管家~新玩法(一)

环境&#xff1a; AI-Sphere-Butler VBCABLE2.1.58 Win10专业版 豆包桌面版1.47.4 ubuntu22.04 英伟达4070ti 12G python3.10 问题描述&#xff1a; AI-Sphere-Butler之如何将豆包桌面版对接到AI全能管家~新玩法&#xff08;一&#xff09; 聊天视频&#xff1a; AI真…

【STM32】启动流程

1、.s启动文件解析 STM32的启动文件&#xff08;一般是.s汇编文件&#xff0c;如startup_stm32f407xx.s&#xff09;是STM32上电后执行的第一段代码&#xff0c;承担着“系统初始化化引导员”的角色。 它的主要作用是设置初始化栈指针&#xff08;SP&#xff09;、程序计数器&…

【vim】通过vim编辑器打开、修改、退出配置文件

通过vim编辑器打开任一配置文件 vim /etc/profile 英文输入下&#xff0c;按i键进入INSERT模式&#xff0c;修改配置文件 完成修改后&#xff0c;按esc键退出INSERT模式 英文输入下&#xff0c;输入":wq!"&#xff0c;即可保存并退出 :q #不保存并退出 :q! …

Effective Modern C++ 条款6:当 auto 推导类型不符合预期时,使用显式类型初始化惯用法

在C开发中&#xff0c;auto关键字以其简洁性和高效性被广泛使用。然而&#xff0c;“自动推导”并非万能&#xff0c;尤其在某些特殊场景下&#xff0c;auto的推导结果可能与开发者预期不符&#xff0c;甚至导致未定义行为。今天&#xff0c;我们以《Effective Modern C》条款6…

学习Linux进程冻结技术

原文&#xff1a;蜗窝科技Linux进程冻结技术 功耗中经常需要用到&#xff0c;但是linux这块了解甚少&#xff0c;看到这个文章还蛮适合我阅读的 1 什么是进程冻结 进程冻结技术&#xff08;freezing of tasks&#xff09;是指在系统hibernate或者suspend的时候&#xff0c;将…

GitHub 趋势日报 (2025年06月22日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 624 LLMs-from-scratch 523 ai-engineering-hub 501 n8n 320 data-engineer-handb…

kotlin中为什么新增扩展函数功能?

在 Kotlin 中&#xff0c;扩展函数的本质是「不修改原有类代码&#xff0c;为其新增功能」&#xff0c;这源自编程中「开闭原则」&#xff08;对扩展开放&#xff0c;对修改关闭&#xff09;的第一性原理。 核心需求&#xff1a;当需要给第三方库的类&#xff08;如 Android 的…

excel 数据透视表介绍

Excel 数据透视表(PivotTable)就是你的数据分析神器!它能帮你快速汇总、分类、比较和分析 大量数据&#xff0c;从看似杂乱无章的表格中一键提取关键信息 &#xff0c;生成交互式的汇总报告。无需复杂公式&#xff0c;只需拖拽几下&#xff0c;就能让数据“开口说话”&#xff…

半导体行业中的专用标准产品ASSP是什么?

半导体行业中的专用标准产品ASSP是什么&#xff1f; “专用标准产品”&#xff08;ASSP - Application Specific Standard Product&#xff09;是半导体集成电路中的一个重要分类。 你可以把它理解为介于通用标准产品和全定制ASIC之间的一种芯片。以下是它的核心定义和特点&a…

秋招Day14 - MySQL - 锁

MySQL中有几种类型的锁&#xff1f; 锁粒度来分&#xff0c;有表锁、页锁和行锁。 加锁机制划分&#xff0c;有乐观锁和悲观锁。 按兼容性划分&#xff0c;有共享锁和排他锁。 按锁模式划分&#xff0c;有记录锁&#xff0c;间隙锁&#xff0c;next-key锁&#xff0c;意向锁…

/var/lib/docker/overlay2目录过大怎么办

/var/lib/docker/overlay2 是 Docker 默认用于存储 容器镜像和容器运行时数据 的核心目录&#xff0c;基于 overlay2 存储驱动实现。以下是其具体作用和内容的详细解析&#xff1a; 1. overlay2 目录的作用 存储镜像分层结构&#xff1a; Docker 镜像采用分层设计&#xff0c;o…

JimuReport:一款免费的数据可视化报表工具

JimuReport&#xff08;积木报表&#xff09;是一款免费的企业级数据可视化报表软件&#xff0c;提供拖拽的方式像搭建积木一样完成在线设计&#xff0c;功能涵盖数据报表、打印设计、图表报表、门户设计、大屏设计等。 数据源 JimuReport 支持 30 多种数据源&#xff0c;包括…

Neo4j.5.X社区版创建数据库和切换数据库

在使用Neo4j数据库&#xff08;版本&#xff1a;neo4j-community-5.22.0&#xff09;时&#xff0c;系统自带的“neo4j”和“system”数据库适用于日常的简单学习和练习&#xff0c;但对于新的项目&#xff0c;将项目数据与练习数据混用会带来诸多不便&#xff0c;例如查询效率…

DAY33神经网络

浙大疏锦行 定义了一个简单的神经网络&#xff0c;主要是掌握pytorch框架

拼团系统多层限流架构详解

拼团系统多层限流架构详解 一、整体架构设计理念 多层限流采用"层层设防"思想&#xff0c;通过网关层全局流量控制→服务层接口粒度限流→本地资源隔离→热点参数精准防护的四级防御体系&#xff0c;实现从粗到细的流量治理&#xff0c;确保大促期间系统稳定性。 …

[ctfshow web入门] web92 `==`特性与intval特性

信息收集 和之前的题差不多&#xff0c;这次是使用了不严格相等的&#xff0c;详情看这篇博客&#xff1a; 和 在 PHP 中有何区别&#xff1f;一共包含哪些部分&#xff1f; 首先&#xff0c;不能使$num 4476&#xff0c;然后需要使intval($num,0)4476 include("flag…

在Springboot项目部署时遇到,centos服务器上,curl请求目标地址不通 ,curl -x 可以请求通的解决办法

在甲方服务器部署项目时&#xff0c;通常遇到需要开通外网权限的问题&#xff0c;有的是直接给开通服务器的白名单&#xff0c;就可以直接访问白名单外网地址了。也有的是通过网络转发&#xff0c;将url前面的部分替换&#xff0c;可以进行网络请求。有一次遇到一个罕见的&…

Python异步爬虫编程技巧:从入门到高级实战指南

Python异步爬虫编程技巧&#xff1a;从入门到高级实战指南 &#x1f680; &#x1f4da; 目录 前言&#xff1a;为什么要学异步爬虫异步编程基础概念异步爬虫核心技术栈入门实战&#xff1a;第一个异步爬虫进阶技巧&#xff1a;并发控制与资源管理高级实战&#xff1a;分布式…