Linux 开发学习 (一) taro Posted on Mar 22 2021 Linux 笔记 开发 # gcc 编译四步骤 ![](/api/file/getImage?fileId=604e0db3c9095f000e000023) # gcc 编译参数 gcc编译: ``` 4步骤: 预处理、编译、汇编、连接。 -I: 指定头文件所在目录位置。 -c: 只做预处理、编译、汇编。得到 二进制 文件!!! -g: 编译时添加调试语句。 主要支持 gdb 调试。 -Wall: 显示所有警告信息。 -D: 向程序中“动态”注册宏定义。 #define NAME VALUE ``` 当头文件和源码不在一个目录下时,需要指定头文件 ![](/api/file/getImage?fileId=604e0db3c9095f000e000029) 将hello.h放入新建的文件夹hellodir之后,编译会失败 ![](/api/file/getImage?fileId=604e0db3c9095f000e00002b) gcc -I ./hellodir hello.c -o hello 其中-I参数指定头文件所在位置,位置可以在编译文件前,也可以在后面 ![](/api/file/getImage?fileId=604e0db3c9095f000e00002e) # 动态库和静态库理论对比 静态库在文件中静态展开,所以有多少文件就展开多少次,非常吃内存,100M展开100次,就是1G,但是这样的好处就是静态加载的速度快 使用动态库会将动态库加载到内存,10个文件也只需要加载一次,然后这些文件用到库的时候临时去加载,速度慢一些,但是很省内存 动态库和静态库各有优劣,根据实际情况合理选用即可。 ![](/api/file/getImage?fileId=604e0db3c9095f000e00002f) # 静态库制作 静态库名字以lib开头,以.a结尾 例如:libmylib.a 静态库生成指令 ar rcs libmylib.a file1.o - 写好源代码 ![](/api/file/getImage?fileId=604e0db3c9095f000e000030) - 编译源代码生成.o文件 ![](/api/file/getImage?fileId=604e0db3c9095f000e000038) - 制作静态库 ar rcs libname.a file1.o file2.o … ![](/api/file/getImage?fileId=604e0db3c9095f000e000036) - 静态库的使用: gcc test.c lib库名.a -o a.out ---------- ![](/api/file/getImage?fileId=604e0db3c9095f000e000031) 上图中,test.c直接使用了mymath库中的add,sub,div1函数,所以在编译时要导入静态库 编译时出现了函数未定义的警告,可以忽略,让系统生成默认的定义。 ![](/api/file/getImage?fileId=604e0db3c9095f000e000039) test.c只有296,然而test有8776,所以静态库使用时,是直接编译到文件里的。 # 静态库使用及头文件对应 上一节出现的警告,用编译器隐式声明来解决的 编译器只能隐式声明返回值为int的函数形式:`int add(int ,int )`; 如果函数不是返回的int,则隐式声明失效,会报错 - 在test.c中加入函数声明: ![](/api/file/getImage?fileId=604e0db3c9095f000e000026) - 再次进行编译,就不会报错了: ![](/api/file/getImage?fileId=604e0db3c9095f000e00002c) - 上面这个方法,需要库的使用者知道库里的函数,完事儿一个一个加到代码里,不太行 ---------- 下面使用头文件的方法来加载静态库 - 左边的define为头文件守卫,防止在代码中多次include头文件,多次展开静态库,带来的额外开销 ![](/api/file/getImage?fileId=604e0db3c9095f000e00002d) - 这样也不会报错了,而且更加科学 ![](/api/file/getImage?fileId=604e0db3c9095f000e000033) - 下面将静态库和头文件分别放至其他目录下 运行过程如下 ![](/api/file/getImage?fileId=604e0db3c9095f000e000027) # 动态库制作-生成与位置无关代码 ![](/api/file/getImage?fileId=604e0db3c9095f000e000035) 写在源代码里的函数,相对main函数偏移是一定的,链接时,回填main函数地址之后,其他源代码里的函数也就得到了地址。 动态库里的函数会用一个`@plt`来标识,当动态库加载到内存时,再用加载进去的地址将`@plt`替换掉。 # 动态库制作-演示 1. 生成位置无关的.o文件 gcc -c add.c -o add.o -fPIC 使用这个参数过后,生成的函数就和位置无关,挂上@plt标识,等待动态绑定 ![](/api/file/getImage?fileId=604e0db3c9095f000e000032) 2. 使用 gcc -shared制作动态库 gcc -shared -o lib库名.so add.o sub.o div.o ![](/api/file/getImage?fileId=604e0db3c9095f000e000037) 3. 编译可执行程序时指定所使用的动态库。-l:指定库名 -L:指定库路径 gcc test.c -o a.out -l mymath -L ./lib ![](/api/file/getImage?fileId=604e0db3c9095f000e000024) ![](/api/file/getImage?fileId=604e0db3c9095f000e000028) 4. 运行可执行程序./a.out ![](/api/file/getImage?fileId=604e0db3c9095f000e000025) # 动态库加载错误原因及解决方式 出错原因分析: 连接器: 工作于链接阶段,工作时需要 -l 和 -L 动态链接器: 工作于程序运行阶段,工作时需要提供动态库所在目录位置 指定动态库路径并使其生效,然后再执行文件 通过环境变量指定动态库所在位置:`export LD_LIBRARY_PATH=动态库路径` ![](/api/file/getImage?fileId=604e0db3c9095f000e00002a) 解决方式: ## 通过环境变量: export LD_LIBRARY_PATH=动态库路径./a.out 成功!!! (临时生效, 终端重启环境变量失效) ## 永久生效: 写入终端配置文件。 .bashrc 建议使用绝对路径。 1. vi ~/.bashrc 2. 写入 export LD_LIBRARY_PATH=动态库路径 保存 3. .bashrc/ source .bashrc / 重启 终端 ---> 让修改后的.bashrc生效 4. ./a.out 成功 ## 拷贝自定义动态库到 /lib (标准C库所在目录位置) ## 配置文件法 1) sudo vi /etc/ld.so.conf 2) 写入 动态库绝对路径 保存 3) sudo ldconfig -v 使配置文件生效。 4)./a.out 成功!!!--- 使用 ldd a.out 查看 # 扩展讲解-数据段合并 ![](/api/file/getImage?fileId=604e0db3c9095f000e000034) > 内存格式没有太懂,等待回来填坑 # gdb调试基础指令 使用gdb之前,要求对文件进行编译时增加-g参数,加了这个参数过后生成的编译文件会大一些,这是因为增加了gdb调试内容 gdb调试工具: 大前提:程序是你自己写的。 ---逻辑错误 > 基础指令: > -g:保留调试信息和符号表。 gdb ./a.out > > **l/list**: list 1 列出源码。根据源码指定行号设置断点。 > > **b/break**: b 20 在20行位置设置断点。 > > **run/r**: 运行程序 > > **n/next**: 下一条指令(会越过函数) > > **s/step**: 下一条指令(会进入函数) > > **p/print**:p i 查看变量的值。 > > **continue**:继续执行断点后续指令。 > > **finish**:结束当前函数调用。 > > **quit**:退出gdb当前调试。 > > >---------- > 其他指令 > > **run**:使用run查找段错误出现位置。 > > **set args**: 设置main函数命令行参数 (在 start、run 之前) > > **run** 字串1 字串2 ...: 设置main函数命令行参数 > > **info** b: 查看断点信息表 > > **b** 20 if i = 5: 设置条件断点。 > > **ptype**:查看变量类型。 > > **bt**:列出当前程序正存活着的栈帧。 > > **frame**: 根据栈帧编号,切换栈帧。 > > **display**:设置跟踪变量 > > **undisplay**:取消设置跟踪变量。 使用跟踪变量的编号。 段错误咋调呢? 直接run,程序停止的位置就是段错误的位置 ---------- ![](/api/file/getImage?fileId=604e1059c9095f000e00003a) # makefile基础规则 makefile: 管理项目。 命名:makefile Makefile --- make 命令 ## 1 个规则: 目标:依赖条件 (一个tab缩进)命令 1. 目标的时间必须晚于依赖条件的时间,否则,更新目标 2. 依赖条件如果不存在,找寻新的规则去产生依赖条件。 ALL:指定 makefile 的终极目标。 ## 2 个函数: src = $(wildcard ./*.c): 匹配当前工作目录下的所有.c 文件。将文件名组成列表,赋值给变量 src。 src = add.c sub.c div1.c obj = $(patsubst %.c, %.o, $(src)): 将参数3中,包含参数1的部分,替换为参数2。 obj = add.o sub.o div1.o clean: (没有依赖) -rm -rf $(obj) a.out “-”:作用是,删除不存在文件时,不报错。顺序执行结束。 ## 3 个自动变量: $@: 在规则的命令中,表示规则中的目标。 $^: 在规则的命令中,表示所有依赖条件。 $<: 在规则的命令中,表示第一个依赖条件。如果将该变量应用在模式规则中,它可将依赖条件列表中的依赖依次取出,套用模式规则。 模式规则: %.o:%.c gcc -c $< -o %@ 静态模式规则: $(obj):%.o:%.c gcc -c $< -o %@ 伪目标: .PHONY: clean ALL 参数: -n:模拟执行make、make clean 命令。 -f:指定文件执行 make 命令。 xxxx.mk > 学习大佬的笔记,大多为复制粘贴 https://github.com/guyanyijiu/baidu-online-disk-helper Kerberos Thinkphp笔记(架构)