前言

关于Linux系统编程的前导课程学习笔记,共涉及 GCC GDB Makefile 三个部分

1. GCC

介绍

**GCC**(英文全拼:GNU Compiler Collection)是 GNU 工具链的主要组成部分,是一套以 GPL 和 LGPL 许可证发布的程序语言编译器自由软件,由 Richard
Stallman 于 1985 年开始开发。
GCC 原名为 GNU C语言编译器,因为它原本只能处理 C 语言,但如今的 GCC 不仅可以编译 C、C++ 和 Objective-C,还可以通过不同的前端模块支持各种语言,包括
Java、Fortran、Ada、Pascal、Go 和 D 语言等等。
GCC 的编译过程可以划分为四个阶段:预处理(Pre-Processing)、编译(Compiling)、汇编(Assembling)以及链接(Linking)。

1.1 相关参数

4个步骤 :预处理、编译、汇编、链接

-E:仅执行预处理 # hello.i 源文件 i
-S:只编译 # hello.s 汇编文件 s
-c:编译和汇编,但不链接。 # hello.o 目标文件 o
-o :指定输出文件。 # a.out 可执行文件
-I: 指定头文件位置 # gcc -I ./inc hello.c -o hello
-g: 编译时增加调试文件
-Wall: 显示所有警告信息
-D: 向程序中“动态“注册宏定义

1.2 静态库和动态库

静态库

空间大,速度块,基本淘汰

1. gcc -c foo.c -o foo.o # 生成 foo.o 目标文件
2. ar rcs libfoo.a foo.o # 生成 libfoo.a 静态库
3. gcc hello.c -static libfoo.a -o hello # 编译 并链接静态库 libfoo.a
4. gcc test.c -static ./lib/libmymath.a -I ./inc -o b.out # 文件+库+头文件

动态库

空间小,速度慢,最多使用

1. gcc -c add.c -o add.o -fPIC # 生成于位置无关的代码 -fPIC
2. gcc -shared -o libmymath.so add.o sub.o # 制作动态库
3. -l: 指定库名 -L:指定库路径
gcc test.c -lmymath -L./lib -I./inc -o a.out # 编译可执行程序

执行报错
原因

  1. 链接器:工作于链接阶段,工作时需要 -l 和 -L
  2. 动态链接器:工作于程序运行阶段,工作时需要提供动态库所在目录位置解决
    解决 :
  3. export LD\_LIBRARY\_PATH=动态库路径:$LD\_LIBRARY\_PATH (一次进程里,临时)
  4. 移动到 /usr/lib (永久)
  5. .bashrc 中 写入 环境变量 (永久)
  6. vim /etc/ld.so.conf # 写入动态库的路径
    ldconfig # 刷新 (永久)
    ldd是Linux系统下的一个命令行工具,用于查看可执行程序或共享库所依赖的动态链接库(Dynamic Linking
    Library,简称DLL)。它会列出指定的可执行程序或共享库所使用的共享库,并显示它们的完整路径。
    root@freecho:/opt/C/gcc/lib/dynamicLib# ldd a.out
    linux-vdso.so.1 (0x0000ffffb6b6d000)
    libmymath.so => /lib/libmymath.so (0x0000ffffb6af0000)
    libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000ffffb6940000)
    /lib/ld-linux-aarch64.so.1 (0x0000ffffb6b34000)
    root@freecho:/opt/C/gcc/lib/dynamicLib#

    2. GDB

    介绍

    **GDB** 全称“GNU symbolic debugger”,从名称上不难看出,它诞生于 GNU 计划(同时诞生的还有 GCC、Emacs 等),是 Linux 下常用的程序调试器。发展至今,GDB
    已经迭代了诸多个版本,当下的 GDB 支持调试多种编程语言编写的程序,包括 C、C++、Go、Objective-C、OpenCL、Ada 等。实际场景中,GDB 更常用来调试 C 和 C++ 程序

    2.1 基础指令

    gcc gdbtest.c -o a.out -g # -g 使用该参数编译可执行文件,得到调试表
    gdb a.out # 开始
    l 1 # 从第一行开始显示
    l # 继续往下显示 (lsit)
    b 52 # 52行设置断点 (breakout)
    r # 运行 (run)
    n # 逐过程单步执行,越过函数,系统函数用 n (next)
    s # 逐语句单步执行,进入函数,其他情况与n无区别,执行下一步 (step)
    until 16 # 运行程序直到达到指定的行号并停止
    p i # 打印变量 i 的值 (print)
    continue # 继续执行断点后续指令
    quit # 退出gdb调试

    2.2 断点指令

    (gdb) b 23 # 设置断点
    (gdb) b 30
    (gdb) b 41
    (gdb) b 41 if i=5 # 设置条件断点
    (gdb) info b # 查看断点信息表
  7. breakpoint keep y 0x00000000000009f8 in select\_sort at gdbtest2.c:23
  8. breakpoint keep y 0x0000000000000a7c in select\_sort at gdbtest2.c:30
  9. breakpoint keep y 0x0000000000000b14 in print\_arr at gdbtest2.c:41
  10. breakpoint keep y 0x0000000000000b14 in print\_arr at gdbtest2.c:41
    stop only if i=5
    (gdb) delete 3 # 删除指定断点
    delete # 删除所有断点
    disable # 禁用断点
    enable # 启用断点

  11. 使用 r 查找段错误出现位置
  12. start :用于开始执行程序并停在main函数的第一条语句上
  13. finish :当你在调试一个函数时,你可以使用finish命令来执行该函数的剩余部分,并停在函数调用的地方
  14. ser args :设置 main 函数命令行参数,在start run 命令之前
  15. run 字符串1字符串2 ... : 设置 main 函数命令行参数
  16. info b:查看断点信息表 information breakout
  17. ptype i:查看变量类型
  18. backtrace :查看函数的调用的栈帧和层级关系 缩写:bt
  19. frame 编号:切换函数的栈帧 缩写:f
  20. display i :设置跟踪变量
  21. undisplay 编号:取消跟踪变量

    补:栈帧

    栈帧(Stack Frame) 是在程序执行期间,用于管理函数调用和局部变量的一种数据结构。在函数调用时,会创建一个栈帧来存储函数的参数、局部变量以及保存调用函数的返回地址和调用函数的寄存器状态等信息。
    每个函数调用都会创建一个新的栈帧,将其添加到函数调用栈(Call Stack)的顶部。当函数执行结束后,其对应的栈帧会被销毁,栈帧也会被从调用栈中移除,程序会返回到调用该函数的上一个栈帧继续执行。
    栈帧通常包含以下几个重要的部分:

  22. 返回地址:指向调用该函数的下一条指令的地址,用于函数执行完后返回到正确的位置。
  23. 函数参数:将函数的参数值存储在栈帧中,以便在函数内部使用。
  24. 本地变量:函数中定义的局部变量也被存储在栈帧中,它们只在函数执行期间存在,函数结束后将被销毁。
  25. 保存的寄存器状态:为了保护调用函数时的寄存器状态,可能需要将某些寄存器的值保存在栈帧中,在函数执行完后再恢复寄存器的值。
    栈帧的使用使得函数调用和返回的过程更加高效和可控,同时提供了函数间局部变量的隔离,避免了函数之间的冲突。栈帧在计算机体系结构和操作系统中发挥着重要作用,为函数调用提供了必要的支持。

    3. Makefile

    介绍

    Makefile是一个用于构建和管理项目的工程文件,它定义了项目中的源文件、依赖关系以及构建规则等信息,用于自动化编译和链接程序。
    Makefile通常使用make工具来执行其中定义的规则和命令。通过Makefile,开发人员可以在项目中定义编译、链接、清理等操作的规则,以及指定项目中的文件之间的依赖关系。这样,当项目的源文件发生变化时,make工具可以根据Makefile中的规则,自动检测变化并重新编译需要更新的文件,从而加快项目的开发和构建过程。
    文件命令:makefile Makefile

    1个规则

    目标:依赖条件
    (一个tab缩进) 命令
  26. 目标的时间必须晚于依赖条件的时间,否则,更新目标
  27. 依赖条件如果不存在,找寻新的规则去产生依赖

    ALL:指定makefile的最终目标,否则最终目标写在文件开头
    步骤
    root@freecho:/opt/C/make# vim makefile
    root@freecho:/opt/C/make# cat makefile
    hello:hello.c
    gcc hello.c -o a.out
    root@freecho:/opt/C/make# ls
    hello.c makefile
    root@freecho:/opt/C/make# make
    gcc hello.c -o a.out
    root@freecho:/opt/C/make# ls
    a.out hello.c makefile

    makefile文件1

    ALL:a.out # 指定makefile的最终目标,否则最终目标写在文件开头
    hell.o:hello.c
    gcc -c hello.c -o hello.o
    add.o:add.c
    gcc -c add.c -o add.o
    sub.o:sub.c
    gcc -c sub.c -o sub.o
    mul.o:mul.c
    gcc -c mul.c -o mul.o
    div1.o:div1.c
    gcc -c div1.c -o div1.o
    a.out:hello.o add.o sub.o mul.o div1.o
    gcc hello.o add.o sub.o mul.o div1.o -o a.out

    2个函数

    两个函数分别为:wildcard(通配符)patsubst(匹配替换)
    **2.1 $(wildcard ./\*.c)**

    src = $(wildcard \*.c)
    # src = add.c sub.c mul.c div1.c

    匹配当前目录下的所有后缀为 .c 的文件,将文件名组成列表,赋值给变量 src
    **2.2 $(patsubst %.c, %.o, $(src))**

    obj = $(patsubst %.c, %.o, $(src))
    # obj = add.o sub.o mul.o div1.o

    参数3 中,包含 参数1 的部分,替换为 参数2,即把 src 变量里所有后缀为 .c 的文件替换为 .o
    makefile文件2、3

    src = $(wildcard \*.c) # src = add.c sub.c mul.c div1.c
    obj = $(patsubst %.c, %.o, $(src)) # obj = add.o sub.o mul.o div1.o
    ALL:a.out
    hell.o:hello.c
    gcc -c hello.c -o hello.o
    add.o:add.c
    gcc -c add.c -o add.o
    sub.o:sub.c
    gcc -c sub.c -o sub.o
    mul.o:mul.c
    gcc -c mul.c -o mul.o
    div1.o:div1.c
    gcc -c div1.c -o div1.o
    a.out: $(obj)
    gcc $(obj) -o a.out
    clean: # 终端输入 make clean 才会生效
    -rm -rf $(obj) # -rm 前面的 '-' 表示出错依然执行

    步骤
    clean: (没有依赖)
    make clean -n 会演示清除命令,不会实际生效
    make clean 则会实际生效

    root@freecho:/opt/C/make# make
    gcc -c add.c -o add.o
    gcc -c div1.c -o div1.o
    gcc -c -o hello.o hello.c
    gcc -c mul.c -o mul.o
    gcc -c sub.c -o sub.o
    gcc add.o div1.o hello.o mul.o sub.o -o a.out
    root@freecho:/opt/C/make# ls
    add.c add.o a.out div1.c div1.o hello.c hello.o m1 m2 makefile mul.c mul.o sub.c sub.o
    root@freecho:/opt/C/make# make clean -n
    rm -rf add.o div1.o hello.o mul.o sub.o
    root@freecho:/opt/C/make# ls
    add.c add.o a.out div1.c div1.o hello.c hello.o m1 m2 makefile mul.c mul.o sub.c sub.o
    root@freecho:/opt/C/make# make clean
    rm -rf add.o div1.o hello.o mul.o sub.o
    root@freecho:/opt/C/make# ls
    add.c a.out div1.c hello.c m1 m2 makefile mul.c sub.c

    3个自动变量

    $@ :在规则的命令中,表示规则中的目标
    $< :在规则的命令中,表示规则中的第一个条件,如果将该变量应用于 模式规则 中,它可将依赖列表中的依赖依次取出,套用规则模式
    $^ :在规则的命令中,表示规则中的所有依赖条件,组成一个列表,以空格隔开,如果这个列表中有重复的项则消除重复
    makefile文件4

    src = $(wildcard \*.c) # src = add.c sub.c mul.c div1.c
    obj = $(patsubst %.c, %.o, $(src)) # obj = add.o sub.o mul.o div1.o
    ALL:a.out
    hell.o:hello.c
    gcc -c $< -o $@
    add.o:add.c
    gcc -c $< -o $@
    sub.o:sub.c
    gcc -c $< -o $@
    mul.o:mul.c
    gcc -c $< -o $@
    div1.o:div1.c
    gcc -c $< -o $@
    a.out: $(obj)
    gcc $^ -o $@
    clean:
    -rm -rf $(obj)

    模式规则

    编译 .c 文件 生成 .o 文件

    %.o:%.c
    gcc -c $< -o $@

    makefile文件5

    src = $(wildcard \*.c) # src = add.c sub.c mul.c div1.c
    obj = $(patsubst %.c, %.o, $(src)) # obj = add.o sub.o mul.o div1.o
    ALL:a.out
    a.out: $(obj)
    gcc $^ -o $@
    %.o:%.c
    gcc -c $< -o $@
    clean:
    -rm -rf $(obj) a.out

    拓展

    **静态模式规则**
    表示对哪一个依赖条件套用这个规则

    $(obj):%.o:%.c
    gcc -c $< -o $@

    **伪目标**
    声明了 .PHONY: clean ALL,它告诉 Make 不会去检查是否存在名为 cleanALL
    的文件,而是直接执行它们后面的命令块。这样,运行 make cleanmake ALL 时,Make 将执行相应的命令块。

    .PHONY: clean ALL

    最终形态 makefile

    src = $(wildcard \*.c)
    obj = $(patsubst %.c, %.o, $(src))
    myArgs = -Wall -g
    ALL:a.out
    a.out: $(obj)
    gcc $^ -o $@ $(myArgs)
    $(obj):%.o:%.c
    gcc -c $< -o $@ $(myArgs)
    clean:
    -rm -rf $(obj) a.out
    .PHONY: clean ALL

    **相关参数**

    -n:模拟执行make、make clean 命令
## 相关链接
教程视频:[Linux系统编程\*哔哩哔哩\*bilibili](https://www.bilibili.com/video/BV1KE411q7ee?p=1)
Linux系列文章:[Linux – Echo (liveout.cn)](https://www.liveout.cn/category/learn/linux/)
Linux基础命令:[Linux学习资料分享 – Echo (liveout.cn)](https://www.liveout.cn/1-2/)
Vim配置:[Linux学习资料分享 – Echo (liveout.cn)](https://www.liveout.cn/1-2/)
GitHub仓库,包含教程讲义、代码以及笔记:

标签: C/C++, Linux

已有 7 条评论

  1. 开始学cmake,但是makefile又忘记了,回来看看~

  2. 今天读了《CSAPP》的第七章:链接。感觉有了更深的理解。
    《CSAPP》,即《深入理解计算机系统》是一本非常经典的著作,非常值得认真阅读!

  3. 动态库使用:网络编程时将错误检查封装成动态库从而规模化

  4. […] GCC、GDB、Makefile:GCC、GDB、Makefile学习笔记 – Echo (liveout.cn) […]

  5. […] GCC、GDB、Makefile:GCC、GDB、Makefile学习笔记 – Echo (liveout.cn) […]

  6. 在搞编译原理吗?

    1. 在学习linux编程,打算以后搞嵌入式软件~ :flower-heihei:

添加新评论