简易环境搭建
本地
- 下载 vmware
- 从官网下载 20.04LTS 镜像(长期维护版),安装虚拟机,100G空间,4G内存,2核(本地 win11,1T,16G,4核)
- 下载 ssh,复制 id_rsa.pub 公钥文件
虚拟机
- 安装 gcc,g++,ssh,gdb,make,
sudo apt install gcc g++ ssh gdb make
- 创建 ssh 公私钥,
ssh-keygen
- 复制之前的公钥到 authorized_keys文件,若没有则创建该文件
- ifconfig 查看 ip 地址
本地
- 下载vscode,在remote管理设置里,以之前 ip 地址创建远程连接
- 登录虚拟机
- 下载 C++ 全家桶插件,中文插件,文件显示美化插件
GCC
编译 C 文件,尽量使用 gcc 命令,编译 C++ 文件,尽量使用 g++
gcc 不能自动和 C++ 使用的库链接,g++ 统一当作 C++ 编译,语法检查更严,故尽量用对应命令编译
gcc编译选项 | 说明 |
---|---|
-E | 预处理指定的源文件,不进行编译 |
-S | 编译指定的源文件,但是不进行汇编 |
-c | 编译、汇编指定的源文件,但是不进行链接 |
-o | -o file1 file2 或 file2 -o file1 将文件 file2 编译成可执行文件 file1 |
-I | 指定include包含文件的搜索目录 |
-g | 在编译的时候,生成调试信息,该程序可以被调试器调试 |
-D | 在程序编译的时候,指定一个宏 |
-w | 不生成任何警告信息 |
-Wall | 生成所有警告信息 |
-On | n的取值范围:0~3。编译器的优化选项的4个级别,-o0表示没有优化,-o1为缺省值,-o3优化级别最高 |
-l | 在程序编译的时候,指定使用的库 |
-L | 指定编译的时候,搜索的库的路径 |
-fPIC/-fpic | 生成与位置无关的代码 |
-shared | 生成共享目标文件,通常用在建立共享库时 |
-std | 指定c方言,如:-std=c99,gcc默认的方言是GNU c |
库
静态库:GCC 进行链接时,会把静态库中代码打包到可执行程序中
动态库:GCC 进行链接时,动态库的代码不会被打包到可执行程序中
静态库
制作
ar rcs libxxx.a ...
使用 .o 文件创建一个名为xxx的静态库
r —— 将文件插入备存文件中
c —— 建立备存文件
s —— 索引
扩展名在 linux 下是 .a ,windows 下 .lib
使用
对于该示例,可以使用命令 gcc main.c -o app -I ./include/ -l calc -L ./lib
来将静态库编译进来
表示在 ./include
目录下搜寻头文件,链接名为 calc
的库,在 ./lib
下搜寻库
动态库
程序启动之后,动态库会被动态加载到内存中,通过ldd (list dynamicdependencies)命令检查动态库依赖关系
动态库的定位(linux系统):
对于elf格式的可执行文件,由 ld-linux.so 完成链接,路径为
DT_RPATH段 -> 环境变量 LD_LIBRARY_PATH -> /etc/ld.so.cache 文件列表 -> /lib/,/usr/lib
环境变量:
全局在 /etc/profile 修改,用户在 ~/.bashrc,用户与全局相同的环境变量会覆盖
一个变量可以对应多个值,多个值以 :隔开,寻找时依次寻找
创建环境变量可用在上述两个文件中写export命令,并source使之生效,比如 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:绝对路径
不用export定义的变量只对该shell有效,对子shell也是无效的
如果只在某个终端写export命令,则环境变量是临时的
制作
gcc -c -fpic/fPIC a.c b.c
gcc -shared a.o b.o -o libcalc.so
-fpic/fPIC 生成位置无关的文件
-shared 生成共享库,以动态链接
如果链接的可执行文件的GOT大小超过计算机特定的最大大小,则会从链接器收到错误消息,指示-fpic不起作用。在这种情况下,请使用-fPIC重新编译。GOT大小根据操作系统的不同而大小不一样。因此尽量使用-fPIC编译
扩展名在 linux 下是 .a ,windows 下 .dll
使用
示例:
gcc main.c -o main -I include/ -L lib/ -l calc
而后由之前所说有三种方法:
第一种:
然后在之前说的两个文件夹添加环境变量地址即可,并使用source使之生效
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/caidd/linux/test/lib
该命令表示,保持原始环境变量的内容,在其后添加一个变量,即库文件夹位置
第二种:
修改 /etc/ld.so.conf 文件,直接将目标库的文件夹绝对位置复制到最后即可
然后使用 sudo ldconfig 使之生效
第三种:
将库放到 /lib/,/usr/lib 里面,便会自动找到,但由于会引起命名冲突,所以不推荐
静态库与动态库对比
静态库:
优点
- 静态库被打包到应用程序中加载速度快
- 发布程序无需提供静态库,移植方便
缺点
- 消耗系统资源,浪费内存
- 更新、部暑、发布麻烦
动态库:
优点
- 可以实现进程间资源共享(共享库)更新、部署、发布简单
- 可以控制何时加载动态库
缺点
- 加载速度比静态库慢
- 发布程序时需要提供依赖的动态库
Makefile
自己学,后续会接触到的cmake,xmake,主要用于生成 makefile 文件。这些东西都是用于处理大型项目的依赖关系。
Makefile 不仅使得项目编译更为轻松,还能使编译速度更快,其会自动比对文件的修改时间,只有当目标文件不存在或依赖文件的更新时间晚于目标文件才会进行更新。
GDB
生成可调试执行程序,gcc,g++ 需要加上 -g 参数,才能在原始程序添加符号表,使得 gdb 能够进行调试
启动和退出
gdb 可执行程序名字
quit 退出
给程序设置参数/获取设置参数
set args 10 20
show args
GDB使用帮助
help
查看当前文件代码
list/l (从默认位置显示)
list/l 行号 (以指定行号为中心进行显示)
list/l 函数名 (从指定的函数显示)
查看非当前文件代码
list/l 文件名:行号
list/l 文件名:函数名
设置与查看显示的行数
show list/listsize
set list/listsize 行数
设置断点
b/break 行号
b/break 函数名
b/break 文件名:行号
b/break 文件名:函数
查看断点
i/info b/break
删除断点
d/del/delete 断点编号
设置断点无效
dis/disable 断点编号
设置断点生效
ena/enable 断点编号
设置条件断点(一般用在循环的位置)
b/break 10 if i == 5
运行GDB程序
start(程序停在第一行)
run(遇到断点才停)
继续运行,到下一个断点停
c/continue
向下执行一行代码(不会进入函数体)
n/next
变量操作
p/print 变量名(打印变量值)
ptype 变量名(打印变量类型)
向下单步调试(遇到函数进入函数体)
s/step
finish(跳出函数体)
自动变量操作
display num(自动打印指定变量的值)
i/info display
undisplay 编号
其他操作
set var 变量名 = 变量值
untill (跳出循环,跳出循环的条件是循环内部没有断点,并且在循环语句内)
多进程调试
set folllow-fork-mode [parent(默认) | child] 设置调试父进程或者子进程
set detach-on-fork [on l off] 设置调试模式
默认为on,表示调试当前进程的时候,其它的进程继续运行,如果为 off,调试当前进程的时候.其它进程被GDB挂起。
info inferiors 查看调试的进程
inferior id 切换当前调试的进程
detach inferiors id 使进程脱离GDB调试
Core dump 分析
gdb core模式