【Linux】想学习Linux不看这一篇你就慢了-->工具篇(四)gdb、进度条程序

主页:HABUO🍁主页:HABUO

🍁Linux专栏🍁

🍁如果再也不能见到你,祝你早安,午安,晚安🍁


目录

📕一、 Linux调试器 - gdb

✨1. gdb是什么

✨2. gdb的使用

更改发行版本:

调试阶段:

1️⃣进入调试模式

2️⃣显示代码

3️⃣断点相关指令

4️⃣调试运行 

5️⃣显示变量

6️⃣其它指令

7️⃣退出调试模式

📕二、Linux下第一条程序-进度条

✨1. 背景知识:

✨2. 进度条程序

📕三、总结 


前言

本篇博客我们来了解最后一个工具gdb,至此我们在Linux上就可以进行编写代码、调试代码、编译代码,一整套完整的线路便贯穿了起来,文章结尾我们就开始在Linux上写第一段程序进度条以此打开Linux新篇章。如果大家对于之前工具陌生或者遗忘那个,请务必进行复习,不然难以进行,大家加油!

📕一、 Linux调试器 - gdb

✨1. gdb是什么

GDB 是 GNU 项目开发的一个命令行源代码级调试器。它是 Linux 和其他类 Unix 系统(如 macOS,BSD)上 C、C++、Rust、Go、Fortran 等编程语言开发的标准调试工具。简单来说,GDB 允许你深入正在运行的程序内部:

  • 1️⃣启动程序: 在受控环境下运行你的程序。
  • 2️⃣暂停执行: 在特定点(断点)或发生特定事件(如信号)时停止程序。
  • 3️⃣检查状态: 当程序暂停时,你可以查看:
  •                         变量的值(局部变量、全局变量)。
  •                         程序执行到了哪一行源代码。
  •                         函数调用堆栈(backtrace)。
  •                         寄存器的内容。
  •                         内存区域的内容。
  • 4️⃣控制执行: 在暂停点之后,你可以:
  •                         单步执行代码(逐行 step 或逐过程 next)。
  •                         继续执行直到下一个断点或程序结束 continue
  •                         跳入函数调用 step 或跳过函数调用 next
  •                         跳出当前函数 finish
  •                         甚至在运行时修改变量的值(小心使用!)。
  • 5️⃣分析崩溃: 当程序崩溃(如段错误 Segmentation Fault)时,GDB 可以加载产生的 core dump 文件,让你查看程序崩溃时的状态(调用栈、变量值等),极大地方便了事后分析。

总结的说就是和VS环境中进行调试是一样的效果,只不过操作不同罢了 

✨2. gdb的使用

更改发行版本:

默认情况下我们在Linux上是无法进行调试的,因为gcc/g++默认生成的可执行是release版本的

相信在学习C语言时所使用的VS环境,页面上方有release/debug更换的按钮,那么到底是什么意思呢?

debug版本就是生成的可执行是调试版本,里面加了一些供程序员进行调试的信息

release版本就是去掉了调试的信息,文件大小更小了,把一些对于用户没有价值的东西去掉

总结如下

特性Debug 版本Release 版本
优化等级无优化(-O0高级优化(如 -O2/-O3
调试符号包含(可被 GDB 等调试器读取)不包含(或剥离)
代码可读性代码顺序与源代码一致代码被重排、内联、删减(难以阅读)
性能慢(保留所有检查)快(激进优化)
文件大小大(含调试信息)小(无冗余信息)
运行时检查启用(如断言 assert()禁用(断言被忽略)
适用场景开发、调试阶段正式部署给用户

那么如何更改Linux下的发行版本?

gcc test.c -o testdebug -g 

只需在gcc编译时后面加入-g即可

使用指令

readelf   -S   XXXX(可执行文件名称) 

就可以查看Linux下可执行程序二进制代码的一些信息。elf就是Linux下的可执行文件格式形式,可以观察到,debug版本里面多了一些调试信息

调试阶段:

我们将以下述代码为例进行调试:

  1 #include<stdio.h>
  2 
  3 
  4 int Add(int left, int right)
  5 {
  6     return left + right;
  7 }
  8 
  9 int main()
 10 {
 11     int sum = Add(10, 20);
 12     printf("sum = %d\n", sum);                                                                                                                           
 13     printf("hello gdba\n");
 14     printf("hello gdbb\n");
 15     printf("hello gdbc\n");
 16     printf("hello gdbd\n");
 17     printf("hello gdbe\n");
 18     printf("hello gdbf\n");
 19     printf("hello gdbg\n");
 20     printf("hello gdbh\n");
 21     printf("hello gdbi\n");
 22     printf("hello gdbj\n");
 23     return 0;
 24 }
1️⃣进入调试模式
  • 使用指令gdb   XXX(调试版本可执行)

2️⃣显示代码
  • 指令:l      
  • l + n(行号):从第n行开始显示

需要注意:gdb会记住上次输入的指令,之后按回车即可 

3️⃣断点相关指令
  • break 行号:在某一行设置断点。b 行号即可
  • break 函数名:在某个函数开头设置断点 
  • info break :查看断点信息。info b即可
  • delete  断电的编号:删除断点。d  编号即可

4️⃣调试运行 
  • r:运行,有断点会在第一个断点处停下,无断点直接运行结束
  • c:运行至下一个断点处结束

  • n:逐过程,即在调用函数处不进入函数内部
  • s:逐语句,在调用函数处进入函数内

5️⃣显示变量
  • breaktrace(或bt):查看各级函数调用堆栈
  • p 变量:打印变量值。(该指令只会显示一次,不会随着程序的执行而变动)
  • display 变量:常显示变量,会类似VS环境中随着程序的变动而改变
  • undisplay 变量编号:将常显示变量取消

6️⃣其它指令
  • finish:执行到当前函数返回,然后停下来等待命令(这个指令可以让我们去判断错误具体出现在那个函数)
  • until:跳转到指定行,并且前面程序均已执行

7️⃣退出调试模式
  • 指令 quit:退出gdb。q即可

总结

上面的指令都了解的话,用起来gdb已经没啥大问题了,下面把常见的一些gdb指令总结一下,如果再后续的使用过程中用到了,大家回来查阅即可。

  • list/l 行号:显示binFile源代码,接着上次的位置往下列,每次列10行。
  • list/l 函数名:列出某个函数的源代码。
  • r或run:运行程序。
  • n 或 next:单条执行。
  • s或step:进入函数调用
  • break(b) 行号:在某一行设置断点
  • break 函数名:在某个函数开头设置断点
  • info break :查看断点信息。
  • finish:执行到当前函数返回,然后挺下来等待命令
  • print(p):打印表达式的值,通过表达式可以修改变量的值或者调用函数 
  • p 变量:打印变量值。
  • set var:修改变量的值
  • continue(或c):从当前位置开始连续而非单步执行程序
  • run(或r):从开始连续而非单步执行程序
  • delete breakpoints:删除所有断点
  • delete breakpoints n:删除序号为n的断点
  • disable breakpoints:禁用断点
  • enable breakpoints:启用断点
  • info(或i) breakpoints:参看当前设置了哪些断点
  • display 变量名:跟踪查看一个变量,每次停下来都显示它的值
  • undisplay:取消对先前设置的那些变量的跟踪
  • until X行号:跳至X行
  • breaktrace(或bt):查看各级函数调用及参数
  • info(i) locals:查看当前栈帧局部变量的值
  • quit:退出gdb

📕二、Linux下第一条程序-进度条

✨1. 背景知识:

先看一段代码:

#include<stdio.h>
#include<unistd.h>

int main()
{
    printf("这是一个试验程序");
    sleep(3);                                                                                                                                            
    return 0;
}

运行之后,会发现,printf竟然没有打印,而是停了3秒之后才出现,相信你会说,你自己sleep了3秒,能怪人家不出现?请看下述代码:

printf("这是一个试验程序\n"); 

如果在printf加了回车换行,你会惊奇的发现,printf先打印,然后休眠了3秒之后代码才结束,这是为何?好,知识点来了。

知识点1

首先针对于没有回车换行,为什么没有?

是因为此时printf所要打印的内容,储存在了缓冲区,而有了\n为什么就有了呢?

这涉及到了标准输出(stdout)的缓冲机制,如下:

缓冲机制类型

  • 行缓冲(Line-buffered):遇到换行符 \n 时自动刷新缓冲区(立即输出内容)。

  • 全缓冲(Fully-buffered):缓冲区满或显式刷新时才输出。

  • 无缓冲(Unbuffered):立即输出(如 stderr)。

终端环境下的 stdout 默认是行缓冲

因此两段代码的具体解释如下:

printf("这是一个试验程序\n");  // 末尾有换行符
sleep(3);
  • \n 触发刷新:换行符使缓冲区立即刷新,内容立刻显示在终端。

  • 后续休眠:之后程序休眠 3 秒,用户先看到输出,再等待。

printf("这是一个试验程序");  // 无换行符
sleep(3);
  • 缓冲区未刷新:输出内容暂存于内存缓冲区(未满且未遇到 \n)。

  • 先休眠 3 秒:程序进入休眠,此时终端无输出。

  • 程序结束时刷新main 函数返回前,自动刷新所有缓冲区,内容在休眠结束后显示。

所以也就是说终端环境下stdout是行缓冲,\n有刷新行缓冲区的功效,是!怎么证明?如下:

int main()
{
    printf("这是一个试验程序");
    fflush(stdout);                                                                                                                                      
    sleep(3);                                                                                                               
    return 0;                                                                                                               
}                                                                                                                           

fflush是刷新缓冲区的,此时你便会看到,先打印结果,再休眠3秒,关于缓冲区的具体知识,在我们学习到Linux后面的时候就会明白

知识点2

上面提到了回车换行,怎么那么陌生,我们不都常说换行嘛,怎么来个回车换行,什么意思?

换行是换行,回车是回车,换行的意思是换到下一行但是不回退到下一行的初始位置

回车是在当前行回退到初始位置

回车换行就是既换到下一行又回退到下一行的初始位置,即我们键盘的ENTER键

对于上述回车换行,我们见下一段代码:

int main()
{
    int cnt = 10;
    while(cnt)
    {
        printf("这是一个倒计时:%2d\r", cnt);
        fflush(stdout);
        cnt--;                                                                                                                                  
        sleep(1);
    }
    return 0;
}

/r就是回车的作用,但是它没有刷新缓冲区的功能,所以我们每次要进行刷新缓冲区才能看到一个正常的倒计时,如下:

✨2. 进度条程序

有了前面的知识铺垫,我们就可以写一个Linux下第一条小程序进度条如下所示:

首先,需要101个字符数组,因为最后一个需要放‘\0’,因而需要101个空间

#define NUM 101
char pg[NUM];
memset(pg, '\0', sizeof(pg));

其次需要对这个数组进行初始化,并且数组赋值肯定是在一个循环当中的,因此需要定义一个临时变量,一来控制循环,二来改变数组里面的值。对于%后面的小圈圈,我们可以让|  \  -  / 四个字符轮流转换视觉上就显示的是转圈圈。

char cir[4] = {'|', '\\', '-', '/'};
int cnt = 0;
   while(cnt <= 100)                                                                                                                                    
   {
        printf("[%-100s][%d%%][%c]\r", pg, cnt, cir[cnt % 4]);
        fflush(stdout);
        pg[cnt++] = STYLE;
        usleep(50000);
    }

对于fflush,与sleep上面例子有不再赘述,至于usleep是为了让它走得相对快些,让它5秒走完,粗略为100次循环,则每次是5/100 = 0.05,usleep是微秒,则0.05*1000000 = 50000,所以让它休眠50000微秒

尤其注意

[%-100s][%d%%][%c]\r,\r必须要放置在最后,不然就会让一部分括号跑到前面,因为回车你把前面的东西清零,\r后面的东西就会跟着上前,每次都是这样,时间如果短的话就会视觉上显示\r后面的东西在前面。

%100s是预留100个空间,没有-是右对齐

%-100s是预留100个空间,并且是左对齐

%是一个特使符号如果要显示%必须%%

cnt % 4:是为了让每次数值控制在0-3以此让小圈圈转起来


📕三、总结 

本篇博客我们主要了解了 Linux 调试器 gdb 和 Linux 下的进度条程序。gdb 是一个命令行源代码级调试器,可用于调试多种编程语言,在 Linux 等系统中发挥重要作用,通过示例代码展示了其基本使用方法,如设置断点、查看变量等。同时,讲解了 Linux 下第一条程序 —— 进度条的实现,介绍了 printf 的缓冲机制以及如何利用相关函数实现动态进度条效果。希望大家有所收获!


评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值