GCC程序编译
GCC (GNU C Compiler) 编译器,GNU 本身是一个计划,目标是开发出一套完全免费的操作系统,GCC就是他推出很好的多平台编译器,不管是嵌入式应用程序开发 还是做驱动开发内核开发 嵌入式内核开发 都需要用到它,用它可以编译链接C C++等程序,
GCC 支持的体系结构有40余种,常见的有X86 ARM POWERPC 等等同时GCC还能运行在不同的操作系统中,如LINUX WINDOWS Solaris 等,GCC除支持C语言外还支持多种语言,如C++ ,Ada,Java 等
在LINUX系统中,可执行的文件没有统一的后缀的,系统从文件的属性(x r w )来区分可行文件 和 不可执行文件
GCC编译程序时,编译过程可以分为四个阶段:预处理 编译 汇编(Assembling)链接
预处理:这个阶段 主要处理源文件中的 #ifdef #include #define 等命令,这个中间阶段会生产一个 *.i文件 实际工作通常不会专门生产这种文件, gcc -E test.c -o test.i
编译:这个阶段把预处理后的结果编译成汇编或者目标模块,输入的是中间文件 *.i,编译后生产的是 汇编语言文件 *.s 这个阶段对应的GCC命令如下所示。
汇编:这个阶段把编译出来的结果汇编成具体的CPU上的目标代码模块,输入的是汇编文件*.s,输出的是机器语言*.o,*o这个也叫做目标文件,这个文件虽然也是机器代码,但是不可执行, gcc -c test.s -o test.o
链接:这个阶段 把多个目标代码模块连接生产一个大的目标模块,输入的是机器代码文件*.o 还有其他的机器代码文件和库文件 输出的是一个可执行的二进制代码文件 gcc test.o -o test
gcc -o test first.c second.c third.c 该命令同时编译三个源文件 再将它们连成一个可执行的程序test( 这里不论是一个原文件还是多个原文件 被编译和连接的原文件中必须有且仅有一个main函数)
GCC 通过后缀来区分输入文件的类别,.c .a(库文件) .C( .cc .cxx C++源文件) .h .l(经过预处理的的C源文件) .ii(经过预处理的的C++源文件) .o(编译后的目标文件).s (汇编源文件) .S(经过预编译的汇编源代码文件)
GCC的使用
GCC [options] [filenames] ,options 编译器所需要的编译选项,gcc大约有100多个编译选项, filenames要编译的文件名
介绍编译选项:
-D MACRO 定义宏 等于在程序中使用#define MACRO “-DMODULE -D__KERNEL__ -DLINUX” 其实是GCC命令行的用法,等效于在一个头文件中定义:#define MODULE #define __KERNEL__ #define LINUX
-pipe 在编译过程中 生产各种临时文件
-o output_filename 确定可执行文件的名称为output_filename 默认是a.o(ut)
-v 把整个过程的输出信息都打印出来
-E 输出预处理的结果,不进行编译 汇编 和 连接
-C(大写) 配合 -E使用 让预处理后的结果把注释保留 以方便阅读
-S 只将源文件编译成汇编代码,不进行汇编 和 连接
-c 只编译 不连接成为可执行文件 编译器只是由输入的.c等代码文件生成.o后缀的目标文件
-g 产生调试工具(GNU 的gdb)所必要的符号信息,要想对编译出的程序进行调试,就必须加入这个选项,也就是把调试开关打开
-O 对程序进行优化编译,链接
-O2 优化程度更深一些 ,优化后的程序 执行时候,要的时间比没有优化的要小,运行程序时 可以time 来计算程序的运行时间 time ./outimize(程序名)
–I 当我们在编译一个程序的时候,包含了一个头文件假设叫做<a.h>,而a.h没有在include这个目录中,一个我们可以把这个头文件拷到这个目录中,另一个我们可以用指令来添加一个头文件的路径,可以有多个 -I 来指定过个路径 ,如指定 gcc –I (大写艾) gcc –I/home/lesson/part/3(头文件所在的目录) 编译的文件名 –o 输出的文件名
-L 添加库的 同理-I 搜寻库文件( *.a )的路径 ( -Ldir )
-l(小艾尔) 在链接时 GCC默认链接C库,其他不会链接的,如果你要用到其他的库如数学库,你要先指明,或者你自己的库,如数学库是libm.a 我们写时可以省略掉前面lib 与后面的.a 他会自动加 写成 –lm 就行 gcc foo.c –L/home.lib –lfoo –o foo
-static 静态链接库文件 静态链接区别于动态链接:
Gcc 默认采用动态链接,.so为动态链接库 .a为静态库,静态链接时,连接器找出程序所需的函数,然后将他们拷贝到可执行文件中,组成一个文件,一旦链接成功,这个静态库也就不需要了,而动态链接,是在程序内留下一个标记指明当程序执行时首先必须载入这个库,动态链接节省空间,gcc –static hello.c –o hello
-wall 生产所以警告信息 推荐大家使用这个选项
-w(小) 不生产任何警告信息
-D MACRO 定义宏 等于在程序中使用#define MACRO
///////////////////////////////////////////////////////////
LINUX 2.4 内核文件 包括头文件 大概有一万个 2.6大概有2万个
Linux 程序员 必须学会使用GNU make来构建和管理 自己的软件工程,GNU的make 能够使整个软件工程的编译链接只需要一个命令就可以完成。Make 只是一个命令,在执行时,需要一个命名为makefile的文件,makefile 文件描述了整个工程的编译连接等的规则,包括工程中的哪些源文件需要编译以及如何编译,需要创建那些库文件,以及如何创建这些库文件,如何最后产生我们想要得可执行文件
在大型的开发项目中,通常有几十到上百个的源文件,如果每次均手工键入 gcc 命令进行编译的话,则会非常不方便。因此,人们通常利用 make 工具来自动完成编译工作。这些工作包括:如果仅修改了某几个源文件,则只重新编译这几个源文件;如果某个头文件被修改了,则重新编译所有包含该头文件的源文件。利用这种自动编译可大大简化开发工作,避免不必要的重新编译。实际上,make 工具通过一个称为 makefile 的文件来完成并自动维护编译工作。makefile 需要按照某种语法进行编写,其中说明了如何编译各个源文件并连接生成可执行文件,并定义了源文件之间的依赖关系。当修改了其中某个源文件时,如果其他源文件依赖于该文件,则也要重新编译所有依赖该文件的源文件。makefile 文件是许多编译器,包括 Windows NT 下的编译器维护编译信息的常用方法,只是在集成开发环境中,用户通过友好的界面修改 makefile 文件而已。默认情况下,GNU make 工具在当前工作目录中按如下顺序搜索 makefile:我的2.6.18的内核默认的呃是 Makefile 而不是makefile
* GNUmakefile
* makefile
* Makefile
在 UNIX 系统中,习惯使用 Makefile 作为 makfile 文件。如果要使用其他文件作为 makefile,则可利用类
似下面的 make 命令选项指定 makefile 文件:
$ make -f Makefile.debug
-----------------------------------------------------------------------
makefile 基本结构
makefile 中一般包含如下内容:
* 需要由 make 工具创建的项目,通常是目标文件和可执行文件。通常使用“目(target)”一词来表示要创建的项目。
* 要创建的项目依赖于哪些文件。
* 创建每个项目时需要运行的命令。
Makefile里面包含了很多条规则:规则用于说明如何生存一个或者多个目标文件,描述的是在什么情况下,如何重建规则的目标文件 通常规则包含 目标的依赖关系 和重建的目标命令
一般规则的格式是:
目标:依赖
命令
一个规则可以有多个命令行,每一条命令占一行,每一个命令行必须以TAB字符开始,TAB字符告诉make此行是一个命令行,make按照命令完成相应的动作
# This makefile just is a example. #表注释
//////////////////////////////////////////////////
edit: main.o kbd.o command.o
insert.o ....
(TAB)cc -o edit main.o kbd.o command.o
insert.o ....
main.o: main.c defs.h
(TAB)cc -c main.c
insert.o:insert.c dfs.h
(TAB)cc -c insert.c
.......
////////////上面写法不好 因为不方便更新 改动/////////////////
obj = main.o kbd.o command.o
insert.o .....
edit: $(obj)
cc -o edit $(obj)
//////////////////make 的编译隐含规则/////////////////////////
编译时make会自动为这个.o文件寻找合适的依赖文件(对应得.c文件 对应是指除了后缀 其余部分都相同的两个文件)同时使用正确的命令来建立这个目标文件 这样写规则时 可以省略掉命令行 同时也可以省略掉 与*.o同名的C文件 只要在依赖关系中给出 包含的各个头文件 就可以
obj = main.o kbd.o command.o
insert.o .....
edit: $(obj)
cc -o edit $(obj)
main.o:defs.h
insert.o:dfs.h
/////////////////////另一类根据依赖关系分类法/////////////////////
obj = main.o kbd.o command.o
insert.o .....
edit: $(obj)
cc -o edit $(obj)
$(obj):defs.h
main.o kbd.o command.o:d.h
insert.o command.o:k.h
//////////////////////////////////////////////////
在makefile中规则的顺序是很重要的,因为makefile中只应该有一个最终目标,其他的目标都是被这个目标所连带出来的,所以一定要让make知道你的最终目标是什么,makefile中目标可能有很多,如果没有指定,默认第一条规则的目标将被确立为最终的目标。
当在shell中输入 make命令后,make将读取当前目录下 的Makefile 文件,并将Makefile中的第一个目标做为其执行的“终极目标”,开始处理第一个规则“终极目标”,处理这个过程 先处理他的依赖条件 根据这个条件 做出相应得动作 对后面的文件
Makefile中 把那些没有任何依赖只有执行动作的目标称为“伪目标”,phony targets
.PHONY:clean PHONY指明clean为一个伪目标
Clean:
Rm –f hello …
-------------------------------------------------------------------------
makefile 变量
GNU的make 工具有许多便于表达依赖性关系以及建立目标的命令的特色。其中之一就是变量或宏的定义能力。如果你要以相同的编译选项同时编译十几个 C 源文件,而为每个目标的编译指定冗长的编译选项的话,将是非常乏味的。但利用简单的变量定义,可避免这种乏味的工作:
# Define macros for name of compiler
CC = gcc
# Define a macr o for the CC flags
CCFLAGS = -D_DEBUG -g -m486
# A rule for building a object file
test.o: test.c test.h
$(CC) -c $(CCFLAGS) test.c
在上面的例子中,CC 和 CCFLAGS 就是 make 的变量。GNU make 通常称之为变量,而其他 UNIX 的 make
工具称之为宏,实际是同一个东西。在 makefile 中引用变量的值时,只需变量名之前添加 $ 符号,如
上面的 $(CC) 和 $(CCFLAGS)。
GNU make 的主要预定义变量
GNU make 有许多预定义的变量,这些变量具有特殊的含义,可在规则中使用。下面给出了一些主要的
预定义变量,除这些变量外,GNU make 还将所有的环境变量作为自己的预定义变量。
预定义变量 含义
$* 不包含扩展名的目标文件名称。
$+ 所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件。
$< 第一个依赖文件的名称。
$? 所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚。
$@ 目标的完整名称。$@代表目标
$^ 所有的依赖文件,以空格分开,不包含重复的依赖文件。
$% 如果目标是归档成员,则该变量表示目标的归档成员名称。例如,如果目标名称
为 mytarget.so(image.o),则 $@ 为 mytarget.so,而 $% 为 image.o。
AR 归档维护程序的名称,默认值为 ar。
ARFLAGS 归档维护程序的选项。
AS 汇编程序的名称,默认值为 as (编译器)。
ASFLAGS 汇编程序的选项。
CC C 编译器的名称,默认值为 cc(编译器)。
CCFLAGS C 编译器的选项。
CPP C 预编译器的名称,默认值为 $(CC) -E。
CPPFLAGS C 预编译的选项。
CXX C++ 编译器的名称,默认值为 g++。
CXXFLAGS C++ 编译器的选项。
FC FORTRAN 编译器的名称,默认值为 f77。
FFLAGS FORTRAN 编译器的选项。
//////////////////////////////////////////////////////////////////
我们系统中装了gcc编译器,功能强大,但我们在编译一个文件时 即可用cc 也可以用gcc 他们的关系是
Linux CC与Linux GCC的区别概括介绍。从名字上看,老的unix系统的CC程序叫做C Compiler。但GCC这个名字按GNU的说法叫做Gnu Compiler Collection。因为gcc包含很多编译器(C, C++, Objective-C, Ada, Fortran,and Java)。所以它们是不一样的,一个是一个古老的C编译器,一个是编译器的Gnu的编译器的集合(Gcc里的C编译器比CC强大太多了,所以你没必要用CC)。当你调用gcc时不一定是调用的C/C++编译器,是gcc根据文件扩展名自动识别并调用对应的编译器,具体可查阅$man gcc。
你是下载不到CC的,原因是:CC来自于昂贵的Unix系统,CC是商业软件,要想用你需要打电话,写订单,而不是打开你的Browser去download。
linux下的cc是gcc的符号链接。可以通过$ls –l /usr/bin/cc来简单察看.而编译时看到的控制台输出CC则是一个指向gcc的变量,该变量是make程序的内建变量,就算你在Makefile中没有CC= ,该变量也会存在,并默认指向gcc。cc的符号链接和变量存在的意义在于源码的移植性,可以方便的用GCC来编译老的用cc编译的unix软件,甚至连Makefile都不要改。而且也便于linux程序在unix下编译。
近几年的一个新情况是越来越多的unix用户,据我所知像solaris,bsd用户也不太使用CC了,人们都一定要装一个gcc,用它来编译C/C++程序。原因显而易见,gcc足够强大,健壮。支持估计目前为止只有它支持的ISO c/c++ 新特性。当然你最好不要使用night版本的gcc。
make是用来编译的,它从Makefile中读取指令,然后编译。
make install是用来安装的,它也从Makefile中读取指令,安装到指定的位置,make install 就是读取makefile文件中 install:对应得语句 类似于 make clean
Make是一个解释Makefile文件中指令的命令工具,其最基本的功能就是通过Makefile文件来描述源程序之间的相互关系并自动维护编译工作,它会告知系统以何种方式编译和链接程序。一旦确定完成Makefile文件,剩下的工作就只是在Linux终端下输入make这样的一个命令,就可以自动完成所有的编译任务,并生成目标程序。工程情况下GNU make的工作流程如下。
1、查找当前目录下的Makefile文件
2、初始化文件中的变量
3、分析Makefile中的所有规则
4、为所有的目标文件建立依赖关系
5、根据依赖关系,决定哪些目标文件要重新生成
6、执行生成命令
//////////////////////////////////////////////////////////////////
隐含规则
GNU make 包含有一些内置的或隐含的规则,这些规则定义了如何从不同的依赖文件建立特定类型的目标。
GNU make 支持两种类型的隐含规则:
1: 后缀规则(Suffix Rule)。后缀规则是定义隐含规则的老风格方法。后缀规则定义了将一个具有某个
后缀的文件(例如,.c 文件)转换为具有另外一种后缀的文件(例如,.o 文件)的方法。每个后缀规
则以两个成对出现的后缀名定义,例如,将 .c 文件转换为 .o 文件的后缀规则可定义为:
.c.o:
$(CC) $(CCFLAGS) $(CPPFLAGS) -c -o $@ $<
2:模式规则(pattern rules)。这种规则更加通用,因为可以利用模式规则定义更加复杂的依赖性规则。
模式规则看起来非常类似于正则规则,但在目标名称的前面多了一个 % 号,同时可用来定义目标和依赖
文件之间的关系,例如下面的模式规则定义了如何将任意一个 X.c 文件转换为 X.o 文件:
%.c:%.o
$(CC) $(CCFLAGS) $(CPPFLAGS) -c -o $@ $<
------------------------------------------------------------------------------
运行 make
我们知道,直接在 make 命令的后面键入目标名可建立指定的目标,如果直接运行 make,则建立第一个
目标。我们还知道可以用 make -f mymakefile 这样的命令指定 make 使用特定的 makefile,而不是
默认的 GNUmakefile、makefile 或 Makefile。但 GNU make 命令还有一些其他项,下面列举了一些make的命令行参数选项
命令行选项 含义
-C DIR 在读取 makefile 之前改变到指定的目录 DIR。
-f FILE 以指定的 FILE 文件作为 makefile。
-h 显示所有的 make 选项。
-i 忽略所有的命令执行错误。
-I DIR 当包含其他 makefile 文件时,可利用该选项指定搜索目录。
-n 只打印要执行的命令,但不执行这些命令。
-p 显示 make 变量数据库和隐含规则。
-s 在执行命令时不显示命令。
-w 在处理 makefile 之前和之后,显示工作目录。
-W FILE 假定文件 FILE 已经被修改。
------------------------------------------------------------------------------
变量 如果我们要为一个目标添加一个依赖 用变量就方便 只要修改变量赋值处就行
如给hello 添加一个依赖 func3.o
我们可以两种方法修改:
1: hello:main.o func1.o func3.o
Gcc main.o func1.o func3.o –o hello
2: 用变量 obj=main.o func1.o func3.o
Hello:$(obj) gcc $(obj) –o hello
Makefile 中系统默认的自动化变量
$^ 代表所有的依赖文件
$<代表第一个依赖文件
如: hello:main.o func1.o func2.o
Gcc main.o func1.o func2.o –o hello
è Hello: main.o func1.o func2.o
è Gcc $^ -o $@
Makefile 中 #后面的内容视为注释
@取消回显
!!!!!!!!!!!!!!
特别注意:
在编写makefile时 写命令时 前面的空白不是空格键 而是一个tab键 注意了 ,同时 用于分解多行的反斜线""后面不能有空格 这些很容易就犯错
gcc前一定要 有一个tab分隔符,不能有空格;否则会出现“makefile:2: *** 遗漏分隔符 make中规定每一Shell命令之前的开头必须使用字符 当时第九行处我是顶格写的 所以不对 rm....所以在开头处加了一个TAB制表符 就OK了#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
static int hello_init(void)
{
printk(KERN_ALERT "hello module init
");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "hello module exit
");
}
module_init(hello_init);
module_exit(hello_exit);
//////////////////////////////////////
ifneq ($(KERNELRELEASE),)
obj-m := hello.o
else
KERNELDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
clean:
rm -f *.ko *.mod.c *.mod.o *.o
编译模块
#make
清除
#make clean
/////////////////////说明如下/////////////////////////
hello.c文件中调用的头文件
init.h中的module_init(),module_exit()
kernel.h中的printk(),KERN_ALERT
module.h中的MODULE_LICENSE()
Makefile文件中的核心
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
1)-C $(KERNELDIR)
表示在$(KERNELDIR)目录下执行make命令。 编译内核模块时需要依赖内核代码的
2)M=$(PWD) 表示当前目录
表示包含$(PWD)下的Makefile文件。
3)modules
表示模块编译。
4)用到了ifneq...else...endif语句
由于开始还没定义KERNELRELEASE,所以只能执行else分支。
而在执行
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
后,会在内核的Makefile中定义KERNELRELEASE,当再次进入本Makefile时,
则只会执行ifneq的第一个分支,即
obj-m := hello.o
这一句话是非常重要的。事实上,这个Makefile做的本份工作就是它