Linux中的开发工具(下)

发布于:2025-03-26 ⋅ 阅读:(26) ⋅ 点赞:(0)

目录

make/makefile

扩展语法

应用---进度条

缓冲区

git的使用

安装git

创建项目

使用

克隆

提交到暂存区

提交到本地仓库

提交到远程仓库

gdb/cgdb

使用

预备

实操

条件断点


make/makefile

  • 会不会写makefile,从⼀个侧⾯说明了⼀个⼈是否具备完成⼤型⼯程的能⼒
  • ⼀个⼯程中的源⽂件不计数,其按类型、功能、模块分别放在若⼲个⽬录中,makefile定义了⼀ 系列的规则来指定,哪些⽂件需要先编译,哪些⽂件需要后编译,哪些⽂件需要重新编译,甚⾄ 于进⾏更复杂的功能操作
  • makefile带来的好处就是⸺“⾃动化编译”,⼀旦写好,只需要⼀个make命令,整个⼯程完全 ⾃动编译,极⼤的提⾼了软件开发的效率。
  • make是⼀个命令⼯具,是⼀个解释makefile中指令的命令⼯具,⼀般来说,⼤多数的IDE都有这 个命令,⽐如:Delphi的make,VisualC++的nmake,Linux下GNU的make。可⻅,makefile 都成为了⼀种在⼯程⽅⾯的编译⽅法。
  • make是⼀条命令,makefile是⼀个⽂件,两个搭配使⽤,完成项⽬⾃动化构建。

基本用法:

当我们写了多个源代码,为了方便,我们可以新建一个makefile文件。

⼀般我们这种clean的⽬标⽂件,我们将它设置为伪目标,用 .PHONY修饰,伪目标的特性是总是被执行。

解释:

看一个现象:

我们加上 .PHONY修饰就可以连续执行了:

为什么加上.PHONY修饰就能连续执行?

因为 .PHONY修饰就是总是被执行生成目标文件。

而内容变了,往往属性就变了(大小变了,内容修改时间也变了),而属性变了内容不一定变!

演示:

这个时间和 .PHONY 有什么关系呢?

加了.PHONY修饰就是总是被执行,没加就是总是不被执行,也就是说就是没加.PHONY导致了不能连续生成可执行文件。

原理:

这个时间就是它们的Modify时间,.PHONY的作用就是忽略源文件和可执行文件的时间对比。

我们知道touch有新建文件的意思,但还有刷新文件时间的意思,我们刷新一下,就可以连续生成可执行文件了。

继续看:

但是我们一般不这样写,我们一般直接写一行就行,这只是推导。

扩展语法

我们每次写得时候,发现make一下,命令行都会回显,我们怎么样才能不回显?

加上@即可!

如果有多个源文件,makefile怎么写呢?

应用---进度条

缓冲区

我们知道\n是换行,而回车是\r(光标回到当前行的起始位置,而不清除原始数据,如果新数据来到,只覆盖而已),换行回车\n\r。

我们键盘上的回车键其实就是我们这里的回车换行键。

我们简单用printf函数打印一下,发现\n就能立马打印,而如果不加\n,就会发现不会立马打印。

只是不好观察。

我们可以借助sleep函数(秒)和usleep函数(微秒)观察!头文件:unistd.h

往显示器中打印时,首先进入缓冲区,然后程序结束再将缓冲区中的数据写入到显示器,所以\r不能立马显示出来,而\n能,因为\n是换行了,\n是行刷新。

显示器其实是字符设备,它只认识字符,我们写入的任何数据,在它看来都是字符!

我们写一个简单的倒计时:

加深理解 \r 和缓冲区:

printf的底层其实就是fprintf函数。

所以我们这样写:

我们来尝试写一个进度条:

  #define NUM 101    
  #define STYLE '#'    
  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;    
      usleep(50000);    
    }    
    printf("\n");   
}

解释:

以起始和结尾着手分析更好,循环过程不好分析,也不需要分析!

进阶---进度条:

我们在真正下载的时候,有一个下载多少mb,我们可以做一个非常拟合的进度条!

代码:

double total=1024.0;    
double speed=1.0;    
    
void Download()    
{    
  int current=0;    
  while(current<=total)    
  {    
    process_v2(total,current);    
    current+=speed;    
    usleep(3000);                                                                                                                           
  }    
  printf("\ndownload %.2f Done!\n",total);    
}
  #define NUM 101    
  #define STYLE '#'    
      
  void process_v2(double total,double current)    
  {    
    char buffer[NUM];    
    memset(buffer,0,sizeof(buffer));    
    const char* lable="|-\|/";    
    int len=strlen(lable);    
      
    int num=(int)(current*100/total);    
    int i=0;    
    for(i=0;i<num;i++)    
    {    
      buffer[i]=STYLE;    
    }    
    static int cnt=0;                                                                                                                       
    printf("[%-100s][%d%%][%c]\r",buffer,num,lable[cnt++]);    
    cnt%=len;    
    fflush(stdout);    
  }    

git的使用

我们以gitee为例。

安装git

yum install git

创建项目

使用

.git文件是隐藏的本地仓库,存放着所有的修改记录。

克隆

我们使用需要先将远程仓库克隆到本地:

git clone [刚刚复制的链接]

注意:第一次提价需要gitee的用户名和密码!

提交到暂存区

git add [路径文件]

提交到本地仓库

git commit -m "......"

注意:双引号中必须要填写内容。

但是我们commit必须要告诉git我们是以什么身份(什么名字),提交记录的,以便后续能找到是谁提交的这个代码!

我们可以试试这两句代码来告诉我们是谁:

git config --global user.email "you@example.com" (你的gitee邮箱地址)
git config --global user.name "Your Name"

验证是否成功:

注意:邮箱地址最后和gitee邮箱地址一样,名字可以不一样!

然后再commit一下,继续输入你的gitee用户名和密码即可,即可提交本地仓库成功!

这两句代码就是全局配置。运行完之后,家目录下自动会有.gitconfig文件(~/.gitconfig)

打开我们看到:

仓库配置:

git config  user.email "you@example.com" (你的gitee邮箱地址)
git config  user.name "Your Name"

我们来介绍两种配置的方法:(重点全局配置)

仓库配置:仅对当前仓库起效(只有当前仓库不需要密码!)

只需要配置仓库目录下的 .git/config 文件。

全局配置:对当前用户的所有仓库都起效!

只需要配置家目录下的.gitconfig文件。

我们主要介绍全局配置(提交到远程仓库也是讲全局配置)。

提交到远程仓库

提交需要gitee用户名和密码,我们可以配置免密码提交。

在家目录下,创建.git-credentials文件。

打开.git-credentials文件,输入:

https://{username}:{passward}@gitee.com

然后再执行命令:

git config --global credential.helper store

就会发现.gitconfig文件多了内容:

再重新执行push命令,即可成功!

指令:

git  status:查看当前提交状态

git log:查看提交日志

总结:

当本地仓库和远程仓库内容不统一时,我们去提交,就会冲突,所以我们需要git  pull  执行一下,将

本地仓库更新一下,保证远程仓库和本地仓库一致!

当然我们可以多次add和commit到本地仓库,最后一起提交到远程仓库!

gdb/cgdb

我们写了代码,肯定时不时的会发生错误,所以我们需要调试器----gdb/cgdb

下载:

yum install -y gdb
yum install -y cgdb

我们可以使用gdb,也能使用cgdb,一般使用cgdb,因为gdb在我们调试的时候,我们并不能看到我们的代码,比较难用,而cgdb能!

图:

一下我们都使用cgdb作为演示!

使用

预备

我们知道程序的发行有两种:debug模式和release模式,而Linux gcc/g++出来的二进制程序,默认时release模式。

注意:只有在debug模式下形成的可执行文件才能调试!

所以我们在形成可执行文件指令时,需要加上-g指令。

如:

gcc/g++ dst.c -o src -g

注意:gdb/cgdb 后面接形成后的可执行文件!而不是接源文件!

如:

gdb/cgdb src

实操

基本指令:

退出:ctrl+d 或 q 或 quit。

l +行号:显示源代码,每次列出10行,列举出来后,再按一次回车,可以看到全部代码。

如:

光标切屏:i 和 ESc 按键。

解释:

b 行数或 b dst.c:行数 :在某一行打断点。 

info b:查看断点信息。

disable 断点编号:禁用此断点。

enable 断点编号:启用此段点。

d 断点编号:删除此断点。

b 函数名:在函数开头打断点,相当于在这个函数的第一行打了断点。

r:程序开始跑起来,跳到第一个断点处。当在调试中,想重新开始一遍,也可以按下 r ,回到第一个断点处。

n:(next)代表逐过程,相当于 vs 2022中的F10按钮。比如可以跳过函数。

s:(step)代表逐语句,相当于vs2022中的F11按钮。

如果想跳过一个循环呢?逐过程和逐语句都跳不过循环。

until:执行到函数中,可直接跳出循环。

until 行数:执行到指定行。

c:(continue)执行到下一个断点。

finish:直接跳出函数。

p 变量名:(print)查看变量名内容。

info locals:看当前函数的所有临时变量内容。

watch 变量名:执⾏时监视⼀个表达式(如变量)的值。如果监视的表达式在程序运⾏期间的值发⽣变化,GDB会暂 停程序的执⾏,并通知使⽤者。

set var 表达式:临时修改变量值,一般用来确定问题原因。

条件断点

b 行号 if  变量名==值:只有i等于某个值时,才触发这个断点,且只触发一次。

condition 断点编号 变量名==值:给某个断点新增条件。

这里不过多展示。

反汇编:

objdump -S src > src.s

将可执行文件反汇编成.s文件。

好了,我们下期见。