Linux——基础开发工具(下)

💁‍♂️个人主页:进击的荆棘

👇作者其它专栏:

《数据结构与算法》《算法》《C++起始之路》《Linux》


目录

1.软件包管理器

2.编辑器Vim

3.编译器gcc/g++

4.自动化构建--make/Makefile

5.Linux 第一个系统程序--进度条

6.版本控制器Git

7.调试器-gdb/cgdb使用


5.Linux 第一个系统程序--进度条

5.1回车与换行

●回车概念(\r):把字车推回本行最左侧起始位置,只负责光标回到行首,不向下换行

●换行概念(\n):光标垂直下移一行,水平位置保持不变,不会回到行开头

5.2行缓冲区

代码

cpp 复制代码
#include <stdio.h>
     
int main(){
  printf("hello world\n");
  sleep(3);                                                                                                                                                                                                                    
  return 0;
}

现象:先输出hello world并换行后,等待3秒后程序结束

代码

cpp 复制代码
#include <stdio.h>
     
int main(){
  printf("hello world");
  sleep(3);                                                                                                                                                                                                                    
  return 0;
}

现象:等待3秒后输出hello world且不换行

代码

cpp 复制代码
#include <stdio.h>
     
int main(){
  printf("hello world");
  fflush(stdout);
  sleep(3);                                                                                                                                                                                                                    
  return 0;
}

现象:先输出hello world,3秒后程序结束且不换行

5.3倒计时程序

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

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

5.4进度条代码

process.c

cpp 复制代码
#include "process.h"
#include <string.h>
#include <unistd.h>
     
#define NUM 101
#define STYLE '='
     
void FlushProcess(double total,double current){
      char buffer[NUM];
      memset(buffer,0,sizeof(buffer));
      char* label="|/-\\";
      int len=strlen(label);
    
      static int cnt=0;
    
      //填充=
      int num=(int)(current*100/total);
      int i;
      for(i=0;i<num;i++){
      buffer[i]=STYLE;
   }
   
   double rate=current/total;
   cnt%=len;
   printf("[%-100s][%.1lf\%][%c]\r",buffer,rate*100,label[cnt]);
   cnt++;                                                                                                                                                                                                                       
   fflush(stdout);
};
  
//void process_v1(){
//  char buffer[NUM];
//  memset(buffer,0,sizeof(buffer));
//  char* label="|/-\\";
//  int cnt=0;
//  int len=strlen(label);
//  while(cnt<=100){
//    //在原地输出
//    printf("[%-100s][%d\%][%c]\r",buffer,cnt,label[cnt%len]);
//    buffer[cnt]=STYLE;
//    fflush(stdout);
//
//    usleep(50000);
//    cnt++;
//  }
//  printf("\n");
//};

process.h

cpp 复制代码
#pragma once
#include <stdio.h>                                                                                                                                                                                                               
  
void process_v1();
   
void FlushProcess(double total,double current);

main.c

cpp 复制代码
#include "process.h"
#include <stdio.h>
     
#define total 1024.0
#define speed 1.0
     
void Download(){
   double current=0;
   while(current<=total){
      FlushProcess(total,current);
      //模拟下载数据
      usleep(3000);                                                                                                                                                                                                              
      current+=speed;
   }
   printf("\nDownload %.2lfMB Done\n",current);
}
   
int main(){
  // process_v1();
  Download();
  return 0;
}

Makefile

cpp 复制代码
BIN=processbar
SRC=$(wildcard *.c)
OBJ=$(SRC:.c=.o)
   
$(BIN):$(OBJ)
   gcc -o $@ $^
%.o:%.c
   gcc -c $<
   
.PHONY:clean                                                                                                                                                                                                                     
clean:
   rm -f *.o processbar

6.版本控制器Git

在我们编写各种文档时,为防止文档丢失,更改失误,为了失误后能恢复到原来的版本,不得不复制出一个副本,比如:

"报告-v1"

"报告-v2"

"报告-v3"

"报告-确定版"

"报告-最终版"

......

每个版本有各自的内容,但最终只有一份报告需要被我们使用。但在此之前的工作都需要这些不同版本的报告,于是每次都是复制粘贴副本,产出的文件就越来越多,文件多不是问题,问题是:随着版本数量的不断增多,我们很难记得各版本都修改了什么。

文档如此,写项目代码时也是如此。

6.1版本控制器

为了能够更方便我们管理这些不同版本的文件,便有了版本控制器。版本控制器就是能让我们了解到一个文件的历史,以及它的发展过程的系统。通俗的讲就是一个可以记录工程的每一次改动和版本迭代的管理系统,同时也方便多人协作。

目前最主流的版本控制器就是Git。Git可以控制电脑上所有格式的文件,例如doc、excel、dwg、dgn、rvt等等。对于开发人员来说,Git最重要的就是可以帮助我们管理软件开发项目中的原代码文件。

6.2git简史

同生活中的许多伟大事物一样,Git诞生于一个极具创新的时代。

Linux内核开源项目有着为数众多的参与者。绝大多数的Linux内核维护工作都花在了提交补丁和保存归档的繁琐事务上(1991-2002)。到2002年,整个项目组开始启用一个专有的分布式版本控制系统BitKeeper来管理和维护代码。

到了2005年,开发BitKeeper的商业公司同Linux内核开源社区的合作关系结束,他们回收了Linux内核社区免费使用BitKeeper的权力。这就迫使Linux开源社区(特别是Linux的创造者Linus Torvalds)基于使用BitKeeper时的经验教训,开发处自己的版本系统。他们对新的系统制订了若干目标:

●速度

●简单的设计

●对非线性开发模式的强力支持(允许成千上万个并行开发的分支)

●完全分布式

●有能力高效管理类似Linux内核一样大的超大规模项目(速度和数据量)

自诞生于2005年以来,Git日臻成熟,在高度易用的同时,仍保留着初期设定的目标。它的速度飞快,极其适合管理大项目,有着令人难以置信的非线性分支管理系统。

6.3安装git

yum install git

sjx@VM-0-6-centos \~$ git --version

git version 1.8.3.1

6.4在Github创建项目

注册账户

参考官网提示即可,需要进行邮箱验证。

创建项目

1.登录成功后,进入个人主页,点击右上角+号里的New repository按钮新建项目。

2.然后跳转到的新页面中输入项目名称(注意,名称不能重复,系统会自动校验,校验过程可能会花费几分钟)。校验完毕后,点击下方的Create repository按钮确认创建。

3.在创建好的项目页面中复制项目的链接,以备接下来进行下载。

下载项目到本地

创建好一个放置代码的目录

git clone url

这个url就是刚刚建立好的项目的链接

6.5三板斧

1.git add

讲代码放到刚才下载好的目录中

git add 文件名

将需要用git管理的文件告知git

注意:git add .表示将当前目录所有新增/修改文件加入暂存区

2.git commit

提交改动到本地

git commit -m "XXX"

最后的"XXX"表示改动的备注信息,提交的时候应注明提交日志,描述改动的详细内容

3.git push

同步到远端服务器上

git push

需要填入用户名密码。同步成功后,刷新Github页面就能看到代码改动了。

配置免密码提交

git本地免密码和账号pull、push_没有git账号怎么拉代码-CSDN博客

4.其它

●git log/status/pull

●.ignore

●首次使用问题

7.调试器-gdb/cgdb使用

7.1样例代码

cpp 复制代码
  1 #include <stdio.h>
  2 
  3 int sum(int s,int e){
  4   int result=0;
  5   int i=0;
  6   for(i=s;i<=e;i++){
  7     result+=i;
  8   }
  9   return result;
 10 }
 11 int main(){
 12   int start=1;
 13   int end=100;
 14   printf("I will begin\n");
 15   int n=sum(start,end);
 16   printf("running done,result is:[%d-%d]=%d\n",start,end,n);                                                                                                                                                                     
 17   return 0;
 18 }

7.2预备知识

●程序的发布方式有两种,debug模式和release模式,Linux gcc/g++出来的二进制程序,默认是release模式

●要使用gdb调试,必须在源代码生成二进制程序的时候,加上-g选项,若没有添加,程序无法被编译

cpp 复制代码
//默认模式,不支持调试
[sjx@VM-0-6-centos ~]$ gcc myproc.c -o myproc
[sjx@VM-0-6-centos ~]$ file myproc
myproc: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=1820d336c26f5af9a1c39d185c1b7e9fd8b8775d, not stripped

//debug模式
[sjx@VM-0-6-centos ~]$ gcc myproc.c -o myproc -g
[sjx@VM-0-6-centos ~]$ file myproc
myproc: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=86156357b20aafbe138879a46b580f5f4a82e9ef, not stripped

7.3常见使用

●开始:gdb binfile(文件的可执行程序)

●退出ctrl +d或quit调试命令

|-----------------------------------|-----------------------|-----------------------------------|
| 命令 | 作用 | 样例 |
| list/l | 显示源代码,从上次位置开始,每次列出10行 | list/l 10 |
| list/l 函数名 | 列出指定函数的源代码 | list/l main |
| list/l 文件名:行号 | 列出指定文件的源代码 | list/l myproc.c:1 |
| r/run | 从程序开始连续执行 | r/run |
| n/next | 单步执行,不进入函数内部,逐过程 | n/next |
| s/step | 单步执行,进入函数内部,逐语句F11 | s/step |
| break/b 文件名:行号 | 在指定行号设置断点 | break 10 break test.c :10 |
| break/b 函数名 | 在函数开头设置断点 | break main |
| info break/b | 查看当前所有断点的信息 | info break |
| finish | 执行到当前函数返回,然后停止 | finish |
| print/p 表达式 | 打印指定变量的只 | print/p start+end |
| p/print 变量 | 打印指定变量的值 | p/print x |
| set var 变量=值 | 修改变量的值 | set var i=10 |
| continue/c | 从当前位置开始连续执行程序 | continue/c |
| delete/d breakpoints | 删除所有断点 | delete breakpoints |
| delete/d breakpoints n delete/d n | 删除序号为n的断点 | delete/d breakpoints 1 delete/d 1 |
| disable breakpoints | 禁用所有断点 | disable breakpoints |
| enable breakpoints | 启用所有断点 | enable breakpoints |
| info/i breakpoints info/i b | 查看当前设置的断点列表 | info breakpoints info b |
| display 变量名 | 跟踪显示指定变量的值(每次停止时) | display x |
| undisplay 编号 | 取消对指定编号的变量的跟着显示 | undisplay 1 |
| until X行号 | 执行到指定行号 | until 20 |
| backtrace/bt | 查看当前执行栈的各级函数调用及参数 | backtrace/bt |
| info/i locals | 查看当前栈帧的局部变量值 | info locals |
| quit | 退出GDB调试器 | quit |

7.4cgdb

安装cgdb:

●上面的基本调试还是麻烦,虽然是黑屏,但还是想看到代码调试

●Ubuntu:sudo apt-get install -y cgdb

●Centos:sudo yum install -y cgdb

7.4.1watch

执行时监视一个表达式(如变量)的值。若监视的表达式在程序运行期间的值发生变化,GDB会暂停程序的执行,并通知使用者。

复制代码
(gdb) l main
11
12 return result;
13 }
14
15 int main()
16 {
17 int start = 1;
18 int end = 100;
19 printf("I will begin\n");
20 int n = Sum(start, end);
(gdb) b 20
Breakpoint 1 at 0x11c3: file mycmd.c, line 20.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000000011c3 in main at mycmd.c:20
(gdb) r
Starting program: /home/whb/test/test/mycmd
I will begin
Breakpoint 1, main () at mycmd.c:20
20 int n = Sum(start, end);
(gdb) s
Sum (s=32767, e=-7136) at mycmd.c:5
5 {
(gdb) n
6 int result = 0;
(gdb) watch result                /////////////////
Hardware watchpoint 2: result
(gdb) c
Continuing.

Hardware watchpoint 2: result
Old value = -6896
New value = 0
Sum (s=1, e=100) at mycmd.c:7
7 for(int i = s; i <= e; i++)
(gdb) c
Continuing.
Hardware watchpoint 2: result
Old value = 0
New value = 1
Sum (s=1, e=100) at mycmd.c:7
7 for(int i = s; i <= e; i++)
(gdb) c
Continuing.
Hardware watchpoint 2: result
Old value = 1
New value = 3
Sum (s=1, e=100) at mycmd.c:7
7 for(int i = s; i <= e; i++)
(gdb) c
Continuing.
Hardware watchpoint 2: result
Old value = 3
New value = 6
Sum (s=1, e=100) at mycmd.c:7
7 for(int i = s; i <= e; i++)
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551c3 in main at mycmd.c:20
breakpoint already hit 1 time
2 hw watchpoint keep y result
breakpoint already hit 4 times
(gdb) d 2
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551c3 in main at mycmd.c:20
breakpoint already hit 1 time
(gdb) finish
Run till exit from #0 Sum (s=1, e=100) at mycmd.c:7

0x00005555555551d2 in main () at mycmd.c:20
20 int n = Sum(start, end);
Value returned is $1 = 5050

注意:

●若有一些变量不应该修改,但是我们怀疑它修改导致了问题,可以watch它,若变化了,就会通知我们

●监视变量的变化

7.4.2 set var确定问题原因

更改标志位,假设我们想得到+-result

复制代码
/ mycmd.c
#include <stdio.h>
int flag = 0; // 故意错误
//int flag = -1;
//int flag = 1;
int Sum(int s, int e)
{
int result = 0;
for(int i = s; i <= e; i++)
{
result += i;
}
return result*flag;
}
int main()
{
int start = 1;
int end = 100;
printf("I will begin\n");
int n = Sum(start, end);
printf("running done, result is: [%d-%d]=%d\n", start, end, n);
return 0;
}

(gdb) l main
15
16 return result*flag;
17 }
18
19 int main()
20 {
21 int start = 1;
22 int end = 100;
23 printf("I will begin\n");
24 int n = Sum(start, end);
(gdb) b 24
Breakpoint 1 at 0x11ca: file mycmd.c, line 24.
(gdb) r
Starting program: /home/whb/test/test/mycmd
I will begin
Breakpoint 1, main () at mycmd.c:24
24 int n = Sum(start, end);
(gdb) n
25 printf("running done, result is: [%d-%d]=%d\n", start, end,
n);
(gdb) n
running done, result is: [1-100]=0 # 这⾥结果为什么是0?
26 return 0;
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/whb/test/test/mycmd
I will begin
Breakpoint 1, main () at mycmd.c:24
24 int n = Sum(start, end);
(gdb) s
Sum (s=32767, e=-7136) at mycmd.c:9
9 {
(gdb) n
10 int result = 0;
(gdb) n
11 for(int i = s; i <= e; i++)
(gdb)
13 result += i;
(gdb)
11 for(int i = s; i <= e; i++)
(gdb)
13 result += i;
(gdb) until 14
Sum (s=1, e=100) at mycmd.c:16
16 return result*flag;
(gdb) p result
$1 = 5050
(gdb) p flag
$2 = 0
(gdb) set var flag=1 # 更改flag的值,确认是否是它的原因
(gdb) p flag
$3 = 1
(gdb) n
17 }
(gdb) n
main () at mycmd.c:25
25 printf("running done, result is: [%d-%d]=%d\n", start, end,
n);
(gdb) n
running done, result is: [1-100]=5050 # 是它的原因
26 return 0;

7.4.3条件断点

7.4.3.1添加条件断点
复制代码
(gdb) l main
11
12 return result;
13 }
14
15 int main()
16 {
17 int start = 1;
18 int end = 100;
19 printf("I will begin\n");
20 int n = Sum(start, end);
(gdb) b 20
Breakpoint 1 at 0x11c3: file mycmd.c, line 20.
(gdb) r
Starting program: /home/whb/test/test/mycmd
I will begin
Breakpoint 1, main () at mycmd.c:20
20 int n = Sum(start, end);
(gdb) s
Sum (s=32767, e=-7136) at mycmd.c:5
5 {
(gdb) n
6 int result = 0;
(gdb) n
7 for(int i = s; i <= e; i++)
(gdb) n
9 result += i;
(gdb) display i
1: i = 1
(gdb) n
7 for(int i = s; i <= e; i++)
1: i = 1
(gdb) n
9 result += i;
1: i = 2
(gdb) n
7 for(int i = s; i <= e; i++)
1: i = 2
(gdb) n
9 result += i;
1: i = 3
(gdb)
7 for(int i = s; i <= e; i++)
1: i = 3
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551c3 in main at mycmd.c:20
breakpoint already hit 1 time
(gdb) b 9 if i == 30 # 9是⾏号,表⽰新增断点的位置        ///////////////////////
Breakpoint 2 at 0x555555555186: file mycmd.c, line 9.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551c3 in main at mycmd.c:20
breakpoint already hit 1 time
2 breakpoint keep y 0x0000555555555186 in Sum at mycmd.c:9
stop only if i == 30
(gdb) finish
Run till exit from #0 Sum (s=1, e=100) at mycmd.c:7
Breakpoint 2, Sum (s=1, e=100) at mycmd.c:9
9 result += i;
1: i = 30
(gdb) finish
Run till exit from #0 Sum (s=1, e=100) at mycmd.c:9
0x00005555555551d2 in main () at mycmd.c:20
20 int n = Sum(start, end);
Value returned is $1 = 5050
7.4.3.2给已经存在的断点新增条件
复制代码
(gdb) l main
11
12 return result;
13 }
14
15 int main()
16 {
17 int start = 1;
18 int end = 100;
19 printf("I will begin\n");
20 int n = Sum(start, end);
(gdb) b 20
Breakpoint 1 at 0x11c3: file mycmd.c, line 20.
(gdb) r
Starting program: /home/whb/test/test/mycmd
I will begin
Breakpoint 1, main () at mycmd.c:20
20 int n = Sum(start, end);
(gdb) s
Sum (s=32767, e=-7136) at mycmd.c:5
5 {
(gdb) n
6 int result = 0;
(gdb) n
7 for(int i = s; i <= e; i++)
(gdb) n
9 result += i;
(gdb)
7 for(int i = s; i <= e; i++)
(gdb)
9 result += i;
(gdb)
7 for(int i = s; i <= e; i++)
(gdb)
9 result += i;
(gdb)
7 for(int i = s; i <= e; i++)
(gdb) b 9 # 我们在第9⾏新增⼀个断点,⽤来开始测试
Breakpoint 2 at 0x555555555186: file mycmd.c, line 9.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551c3 in main at mycmd.c:20
        breakpoint already hit 1 time
2 breakpoint keep y 0x0000555555555186 in Sum at mycmd.c:9
(gdb) n
Breakpoint 2, Sum (s=1, e=100) at mycmd.c:9
9 result += i;
(gdb) n
7 for(int i = s; i <= e; i++)
(gdb) n
Breakpoint 2, Sum (s=1, e=100) at mycmd.c:9
9 result += i;
(gdb) condition 2 i==30 #给2号断点,新增条件i==30    //////////////////
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551c3 in main at mycmd.c:20
breakpoint already hit 1 time
2 breakpoint keep y 0x0000555555555186 in Sum at mycmd.c:9
stop only if i==30
breakpoint already hit 2 times
(gdb) n
7 for(int i = s; i <= e; i++)
(gdb) n
9 result += i;
(gdb) c
Continuing.
Breakpoint 2, Sum (s=1, e=100) at mycmd.c:9
9 result += i;
(gdb) p i
$1 = 30
(gdb) p result
$2 = 435

注意:

●条件断点添加常见两种方式:1.新增 2.给已有断点追加

●两者语法有区别

●新增:b 行号/文件名:行号/函数名 if i == 30(条件)

●给已有断点追加:condition 2 i == 30,其中2是已有断点编号,没有if

●cgdb分屏操作ESC进入代码屏,i回到gdb屏

相关推荐
律宏阔2 小时前
WSL Docker 端口明明空闲,但就是绑不上端口
linux·windows
律宏阔2 小时前
WSL 突然断网,无法 ping 内网或外网
linux·windows
穷人小水滴2 小时前
用容器编译 VirtualBox 虚拟机软件 (ArchLinux, podman)
linux·容器·virtualbox
乱码三千3 小时前
如何优雅地直连无公网 IP 的远程 GPU 服务器
linux·人工智能·程序员
yunwei373 小时前
使用 eBPF 跟踪 Nginx 请求
linux·后端·性能优化
yunwei373 小时前
使用 eBPF 跟踪 MySQL 查询
linux·后端·性能优化
yunwei373 小时前
eBPF 示例教程:使用 XDP 捕获 TCP 信息
linux·后端·性能优化
judezh3 小时前
api 容器一直 unhealthy?我把一个 Agent 运行时的健康检查逐条拆了,查出四个问题
运维·docker
GeW3 小时前
制造业高端化,为什么必须重估Linux和数据库?
linux
百万蹄蹄向前冲3 小时前
双端同步!云服务器装最新Node.js v26.10全过程追踪
服务器·人工智能·node.js