GCC编译
GCC 介绍
GCC(GNU Compiler Collection)是由GNU开发的编程语言编译器。最初为GNU操作系统开发,现在已被多数类Unix系统(如Linux、BSD、MacOS X等)采用,甚至在Windows上也可以使用。
GCC 编译流程
-
预处理:生成
.i
文件gcc -o hello.i -E hello.c
-
编译:将预处理后的文件转换为汇编语言,生成
.s
文件gcc -o hello.s -S hello.i
-
汇编:将汇编代码转换为目标代码(机器代码),生成
.o
文件gcc -o hello.o -c hello.s
-
链接:将目标代码链接生成可执行程序
gcc -o hello hello.o
GCC 参数
-
-o:指定目标文件名称
gcc -o output file.c
-
-S:仅生成汇编语言文件
gcc -S file.c
-
-c:仅编译为目标文件,不进行链接
gcc -c file.c
-
-E:仅进行预处理
gcc -E file.c -o file.i
-
-I[dirname]:将
dirname
加入到包含文件的搜索目录列表中gcc -I /usr/include file.c
-
-L[dirname]:将
dirname
加入到库文件的搜索目录列表中gcc -L /usr/lib file.c
-
-l[library]:链接时使用指定的库文件
gcc -L /usr/lib -lcurses file.c
-
-O0, -O1, -O2, -O3:设置优化级别,-O0 表示无优化,-O3 表示最高优化级别
gcc -O3 -o output file.c
-
-w:忽略警告信息
-
-Wall:显示所有警告信息
-
-Werror:将所有警告视为错误
-
-g:生成调试信息
-
-M:生成依赖关系信息
gcc -M file.c
-
-MM:生成依赖关系,但忽略标准头文件
gcc -MM file.c
-
-MD 和 -MMD:生成
.d
文件,包含依赖关系gcc -MD file.c
gcc -MMD file.c -
-MF File:指定依赖文件名称
gcc -M -MF dependencies.d file.c
-
-D macro:定义宏
gcc -DYES -o output file.c
-
-U macro:取消宏定义
gcc -DYES -UYES -o output file.c
静态库与动态库
静态库:以空间换时间,增加代码量,减少运行时间。静态库文件扩展名一般为 .a
。
动态库:以时间换空间,增加运行时间,减少代码量。动态库文件扩展名一般为 .so
。
// myalib.h |
创建静态库
-
编译生成目标文件
gcc -c myalib.c
-
使用
ar
命令归档目标文件ar -rc libtest.a myalib.o
-
使用库文件测试程序main.c
gcc -I ./ -o main.o -c main.c
gcc -o main -L ./ main.o -ltest
创建动态库
-
编译生成动态库
gcc -shared -fPIC -o libtest.so mylib.c
-
编译并链接使用动态库的程序
gcc -o main main.c -L ./ -ltest
-
使用
ldd
命令检查动态库ldd main
-
解决动态库问题
- 将库文件拷贝到系统库目录(如
/lib
)。 - 修改系统库搜索路径,通过配置
/etc/ld.so.conf
或在/etc/ld.so.conf.d/
中添加配置文件,运行ldconfig
更新配置。
- 将库文件拷贝到系统库目录(如
Gdb调试
什么是 gdb
gdb 是 GNU 开源组织发布的一个强大的 Unix/Linux 下的程序调试工具。它可以帮助用户调试程序,检查程序运行中的状态,并动态改变程序的执行环境。
gdb 说明文档位置:gdb 在线文档
gdb 的作用
-
启动用户程序:可以按照用户的要求随意运行程序。
-
设置断点:可让被调试的程序在用户设定的断点处停住。
-
检查程序状态:程序被停住时,可以检查当前程序中的状态。
-
动态改变执行环境:可以动态修改程序的执行环境。
gdb调试core
查看程序是否带有调试信息:
readelf -S <executable> | grep debug |
生成Core文件方法:
ulimit -c unlimited |
编译包含调试信息的可执行文件:使用 -g
选项编译源代码,以确保生成的可执行文件包含调试信息。
gcc -g main.c -o test |
调试Core文件:
gdb <executable> <core_file> |
gdb 常用命令
启动GDB:
gdb <executable> |
载入被调试程序:
file <executable> |
查看源码:
-
列出指定行号的代码:
list <line_number>,<line_number>
-
列出指定文件的源码:
list <filename>:<line_number>
-
列出指定函数的源码:
list <function_name>
运行程序:
run [arguments] |
设置断点:
-
通过行号设置断点:
break <filename>:<line_number>
-
通过函数名设置断点:
break <function_name>
-
设置条件断点:
break <line_number> if <condition>
查看断点:
info breakpoints |
删除断点:
delete <breakpoint_number> |
删除所有断点:
delete |
禁用/启用所有断点:
disable <breakpoint_number> |
单步调试:
-
单步进入(进入函数内部):
step
-
单步执行(不进入函数内部):
next
继续执行到下一个断点:
continue |
查看变量:
print <variable_name> |
设置变量:
set variable <variable_name> = <value> |
设置观察点:
watch <variable_name> |
查看函数调用栈:
backtrace |
分屏调试:
gdb -tui <executable> |
-
上半屏显示代码,下半屏显示 GDB 交互界面。
-
使用方向键调整源代码视图。
-
使用
fs n
或focus next
切换焦点。
Gdb调试死锁
|
编译运行
-
编译代码:
gcc -g test.c -lpthread -o test
-
运行程序:
./test
-
输出结果: 程序输出"thread 1 sell tickets:…"直到死锁发生。
调试方法1
-
查看test进程号:
ps aux | grep test
-
查看进程中的所有线程:
pstree -p <process_id>
-
使用GDB调试:
-
启动GDB:
gdb
-
附加到进程:
attach <process_id>
-
查看所有线程的堆栈跟踪:
thread apply all bt
-
调试方法2
-
查看test进程号:
ps -e | grep test
-
使用GDB调试:
-
启动GDB并附加到进程:
gdb test <process_id>
-
显示所有线程信息:
info threads
-
切换到特定线程(例如第二个线程):
thread 2
-
查看该线程的堆栈跟踪,以确定死锁位置:
bt
-
Reference
[1] GDB调试指南(入门,看这篇够了): https://blog.csdn.net/chen1415886044/article/details/105094688
[2] GDB:调试死锁:https://blog.csdn.net/guowenyan001/article/details/46238355