Makefile编写
示例1
1 | CC=gcc |
变量定义
1 | CC=gcc |
这两行代码定义了变量 CC 和 CFLAGS。
CC=gcc:定义变量CC为 gcc,指定使用 gcc 编译器进行编译。
CFLAGS=-Wall:定义变量CFLAGS为-Wall,指定编译时使用的选项,其中-Wall表示显示所有警告信息。
Makefile执行规则
对于
1 | rr: driver.o list.o CPU.o schedule_rr.o |
开头的 rr 表示后面的命令行代码可以用 make rr 来执行。而 rr 后面的 driver.o list.o CPU.o schedule_rr.o 指的是文件依赖项,文件依赖项的作用在于,在执行下面的编译命令
1 | gcc -Wall -o rr driver.o schedule_rr.o list.o CPU.o |
时,假如编译器发现找不到 schedule_rr.o 文件,那么它会找到 Makefile 中的这一行代码
1 | schedule_rr.o: schedule_rr.c |
先生成 schedule_rr.o 文件后再继续执行。
文件依赖项作用
- 构建顺序控制:依赖项指定了目标构建所依赖的文件或目标。如果依赖项中的文件发生变化或不存在,则目标会被重新构建。依赖项的存在可以确保在构建目标之前先构建其所依赖的文件或目标。
- 增量构建优化,避免重复构建:依赖项允许 make 工具进行增量构建优化。只有发生变化的文件及其相关依赖项会被重新构建,而不需要重新构建所有目标。这样可以提高构建效率。
头文件的依赖关系
1 | list.o: list.c list.h |
这段代码中只指明了 list.o 和 list.h 之间的依赖关系,而事实上 list.c 文件中还用到了头文件 task.h。这样可行是因为 Makefile 文件其实并不需要显式的包含源文件和头文件的依赖关系,这个关系在编译的时候会自动读取。所以将 list.o 后面的 list.h 去掉也是可行的。
示例2
1 | obj-m += seconds.o jiffies.o |
目标文件选择
obj-m += seconds.o jiffies.o 指要构建的目标文件为 seconds.o 和 jiffies.o,最后将构建名为 seconds 和 jiffies 的模块。
Makefile执行规则
对于
1 | all: |
开头的 all 表示后面的命令行代码可以用 make 来执行。接下来对后面的命令行进行解释:
- 这里直接使用的是
make命令而非之前那样的gcc,这是因为这是因为构建 Linux 内核模块的过程通常不仅仅涉及到 C 代码的编译,还包括了其他的操作,例如链接、处理符号表、生成模块文件等。这些操作超出了单纯的 C 代码编译所需的步骤。因此,在构建内核模块时,make命令会负责执行整个构建过程,其中也包含了调用gcc。 - 后面的
-C /lib/modules/$(shell uname -r)/build表示指定工作目录。如果不这么做,会导致工作目录不正确,make无法找到正确的库来编译生成模块。 M参数用于指定内核模块构建过程中的路径,这里M=$(shell pwd)指指定构建过程中的路径为当前工作目录。如果不这样做同样可以生成模块(因为上一步已经进入了正确的工作目录),但是从实践来看会出现一些权限上的问题,需要使用sudo make才能正常生成内核模块。- 最后的
modules是make命令的目标,表示要构建的目标是内核模块。
obj-m
在示例1中,我们的 Makefile 文件并没有用到 obj-m,这是因为 obj-m 是用于编译内核模块的特定变量,而在编译普通的用户空间程序时不会被用到。
在编写一个内核模块时,我们需要使用 obj-m 变量来定义的模块对象(.o 文件),以便在构建过程中让 make 工具会编译和链接相应的内核模块。
对于普通的用户空间程序,我们可以直接在 Makefile 中指定你的源文件和目标文件,而无需使用 obj-m 变量。
评论
