【Linux】Linux的基础工具
目录
1. 整体学习思维导图
2. 安装工具yum/apt
2.1 安装工具是什么?
2.2 理解安装的过程
3. Vim 编辑器的使用
3.1 vim的多模式
3.2 命令模式:快速编辑
3.3 替换模式
3.4 底行模式
3.5 视图模式
3.6 小技巧
4. gcc/g++
5. 理解编译和库的基础概念
5.1 理解条件编译
5.1.1 命令行宏定义
5.1.2 条件编译的用途
5.1.3 汇编的由来
5.2 动静态库
5.2.1 库的命名方式
5.2.2 动静态库的对比
5.2.3 ldd/file/-static指令
尝试链接
6. make/makefile是什么?
6.1 依赖关系,依赖方法
6.2 推导
6.3 进度条
6.3.1 先做一个倒计时程序
6.3.2 做一个进度条程序
6.4 makefile的使用拓展
7. Git版本控制器
7.1 git的安装
7.2 创建远端仓库
7.3 git三板斧
7.3.1 git add
7.3.2 git commit
7.3.3 git push
7.4 补充知识
8. gdb调试器
8.1 debug模式
8.2 cgdb调试指令
1. 整体学习思维导图
2. 安装工具yum/apt
2.1 安装工具是什么?
打个比方来说,我们的手机在下载一款我们需要的APP时,我们一般会去应用商店安装,而这个应用商店就可以称作为一个安装工具。
在Linux中的安装分为多种:
-
第一种:源码安装,顾名思义就是给我们源码自己运行安装,这无疑是很难得,我们并不了解他人的程序,会让我们拿到该软件时还需花费一定时间去了解。
-
第二种:软件包安装rpm,这个软件包不是指我们平时手机下载的安装包,手机的安装包是一个集合体,简单说一款软件是由多种程序所构成的,包含了很多的库调用,而这里的软件安装包只是软件并不包含库,这意味着我们在安装完这个安装包后依然会因为缺少一些库的支持而不能运行。
-
第三种:包管理器 yum(Centos)/apt/apt-get(ubuntu),包管理器就如同一个应用商店,它将安装包和所需库都准备好了,我们使用它下载必定不会出现缺少库文件运行不起来的情况(我想软件开发者也不会让这种情况发生)。
2.2 理解安装的过程
以下是安装指令:
yum [选项] install(安装)/remove(移除) name(软件名称)
-y 安装时不询问是否同意
yum list 列出所有可以安装的程序包
通过以上选项我们又产生了新的问题:
-
为什么yum 只需要知道软件名即可下载,yum又是去哪里寻找下载的链接?
首先我们要知道评估一个操作系统的好坏就是其生态如何:
生态
-
社区
-
文档
-
人群
-
问题
-
配套的软件
所以我们的yum能通过软件名就可以安装是因为拥有系统开发者内置的链接查找的,但是我们有知道这些链接下载都在国外,通过我们国内的一些镜像所提供的链接我们才可以使用的。
/etc/yum.repos.d/ yum源配置文件
3. Vim 编辑器的使用
3.1 vim的多模式
3.2 命令模式:快速编辑
-
定位到开始:gg
-
定位到结尾:n + shift + g = G, n可以指定定位到该行。
-
一行定位到开始: ^ ; 一行定位到结尾:$
-
n + (h,j,k,l) 分别对应上下左右
-
n + (w,b) w:以单词为单位,向右移动;b:以单词为单位,向左移动
-
n + yy : 复制,n是复制的行数
-
n + p : 粘贴到当前行,n是粘贴的行数
-
u:撤销 ctrl + r:撤销u的操作
-
n + dd:剪切/删除当前行,n代表数量
-
n + x:删除光标所在位置字符
-
n + shift + x:光标右侧不动,左侧删除
-
shift + ~:大小写切换
-
n + r :替换光标所在字符,r->目标字符
-
shift + 3: 选中单词,n:逆向查找
3.3 替换模式
-
shift + r:批量替换
3.4 底行模式
w/q/!
多窗口模式:vs (另一个文件) , ctrl + ww切换窗口
底行替换
%s/str1/str2/g
如果想要将文件中所有
nihao 替换为 hello 则命令为: %s/nihao/hello/g
3.5 视图模式
Ctrl + v : 进入视图模式
进入视图模式,shift+i进入插入模式再次Esc就可以实现批量化批注
3.6 小技巧
/* 进入目标行数 */
vim code.c +n /* n代表行数 */
4. gcc/g++
gcc编译器可以编译C语言和cpp代码,g++只能编译cpp代码
gcc file.c/file.cpp -o filename
我们将拿以下文件作为测试内容:
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_3]$ gcc hello.c -o hello
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_3]$ ll
total 16
-rwxrwxr-x 1 ouyang ouyang 8440 Dec 7 09:26 hello
-rw-rw-r-- 1 ouyang ouyang 228 Dec 7 09:25 hello.c
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_3]$ ./hello
Hello Linux!
Hello Linux!
Hello Linux!
Hello Linux!
Hello Linux!
Hello Linux!
-
预处理(-E)
E选项是进行程序翻译,在预处理完停下,处理文件的后缀为.i
头文件展开,去注释,宏替换,条件编译。
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_3]$ gcc -E hello.c -o hello.i
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_3]$ ll
total 24
-rw-rw-r-- 1 ouyang ouyang 228 Dec 7 09:25 hello.c
-rw-rw-r-- 1 ouyang ouyang 17029 Dec 7 09:31 hello.i
-
编译(-S)
S选项是进行程序翻译,在编译完停下,处理完的后缀为.s
生成汇编代码
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_3]$ gcc -S hello.c -o hello.s
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_3]$ ll
total 28
-rw-rw-r-- 1 ouyang ouyang 228 Dec 7 09:25 hello.c
-rw-rw-r-- 1 ouyang ouyang 17029 Dec 7 09:33 hello.i
-rw-rw-r-- 1 ouyang ouyang 594 Dec 7 09:33 hello.s
-
汇编(-c)
c(小写)选项是进行程序翻译,在生成机器可识别代码时停下,处理完的后缀为.o
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_3]$ gcc -S hello.c -o hello.o
-
链接(-o)
将目标文件.o链接起来
总结:
选项:-ESc / 后缀名:-iso
5. 理解编译和库的基础概念
5.1 理解条件编译
我们看以下代码:
#include <stdio.h>
int main()
{
#ifndef M
printf("no have M\n");
#else
printf("have M = %d\n", M);
#endif
return 0;
}
/* 无疑以上代码输出的是"no have M\n" */
5.1.1 命令行宏定义
-D__ 选项D,__定义宏
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_3]$ gcc Text.c -o Text -DM=20
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_3]$ ./Text
have M = 20
预处理--->修改编辑我们的代码
5.1.2 条件编译的用途
-
区分社区版/专业版,进行代码的动态裁剪,只需要维护一份代码。
-
内核源代码,对于不同的系统如Linux和Windows,我们都需要支持,都实现,但是在不同的系统执行不同的代码。
-
开发工具,应用软件。我们是否开会员等等情景
5.1.3 汇编的由来
一开始机器只识别二进制语言,为了方便发明了汇编语言,但是需要一个编译器把汇编语言翻译成机器语言,那么这个这个编译器先由机器语言实现,然后使用汇编语言实现,后续的语言只需要完成从当前语言到汇编语言的翻译即可。
5.2 动静态库
库:一套方法或者数据集,保证基本的接口,功能,加速我们二次开发(如我们是砍树工,那么我们需要买一个砍树机器直接去砍树即可,而不是自己造一个砍树机器再去砍树)。
5.2.1 库的命名方式
libc.so 动
libc.a 静
命名规则-> lib (name) .so/.a
动态库:多人共享,一旦丢失,会导致所有程序无法执行!
静态库:拷贝到本地,一旦链接后不再害怕静态库丢失!
5.2.2 动静态库的对比
-
动态库不用拷贝本地,生成的可执行程序体积小。静态库需要拷贝本地,生成的可执行程序体积大。
-
可执行程序对动态库的依赖性大。对静态库的依赖性小。
-
程序运行,加载到内存,若是静态链接,会产生大量重复的代码。
-
动态链接,节省资源(内存和磁盘)
5.2.3 ldd/file/-static指令
/* ldd 查看文件是否为动态链接 */
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_3]$ ldd Text
linux-vdso.so.1 => (0x00007ffee35e8000)
libc.so.6 => /lib64/libc.so.6 (0x00007ff540889000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff540c56000)
/* file 查看文件状态 */
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_3]$ file Text
Text: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),
dynamically linked (uses shared libs),
for GNU/Linux 2.6.32, BuildID[sha1]=3abaa3cc07014cd9ddd60b022a633e82956b6cc0,
not stripped
/* -static 在链接时执行静态链接*/
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_3]$ gcc Text.c -o Text_static -static
-rwxrwxr-x 1 ouyang ouyang 8440 Dec 8 09:38 Text
-rw-rw-r-- 1 ouyang ouyang 131 Dec 8 09:37 Text.c
-rwxrwxr-x 1 ouyang ouyang 861336 Dec 8 10:18 Text_static
我们可以看到动态链接和静态链接之间的文件大小差异很大
尝试链接
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_3]$ gcc fun1.o fun2.o main.o -o main
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_3]$ ll
total 48
-rw-rw-r-- 1 ouyang ouyang 75 Dec 8 10:29 fun1.c
-rw-rw-r-- 1 ouyang ouyang 27 Dec 8 10:29 fun1.h
-rw-rw-r-- 1 ouyang ouyang 1496 Dec 8 10:31 fun1.o
-rw-rw-r-- 1 ouyang ouyang 75 Dec 8 10:29 fun2.c
-rw-rw-r-- 1 ouyang ouyang 27 Dec 8 10:29 fun2.h
-rw-rw-r-- 1 ouyang ouyang 1496 Dec 8 10:32 fun2.o
drwxrwxr-x 2 ouyang ouyang 4096 Dec 8 10:21 lib
-rwxrwxr-x 1 ouyang ouyang 8600 Dec 8 10:32 main
-rw-rw-r-- 1 ouyang ouyang 90 Dec 8 10:29 main.c
-rw-rw-r-- 1 ouyang ouyang 1424 Dec 8 10:32 main.o
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_3]$ ./main
Fun1()Fun2()
链接的本质:把所有的.o文件链接起来
6. make/makefile是什么?
-
make是命令
-
makefile是一个文件
见一见:
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_3]$ ll
total 12
-rw-rw-r-- 1 ouyang ouyang 135 Dec 8 10:39 code.c
drwxrwxr-x 2 ouyang ouyang 4096 Dec 8 10:21 lib
-rw-rw-r-- 1 ouyang ouyang 32 Dec 8 10:40 makefile
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_3]$ cat code.c makefile
#include <stdio.h>
int main()
{
printf("Hello Linux\n");
printf("Hello Linux\n");
printf("Hello Linux\n");
return 0;
}
code:code.c
gcc code.c -o code
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_3]$ make
gcc code.c -o code
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_3]$ ll
total 24
-rwxrwxr-x 1 ouyang ouyang 8480 Dec 8 10:40 code
-rw-rw-r-- 1 ouyang ouyang 135 Dec 8 10:39 code.c
drwxrwxr-x 2 ouyang ouyang 4096 Dec 8 10:21 lib
-rw-rw-r-- 1 ouyang ouyang 32 Dec 8 10:40 makefile
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_3]$ ./code
Hello Linux
Hello Linux
Hello Linux
6.1 依赖关系,依赖方法
基础应用:
code:code.c
gcc code.c -o code
.PHONY:clean
clean:
rm -f code
make
make clean
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_8]$ make
gcc code.c -o code
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_8]$ ll
total 20
-rwxrwxr-x 1 ouyang ouyang 8480 Dec 11 15:56 code
-rw-rw-r-- 1 ouyang ouyang 136 Dec 11 15:45 code.c
-rw-rw-r-- 1 ouyang ouyang 64 Dec 11 15:54 makefile
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_8]$ make clean
rm -f code
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_8]$ ll
total 8
-rw-rw-r-- 1 ouyang ouyang 136 Dec 11 15:45 code.c
-rw-rw-r-- 1 ouyang ouyang 64 Dec 11 15:54 makefile
-
⼀般我们这种clean的⽬标⽂件,我们将它设置为伪⽬标,用.PHONY 修饰,伪目标的特性是,总是被执行的。
什么叫做总是被执行?
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_8]$ stat code.c
File: ‘code.c’
Size: 136 Blocks: 8 IO Block: 4096 regular file
Device: fd01h/64769d Inode: 1057217 Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 1001/ ouyang) Gid: ( 1001/ ouyang)
Access: 2024-12-11 15:47:54.587450054 +0800
Modify: 2024-12-11 15:45:44.102708535 +0800
Change: 2024-12-11 15:45:44.102708535 +0800
Birth: -
⽂件 = 内容 + 属性
Modify: 内容变更,时间更新
Change:属性变更,时间更新
Access:常指的是⽂件最近⼀次被访问的时间。
在Linux的早期版本中,每当⽂件被访问时,其atime都会更新。但这种机制会导致⼤量的IO操作。
具体更新原则,不做过多解释。
.PHONY:让make忽略源⽂件和可执⾏⽬标⽂件的M时间对比,而忽略这种时间的对比就是叫做总是被执行!
6.2 推导
code:code.o
gcc code.o -o code
code.o:code.s
gcc -c code.s -o code.o
code.s:code.i
gcc -S code.i -o code.s
code.i:code.c
gcc -E code.c -o code.i
.PHONY:clean
clean:
rm -f *.i *.o *.s code
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_8]$ make
gcc -E code.c -o code.i
gcc -S code.i -o code.s
gcc -c code.s -o code.o
gcc code.o -o code
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_8]$ ls
code code.c code.i code.o code.s makefile
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_8]$ ./code
Hello Linux
Hello Linux
Hello Linux
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_8]$ make clean
rm -f *.i *.o *.s code
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2024_12_8]$ ls
code.c makefile
make的推导如图栈一般,把不存在的依赖关系的依赖方法存放在栈中,直到从栈顶进行推导,如果在中间的推导过程中,有些依赖关系和依赖方法不存在就停止。
6.3 进度条
-
\r 回车 \n 回车换行
-
缓冲区问题
-
我们默认打开三个文件stdin,stdout,stderr,我们使用键盘输入到缓冲区,直到缓冲区刷新屏幕才显示内容。
-
\n 行刷新
-
程序退出,缓冲区刷新
-
6.3.1 先做一个倒计时程序
#include <stdio.h>
#include <unistd.h>
int main()
{
int cnt = 10;
while(cnt>=0)
{
printf("%-2d\r", cnt);
fflush(stdout);
--cnt;
sleep(1);
}
printf("\n");
return 0;
}
6.3.2 做一个进度条程序
process.h
#pragma once
void Downprocess(double TotalSize, double DownSpeed, double tmpDownSpeed);
process.c
#include "process.h"
#include <string.h>
#include <stdio.h>
#define NUM 101
#define SIGN '='
void Downprocess(double TotalSize, double DownSpeed, double tmpDownSpeed)
{
char buffer[NUM];
memset(buffer, 0, sizeof(buffer));
const char* postsign = "|\\-/";
int len = strlen(postsign);
int cnt = (int)(DownSpeed*100/TotalSize);
int i = 0;
for(; i <= cnt; ++i)
buffer[i] = SIGN;
printf("[%-100s][%-2d%%][%c][当前下载速度:%-.2f]\r", buffer, cnt+1, postsign[cnt%len], tmpDownSpeed);
fflush(stdout);
}
main.c
#include "process.h"
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#define SIZE 1024.0
void Text()
{
double TotalSize = SIZE;
double DownSpeed = (double)(rand()%10 + 1);
double tmpDownSpeed = 0.0;
while(DownSpeed <= TotalSize)
{
/* 下载程序*/
tmpDownSpeed = (double)(rand()%10 + 1);
Downprocess(TotalSize, DownSpeed, tmpDownSpeed);
DownSpeed += tmpDownSpeed;
usleep(50000);
}
printf("\nDownLoad Complete!\n");
}
int main()
{
srand(time(NULL));
Text();
return 0;
}
6.4 makefile的使用拓展
SRC=$(shell ls *.c) # SRC是一个命名,$(shell ls *.c)表示找出当前所有.c后缀的文件
OBJ=$(SRC:.c=.o) # $(SRC:.c=.o) SRC中所有的.c文件替换成.o
BIN=process.exe
CC=gcc
OFLAG=-o
CFLAG=-c
RM=rm -f
$(BIN):$(OBJ)
@$(CC) $(OFLAG) $@ $^ # $@ 表示BIN $^ 表示OBJ
# @符号表示在执行命令时不显示命令本身,只显示命令的输出结果。
@echo "linking $^ to $@"
%.o:%.c
@$(CC) $(CFLAG) $< # $< 表示将所有的.c文件转变为.o
@echo "linking $< to $@"
.PHONY:clean
clean:
$(RM) $(BIN) $(OBJ)
7. Git版本控制器
举例一个场景:
我们在大学生活中,有些科目的老师会要求我们写一些报告,现在我们有小明这个同学,老师要求小明交上一份报告,好了小明写好了第一份初版报告(version-1),老师说格式不行,让小明重写,小明拿回去更改后第二个报告(version-2)又交给老师,老师又觉得小明报告的内容不行,让小明再次回去改,小明拿回去更改后第三份报告(version-3)交给老师,老师说小明你这写的啥啊,你还是把第一次写报告(version-1)给我吧,我给你过。
依照以上场景我们在生活中有时的需求会要求我们对一些文档/程序/等等做多次版本备份,以防出现需求回溯,而git就是这么一个版本控制器,Gitee/Github则是一个平台方便我们可视化地管理我们的版本。
7.1 git的安装
# 先检查你的Linux系统是否已经下载,如果没有下载执行以下代码
yum install git
# 查看是否已经安装好
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ ~]$ git --version
git version 1.8.3.1
7.2 创建远端仓库
在Gitee/Github平台创建一个远端仓库,复制URL,使用以下指令克隆一个本地仓库
git clone [url]
7.3 git三板斧
7.3.1 git add
将代码放到刚才下载好的⽬录中
git add [filename]
将需要⽤git管理的⽂件告知git
7.3.2 git commit
提交本地改动的描述
git commit -m "#内容"
提交的时候应该注明提交日志,描述改动的详细内容.
7.3.3 git push
同步到远端仓库
git push
7.4 补充知识
.gitignore内部存放着忽视上传的文件后缀
gitlog 查看提交日志
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ linux_-git_-warehouse]$ git log
commit 029dac80c6b2971a21e1b33697a156418cafa3dc
Author: 阳区欠 <2331701342@qq.com>
Date: Mon Dec 16 19:38:41 2024 +0800
进度条程序
commit 05fe264b868876a034b83d9205adcc99e1467aee
Author: 阳区欠 <2331701342@qq.com>
Date: Mon Dec 16 11:34:57 2024 +0000
Initial commit
8. gdb调试器
这边我使用的是cgdb,相对于gdb好调试一点
8.1 debug模式
我们平时直接执行gcc最后的可执行程序是release模式的代码是不可以调试的,如果想得到可调式的代码程序需要带上-g选项
gcc code.c -o code -g
cgdb调试器:
-
上半部分可以看到调试代码
-
下半部分可以输入调试指令
8.2 cgdb调试指令
-
quit/q :退出调试
-
break/b 行号:打断点
图中红色字就是断点所在处!
-
run/r :运行程序
-
如果存在断点,在断点处停下
-
如果没有断点,跑完整个程序
-
-
list/l 行数:列出源代码,从头开始,多次列出会记录上次的列出记录往下
(gdb) l 10
5 // 1+...+num
6 int total = 0;
7 int i = 0;
8 while(i <= num)
9 {
10 i++;
11 total += i;
12 }
13 printf("%d\n", total);
14 }
-
list/l 函数:列出我们所需函数模块的代码
(gdb) l TotalAdd
1 #include <stdio.h>
2
3 void TotalAdd(int num)
4 {
5 // 1+...+num
6 int total = 0;
7 int i = 0;
8 while(i <= num)
9 {
10 i++;
-
next/n 逐过程,会跳过函数不进入
-
step/s 逐语句,会进入函数内部
-
info break/b:查看断点信息
(gdb) b 19
Breakpoint 1 at 0x40056f: file code.c, line 19.
(gdb) b 12
Breakpoint 2 at 0x40054a: file code.c, line 12.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000040056f in main at code.c:19
2 breakpoint keep y 0x000000000040054a in TotalAdd at code.c:12
-
finish:执行到当前函数返回
(gdb) finish
Run till exit from #0 TotalAdd (num=100) at code.c:6
Breakpoint 2, TotalAdd (num=100) at code.c:13
-
printf/p 表达式:打印表达式的值
(gdb) p i+total
$1 = 5252
-
until 行号:执行到指定行号
-
set var 变量=值
(gdb) set var num=10
(gdb) until 13
TotalAdd (num=10) at code.c:13
(gdb) p i+total
$2 = 77
(gdb) p num
$3 = 10
-
continue/c:从当前位置连续执行程序
-
delete/d 断点编号:删除断点,如果没有断点编号删除所有断点
-
disable/enable 断点编号:禁用断点/启用断点
-
backtrace/bt:查看当前执行栈的各级函数调用及参数
-
info/i 局部变量:查看局部变量值
-
watch 表达式:实时监视表达式的变化
-
条件断点:
b 9 if i == 30 # 如果i==30时,在行号9的断点执行
• 条件断点添加常见两种方式:1.新增2.给已有断点追加
• 新增:b⾏号/⽂件名:行号/函数名if i==30(条件)
• 给已有断点追加:condition 2 i==30,其中2是已有断点编号,没有if