前言1、hello2、GCC
2.1、GCC编译过程2.2、GCC常用选项 3、Makefile
3.2、Makefile的引入及规则3.3、Makefile的语法3.4、Makefile函数
前言韦东山嵌入式Linux应用开发基础知识学习笔记
视频教程地址: https://www.bilibili.com/video/BV1kk4y117Tu?spm_id_from=333.788.b_765f64657363.5
1、hellohello.c
#includeint main(int argc, char **argv) { if (argc >= 2) printf("Hello, %s!n", argv[1]); else printf("Hello, world!n"); return 0; }
main函数参数说明:
argc:argument count 参数个数
argv:argument value 参数值
示例:
输入hello weidongshan
此时argc=2 argv[0]="hello" argv[1]="weidongshan"
安装完成NFS服务之后执行bash命令:arm-buildroot-linux-gnueabihf-gcc -o hello hello.c
在开发板上挂载Ubuntu的NFS目录来互传文件:
在开发板上使用此命令来使得网络互通的开发板和ubuntu主机之间建立挂载关系:mount -t nfs -o nolock,vers=3
例如:mount -t nfs -o nolock,vers=3 10.234.201.221:/home/mi/nfs_rootfs /mnt
发展历程: 电工开关->纸带打孔(使用机器码)->英文助记符(使用汇编码)->使用高级语言
下面通过一个程序的编译过程来说明这四个步骤:
hello.c
#include#define MAX 20 #define MIN 10 //#define _DEBUG #define SetBit(x) (1< 在文件目录下的控制终端输入$ gcc -o hello hello.c -v可以得到编译过程:
Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper OFFLOAD_TARGET_NAMES=nvptx-none OFFLOAD_TARGET_DEFAULT=1 Target: x86_64-linux-gnu Configured with: ../src/configure -v --with-pkgversion='Ubuntu 7.5.0-3ubuntu1~18.04' --with-bugurl=file:///usr/share/doc/gcc-7/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-7 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu Thread model: posix gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04) COLLECT_GCC_OPTIONS='-o' 'hello' '-v' '-mtune=generic' '-march=x86-64' /usr/lib/gcc/x86_64-linux-gnu/7/cc1 -quiet -v -imultiarch x86_64-linux-gnu hello.c -quiet -dumpbase hello.c -mtune=generic -march=x86-64 -auxbase hello -version -fstack-protector-strong -Wformat -Wformat-security -o /tmp/cceyrvE0.s GNU C11 (Ubuntu 7.5.0-3ubuntu1~18.04) version 7.5.0 (x86_64-linux-gnu) compiled by GNU C version 7.5.0, GMP version 6.1.2, MPFR version 4.0.1, MPC version 1.1.0, isl version isl-0.19-GMP GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu" ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/7/../../../../x86_64-linux-gnu/include" #include "..." search starts here: #include <...> search starts here: /usr/lib/gcc/x86_64-linux-gnu/7/include /usr/local/include /usr/lib/gcc/x86_64-linux-gnu/7/include-fixed /usr/include/x86_64-linux-gnu /usr/include End of search list. GNU C11 (Ubuntu 7.5.0-3ubuntu1~18.04) version 7.5.0 (x86_64-linux-gnu) compiled by GNU C version 7.5.0, GMP version 6.1.2, MPFR version 4.0.1, MPC version 1.1.0, isl version isl-0.19-GMP GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 Compiler executable checksum: b62ed4a2880cd4159476ea8293b72fa8 COLLECT_GCC_OPTIONS='-o' 'hello' '-v' '-mtune=generic' '-march=x86-64' as -v --64 -o /tmp/ccfFL7CE.o /tmp/cceyrvE0.s GNU汇编版本 2.30 (x86_64-linux-gnu) 使用BFD版本 (GNU Binutils for Ubuntu) 2.30 COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/ LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../:/lib/:/usr/lib/ COLLECT_GCC_OPTIONS='-o' 'hello' '-v' '-mtune=generic' '-march=x86-64' /usr/lib/gcc/x86_64-linux-gnu/7/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/7/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper -plugin-opt=-fresolution=/tmp/ccqVYCCi.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro -o hello /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/7 -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/7/../../.. /tmp/ccfFL7CE.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o COLLECT_GCC_OPTIONS='-o' 'hello' '-v' '-mtune=generic' '-march=x86-64'从编译过程中可以梳理出下图所示的四个步骤(预处理、编译、汇编、链接):
▲hello.c编译过程 上述过程可以使用gcc -o hello hello.c完成,也可以使用下面四条命令分别完成
gcc -E -o hello.i hello.c <-预处理
gcc -S -o hello.s hello.i <-编译
gcc -c -o hello.o hello.s <-汇编
gcc -c hello hello.o <-链接tips:语法错误是在编译过程中检测到的。
2.2、GCC常用选项▲GCC常用选项 关于GCC常用选项-c的使用:
有这么几个文件:
main.c#include#include "sub.h" int main(int argc, char *argv[]) { int i; printf("Main fun!n"); sub_fun(); return 0; } sub.c
void sub_fun(void) { printf("Sub fun!n"); }sub.h
void sub_fun(void);在文件目录下的终端里输入以下指令:
$ gcc -o test main.c sub.c
$ ./test
可以得到如下结果:
▲终端结果 那么多个文件GCC编译产生的流程又是怎样的呢?
答案如下:
▲多个文件GCC编译流程 这么做的问题:多个文件在进行GCC编译时可能由于一个文件中包含的错误使得整个过程前功尽弃,于是我们可以采用先编译不链接最后统一链接的方法来提高GCC编译的效率。
先编译不链接最后统一链接的方法:
gcc -c -o main.o main.c <-编译
gcc -c -o sub.o sub.c <-编译
gcc -o test main.o sub.o <-链接
./test <-执行▲GCC常用选项 关于GCC常用选项-I的使用:
如果把main.c中的#include "sub.h"更改为#include
会发生什么?
▲Linux终端界面 很显然编译器没有找到sub.h目录所以中止了编译
输入gcc -o test main.c sub.c -v可以在终端查看不同符号修饰头文件名时编译器会搜寻的目录
▲Linux终端界面 使用-I选项可以拓宽<>修饰符所指定的寻找范围,比如输入$ gcc -o test main.c sub.c -I ./ -v,就会发现<>修饰符所指定的寻找范围中多了当前目录。
▲Linux终端界面 ▲GCC常用选项 关于GCC常用选项-L和-l的使用:
在链接库时需要先制作库:库分为动态库和静态库两种。动态库、静态库两者的区别:
静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库
动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在1、制作、使用静态库:
gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
ar crs libsub.a sub.o (可以使用多个.o 生成静态库) <-制作静态库
gcc -o test main.o libsub.a (如果.a 不在当前目录下,需要指定它的绝对或相对路径) <-使用静态库
2、制作、使用动态库:
gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
gcc -shared -o libsub.so sub.o (可以使用多个.o 生成动态库) <-制作动态库
gcc -o test2 main.o libsub.so 或 gcc -o test2 main.o -L ./ -lsub <-使用动态库(其中-l后输入链接哪一个库文件即库文件的文件名sub而非动态库名称libsub.so)
为什么要指定库文件目录和库文件?:当使用文件名寻找动态库时系统会从LIBRARY_PATH中寻找故需要指定库文件目录和库文件来拓展LIBRARY_PATH范围
在运行链接了动态库的文件时不能使用./ 文件名,而要使用export LD LIBRARY_PATH=$LD_LIBRARY_PATH:./指定动态库的位置再运行,这一操作同链接时指定位置是同样道理。
▲Linux终端界面 3、Makefile 3.2、Makefile的引入及规则一些很有用的选项:
gcc -E main.c // 查看预处理结果,比如头文件是哪个
gcc -E -dM main.c > 1.txt // 把所有的宏展开,存在 1.txt 里
gcc -Wp,-MD,abc.dep -c -o main.o main.c // 生成依赖文件 abc.dep,后面 Makefile 会用
echo 'main(){}'| gcc -E -v - // 它会列出头文件目录、库目录(LIBRARY_PATH)使用keil, mdk,avr等工具开发程序时点击鼠标就可以编译了。
1、它的内部机制是什么?
2、它怎么组织管理程序?
3、怎么决定编译哪一个文件?有这么几个文件:
a.c#includeint main() { func_b(); return 0; } b.c
#includevoid func_b() { printf("This is Bn"); } ① 为什么需要 Makefile:高效地编译程序,修改源文件或头文件,只需要重新编译牵涉到的文件,就可以重新生成 APP。
② Makefile 的规则:目标(target)… : 依赖(prerequiries)…
命令(command)
如果“依赖文件”比“目标文件”更加新,那么执行“命令”来重新生成“目标文件”。
命令被执行的 2 个条件:依赖文件比目标文件新,或是 目标文件还没生成。如果想实现:
gcc -c -o a.o a.c
gcc -c -o b.o b.c
gcc -o test a.o b.o
的高效编译,就需要在Makefile里这么写:test:a.o b.o gcc -o test a.o b.o a.o : a.c gcc -c -o a.o a.c b.o : b.c gcc -c -o b.o b.c按照规则中所描述的流程运行使用make命令会出现如下效果:
▲Linux终端界面 3.3、Makefile的语法①、通配符
%.o:表示所用的.o文件
%.c:表示所有的.c文件
$@:表示目标
$<:表示第1个依赖文件
$^:表示所有依赖文件使用通配符可以简化规则。比如我们对上节程序进行修改代码如下:
test:a.o b.o gcc -o test a.o b.o a.o : a.c gcc -c -o a.o a.c b.o : b.c gcc -c -o b.o b.c test: a.o b.o gcc -o test $^ %.o : %.c gcc -c -o $@ $<增加一个c.c:
c.c#includevoid func_c() { printf("This is Cn"); } 修改Makefile:
test: a.o b.o c.o gcc -o test $^ %.o : %.c gcc -c -o $@ $<运行有以下结果:
▲Linux终端界面
②、假想目标: .PHONY
如果想清楚可执行文件可以在Makefile结尾加上如下代码:clean: rm *.o test此时可以使用make clean指令来删除已经生成的可执行文件。
▲可执行文件的编译和删除 需要指出的是:make后面可以带上目标名,也可以不带,如果不带目标名的话它就想生成第一个规则里面的第一个目标。
但是仅仅这么做存在一个问题:
如果目录下存在一个名为clean的文件则会出现如下现象:
▲Linux终端界面 为什么?
我们之前说,一个规则能过执行的条件:
*1)目标文件不存在
*2)依赖文件比目标新现在我们的目录里面有名为“clean”的文件,目标文件是有的,并且没有
依赖文件,没有办法判断依赖文件的时间。这种写法会导致:有同名的"clean"文件时,就没有办法执行make clean操作。解决办法:我们需要把目标定义为假象目标,用关键字PHONY
在Makefile中插入以下代码:.PHONY: clean重新执行make clean操作就会执行相应的删除操做。
③变量
Makefile中有两种变量类型:
(1)简单变量/即时变量
A := xxx # A的值即刻确定,在定义时即确定
(2)延时变量
B = xxx # B的值使用到时才确定想使用变量的时候使用“$”来引用,如果不想看到命令是,可以在命令的前面加上"@"符号,就不会显示命令本身。当我们执行make命令的时候,make这个指令本身,会把整个Makefile读进去,进行全部分析,然后解析里面的变量。常用的变量的定义如下:
:= # 即时变量 = # 延时变量 ?= # 延时变量, 如果是第1次定义才起效, 如果在前面该变量已定义则忽略这句 += # 附加, 它是即时变量还是延时变量取决于前面的定义(附加后变量在输出类似于字符串拼接)如果在一个Makefile文件中有以下代码
A := $(C) B = $(C) C = abc D = 100ask D ?= weidongshan all: @echo A = $(A) @echo B = $(B) @echo D = $(D) C += 123按照上述语法规则,执行文件可以得到以下结果:
▲Linux终端界面 3.4、Makefile函数几个常用的Makefile函数:
$(foreach var,list,text)#对list中的每个var执行text公式 $(filter pattern...,text) # 在text中取出符合patten格式的值 $(filter-out pattern...,text) # 在text中取出不符合patten格式的值 $(wildcard pattern) # pattern定义了文件名的格式, wildcard取出其中存在的文件。 $(patsubst pattern,replacement,$(var)) #从var变量里面取出每一个值 #如果这个符合pattern格式 #把它替换成replacement格式实例:
A = a b c B = $(foreach f, $(A), $(f).o) #给A中的每一个变量添加.o后缀 C = a b c d/ D = $(filter %/, $(C)) #返回C中满足%/的变量 E = $(filter-out %/, $(C)) #返回除C中满足%/的变量的所有变量 files = $(wildcard *.c) #返回目录下满足*.c条件的所有文件名 files2 = a.c b.c c.c d.c e.c abc files3 = $(wildcard $(files2)) #返回目录下满足和file2变量中含有相同字符串文件的文件名 dep_files = $(patsubst %.c,%.d,$(files2)) #从files2变量中取出符合%.c格式的值 #将其替换成%.d格式 all: @echo B = $(B) @echo D = $(D) @echo E = $(E) @echo files = $(files) @echo files3 = $(files3) @echo dep_files = $(dep_files)执行Makefile得到如下结果:
▲Linux终端界面



