【总结】Linux环境下编译原理

程序中的头文件包含两种情况∶
    A)#include<>
    B)#include “myinc.h”
    其中,A类使用尖括号(< >),B类使用双引号(“ ”)。对于A类,预处理程序cpp在系统预设包含文件目录(如/usr/include)中搜寻相应的文件,而对于B类,cpp在当前目录中搜寻头文件,这个选项的作用是告诉cpp,如果在当前目录中没有找到需要的文件,就到指定的dirname目录中去寻找。在程序设计中,如果我们需要的这种包含文件分别分布在不同的目录中,就需要逐个使用-I选项给出搜索路径。

 

gcc程序的编译过程和链接原理

综上可见:

● 组成一个程序的每个源文件通过编译过程分别转换为目标代码。

● 每个目标文件由链接器捆绑在一起,同时引入标准C函数库中任何被该程序所用到的函数,当然也可以搜索到个人工作目录路径下的库函数。

程序在运行过程中要经过两个环境:翻译环境,执行环境。

翻译环境:源代码转换为机器指令。

执行环境:用于实际执行代码。

编译具体步骤如下:

这里写图片描述

一、C/C++文件的编译过程:

先来看一下gcc的使用方法和常用选项
提示:gcc --help

Ⅰ、使用方法:
gcc [选项] 文件名
Ⅱ、常用选项:
选项    含义
-v    查看gcc编译器的版本,显示gcc执行时的详细过程
-o <file>    Place the output into <file>;指定输出文件名为file,这个名称不能跟源文件名同名
-E    Preprocess only; do not compile, assemble or link;只预处理,不会编译、汇编、链接
-S    Compile only; do not assemble or link;只编译,不会汇编、链接
-c    Compile and assemble, but do not link; 编译和汇编,不会链接
      一个C/C++文件要经过预处理(preprocessing)、编译(compilation)、汇编(assembly)、和连接(linking)才能变成可执行文件。

① 预处理:

gcc -E -o hello.i hello.c

预处理就是将要包含(include)的文件插入原文件中、将宏定义展开、根据条件编译命令选择要使用的代码,最后将这些代码输出到一个“.i”文件中等待进一步处理。

用户可以使用gcc的选项”-E”进行查看。

② 编译:

gcc -S -o hello.s hello.i

编译就是把C/C++代码(比如上面的".i"文件)“翻译”成汇编代码。Gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,Gcc把代码翻译成汇编语言。用户可以使用”-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。

③ 汇编:

gcc -c -o hello.o hello.s

汇编就是将第二步输出的汇编代码翻译成符合一定格式的机器代码,在Linux系统上一般表现位ELF目标文件(OBJ文件)。

④ 链接:

gcc -o hello hello.o

链接就是将汇编生成的OBJ文件、系统库的OBJ文件、库文件链接起来,最终生成可以在特定平台运行的可执行程序。(arm,x86)

在这里涉及到一个重要的概念:函数库。

读者可以重新查看这个小程序,在这个程序中并没有定义”printf”的函数实现,且在预编译中包含进的”stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实现”printf”函数的呢?最后的答案是:系统把这些函数实现都被做到名为libc.so.6的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径”/usr/lib”下进行查找,也就是链接到libc.so.6库函数中去,这样就能实现函数”printf” 了,而这也就是链接的作用。

你可以用ldd命令查看动态库加载情况:

[root]# ldd hello.exe

libc.so.6 => /lib/tls/libc.so.6 (0x42000000)

/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

函数库一般分为静态库和动态库两种。静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为”.a”。动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为”.so”,如前面所述的libc.so.6就是动态库。gcc在编译时默认使用动态库。
 

总结:

在编译过程中。除非使用了"-c",“-S”,或"-E"选项(或者编译错误阻止了完整的过程),否则统一完整链接步骤。

譬如:gcc hello.c 和gcc -o hello hello.c都已经完成链接操作

二、链接原理:

gcc -c -o hello.o hello.c 不作最后一步链接,得到hello.o二进制OBJ文件

gcc -v -o hello hello.o 我们来看一样链接过程是怎样的:

  • crt1.o、crti.o、crtbegin.o、crtend.o、crtn.o是gcc加入的系统标准启动文件,对于一般应用程序,这些启动是必需的。
  • -lc:链接libc库文件,其中libc库文件中就实现了printf等函数。

行。  动态链接生成的程序体积较小,但是必须依赖所需的动态库,否则无法执行。

① 动态链接:

动态链接使用动态链接库进行链接,生成的程序在执行的时候需要加载所需的动态库才能运行。  动态链接生成的程序体积较小,但是必须依赖所需的动态库,否则无法执行。 

默认使用动态链接:gcc -o hello_shared hello.o

就是在编译的时候不直接拷贝可执行代码,而是通过记录一系列符号和参数,在程序运行或加载时将这些信息传递给操作系统,操作系统负责将需要的动态库加载到内存中,然后程序在运行到指定的代码时,去共享执行内存中已经加载的动态库可执行代码,最终达到运行时连接的目的。优点是多个程序可以共享同一段代码,而不需要在磁盘上存储多个拷贝,缺点是由于是运行时加载,可能会影响程序的前期执行性能。

优点
节省内存并减少页面交换
dll文件与exe文件独立,只需要保证接口不变,更换dll文件对exe无影响,极大地提高可维护性和可拓展性
不同的编译语言可以用同一个dll文件
适用于大规模的软件开发,是的开发过程独立,耦合度小,便于不同开发者和开发组织者之间进行开发和测试。
缺点
如果程序依赖的dll文件不存在,载入动态链接时,程序会终止并给出错误提示,运行时动态链接程序会加载失败
速度慢

② 静态链接:

静态链接使用静态库进行链接,生成的程序包含程序运行所需要的全部库,可以直接运行,不过静态链接生成的程序体积较大。

gcc -static -o hello_static hello.o

就是在编译链接时直接将需要的执行代码拷贝到调用处,优点就是在程序发布的时候就不需要的依赖库,也就是不再需要带着库一块发布,程序可以独立执行,但是体积可能会相对大一些。
优点
代码装载速度快,执行速度比动态链接快
只需要保证开发者的计算机有正确的lib文件
缺点
生成可执行文件较大,可能包含相同的代码拷贝

区别

多次调用一个外部的函数(dll文件orlib文件),静态链接可能会有多份拷贝,动态链接只有一份拷贝
静态链接的话lib文件中的指令会被包含到exe执行文件中,动态链接的话dll文件不必包含在exe执行文件中,exe可以随时动态的卸载和引用这个dll文件。
静态链接库不能再包含其他动态链接库和静态库,而在动态链接库中可以再包含其他动态链接库和静态库。
 

运行环境:

程序执行过程:

● 程序必须载入内存中。在有操作系统的环境中:一般此过程由操作系统完成。独立环境中,程序载入必须手工完成,也可能通过可执行代码置入只读内存来完成。

● 程序的执行便开始。接着调用main()函数。

● 开始执行程序代码。这个时候程序将使用一个运行时堆栈,存储函数的局部变量,函数参数,返回数据和返回的地址。同时也可以使用静态内存,存储于静态内存中的变量在程序的整个运行过程中一直保留它们的值。

● 终止程序。正常终止main函数,也可能会中途意外终止。
 

03 具体使用时

  1. Linux下,小工程可手动写Makefile,大工程用automake来帮你生成Makefile,要想跨平台,就用cmake。
  2. 如果GUI用了Qt,也可以用qmake+*.pro来管理工程,这也是跨平台的。当然,cmake中也有针对Qt的一些规则,并代替qmake帮你将qt相关的命令整理好了。

另外,需要指出的是,make和cmake主要命令只有一条,make用于处理makefile,cmake用来转译CMakeLists.txt,而qmake是一个体系,用于支撑一个编程环境,它还包含除qmake之外的其它多条命令(比如uic,rcc,moc)。

 

04 参考材料

  1. make makefile cmake qmake都是什么,有什么区别? - 辉常哥的回答 - 知乎 https://www.zhihu.com/question/27455963/answer/89770919
  2. make makefile cmake qmake都是什么,有什么区别? - 玟清的回答 - 知乎 https://www.zhihu.com/question/27455963/answer/36722992

交叉编译

交叉编译(或交叉建立)是这样一种过程,它在一种机器结构下编译的软件将在另一种完全不同的机器结构下执行。一个常见的例子是在 PC 机上为运行在基于 ARM、PowerPC或 MIPS 的目标机的编译软件。幸运的是,GCC 使得这一过程所面临的困难要比听起来小得多。

GCC 中的一般工具通常都是通过在命令行上调用命令(如 gcc)来执行的。在使用交叉编译的情况下,这些工具将根据它编译的目标而命名。例如,要使用交叉工具链为 ARM 机器编译简单的 Hello World 程序,你可以运行如下所示的命令:使用如下命令编译并测试这个代码: arm-linux-gcc -o hello hello.c。

arm-linux-gcc

arm-linux-gcc 是基于 ARM 目标机的交叉编译软件, arm-linux-gcc 跟 GCC 所需的安装包不同,但仅仅是名字不同而已,这是为什么呢?

x86 跟 ARM 所使用的指令集是不一样的,所以所需要的 binutils 肯定不一样;上面提到过 gcc-core 是依赖于 binutils 的,自然 ARM 跟 x86 所使用的 gcc-core 包也不一样;glibc 一个 c 库,最终是以库的形式存在于编译器中,自然 ARM 所使用的 glibc 库跟 x86 同样也不一样,其它的依此类推。

arm-elf-gcc

arm-elf-gcc 跟 arm-linux-gcc 一样,也是是基于 ARM 目标机的交叉编译软件。但是它们不是同一个交叉编译软件,两者是有区别的,两者区别主要在于使用不同的 C 库文件。arm-linux-gcc 使用 GNU 的 Glibc,而 arm-elf-gcc 一般使用 uClibc/uC-libc 或者使用 RedHat专门为嵌入式系统的开发的C库newlib。只是所应用的领域不同而已,Glibc是针对PC开发的,uClibc/uC-libc是与Glibc API兼容的小型化C语言库,实现了Glibc部分功能。

  • arm-linux-*针对运行linux的ARM机器,其依赖于指定的C语言库Glibc,因为同样使用Glibc的linux而使得arm-linux-*在运行linux的ARM机器上编译显得更加和谐。

  •  arm-elf-*则是一个独立的编译体系,不依赖于指定的C语言库Glibc,可以使用newlib等其他C语言库,不要求操作系统支持,当其使用为嵌入式系统而设计的一些轻巧的C语言库时编译裸机程序(没有linux等大型操作系统的程序),如监控程序,bootloader等能使得系统程序更加小巧快捷。

Qmake及makefile

1Configure(配置编译环境)
2cmake = qmake(生成makefile)
3

make (从Makefile中读取指令,然后编译。)

4make install(从Makefile中读取指令,安装程序)

 

我们知道编译和链接阶段是靠g++和gcc编辑器来完成,但是如果编译和链接的阶段如果源文件太多,一个一个编译时就会特别麻烦,于是人们想到,为什么不设计一种类似批处理的程序,来批处理编译源文件呢,于是就有了make工具,它是一个自动化编译工具,你可以使用一条命令实现完全编译。但是你需要编写一个规则文件,make依据它来批处理编译,这个文件就是makefile。
 

///详细过程///
 

1.gcc是GNU Compiler Collection(就是GNU编译器套件),也可以简单认为是编译器,它可以编译很多种编程语言(括C、C++、Objective-C、Fortran、Java等等)。

2.当你的程序只有一个源文件时,直接就可以用gcc命令编译它。

3.但是当你的程序包含很多个源文件时,用gcc命令逐个去编译时,你就很容易混乱而且工作量大

4.所以出现了make工具
make工具可以看成是一个智能的批处理工具,它本身并没有编译和链接的功能,而是用类似于批处理的方式—通过调用makefile文件中用户指定的命令来进行编译和链接的。

5.makefile是什么?简单的说就像一首歌的乐谱,make工具就像指挥家,指挥家根据乐谱指挥整个乐团怎么样演奏,make工具就根据makefile中的命令进行编译和链接的。

6.makefile命令中就包含了调用gcc(也可以是别的编译器)去编译某个源文件的命令。

7.makefile在一些简单的工程完全可以人工手下,但是当工程非常大的时候,手写makefile也是非常麻烦的,如果换了个平台makefile又要重新修改。

8.这时候就出现了Cmake这个工具,cmake就可以更加简单的生成makefile文件给上面那个make用。当然cmake还有其他功能,就是可以跨平台生成对应平台能用的makefile,你不用再自己去修改了。

9.可是cmake根据什么生成makefile呢?它又要根据一个叫CMakeLists.txt文件(学名:组态档)去生成makefile。

10.到最后CMakeLists.txt文件谁写啊?亲,是你自己手写的。

11.当然如果你用IDE,类似VS这些一般它都能帮你弄好了,你只需要按一下那个三角形

12.cmake是make maker,生成各种可以直接控制编译过程的控制器的配置文件,比如makefile、各种IDE的配置文件。
13.make是一个简单的通过文件时间戳控制自动过程、处理依赖关系的软件,这个自动过程可以是编译一个项目。

/linux平台下的编译流程

  文本程序到可执行文件生成无论在什么平台大致分为以下几个部分: 
  1.用编辑器编写源代码,如.c文件。 
  2.用编译器编译代码生成目标文件,如.o。 
  3.用链接器连接目标代码生成可执行文件,如.exe。 
  Linux平台下,.o文件一般是通过编译的但还未链接的目标文件,.out文件一般都是经过相应的链接产生的可执行文件(linux下)。当然这是一般情况下人们这么设置,而真正的,在linux中 .o通常保存的是可执行代码 ,至于可执行文件则没有规定扩展名,用的是文件属性位来决定的是否可执行。在chmod中设置。 
  我们知道编译和链接阶段是靠g++和gcc编辑器来完成,这两个编译阶段是相同的,但是链接阶段g++默认链接c++库,所以一般情况下用gcc编译c文件,而g++编译cpp文件。当然g++也可以编译c文件,而gcc编译cpp文件则需要在后面加上参数-lstdc++,作用就是链接c++库。 
  但是如果编译和链接的阶段如果源文件太多,一个一个编译时就会特别麻烦,于是人们想到,为什么不设计一种类似批处理的程序,来批处理编译源文件呢,于是就有了make工具,它是一个自动化编译工具,你可以使用一条命令实现完全编译。但是你需要编写一个规则文件,make依据它来批处理编译,这个文件就是makefile,所以编写makefile文件也是一个程序员所必备的技能。 
  对于一个大工程,编写makefile实在是件复杂的事,于是人们又想,为什么不设计一个工具,读入所有源文件之后,自动生成makefile呢,于是就出现了cmake工具,它能够输出各种各样的makefile或者project文件,从而帮助程序员减轻负担。但是随之而来也就是编写cmakelist文件,它是cmake所依据的规则。所以在编程的世界里没有捷径可走,还是要脚踏实地的。 
  原文件—cmakelist —cmake —makefile —make —生成可执行文件(make中则包含了多条链接以及gcc/g++编译语句)。 
 
 

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux 应用编程是指在 Linux 操作系统上开发应用程序的过程。 Linux 操作系统是一种开源的操作系统,支持多种编程语言,如 C, C++, Python, Java 等。 在 Linux 上开发应用程序的过程包括以下几个步骤: 1. 安装必要的开发工具: 在 Linux 上开发应用程序,需要安装一些必要的开发工具,如编译器、调试器、版本控制系统等。 2. 选择合适的编程语言: Linux 支持多种编程语言,可以根据自己的喜好选择合适的语言进行开发。 3. 编写代码: 根据应用程序的功能和需求,编写代码。 4. 编译代码: 使用编译器将代码编译成可执行文件。 5. 调试代码: 如果程序中存在错误或者 bug,可以使用调试器来查找并修复问题。 6. 打包应用程序: 将应用程序打包成可以在其他计算机上安装的格式,方便分发和使用。 ### 回答2: Linux 应用编程指的是在Linux操作系统下开发和编写应用程序的过程。Linux作为一个开源的操作系统,拥有广泛的应用领域,包括服务器、嵌入式系统、移动设备等,因此掌握Linux应用编程有着重要的意义。 首先,Linux应用编程需要熟悉Linux操作系统的特性和架构。Linux的核心理念是多任务、多用户的设计哲学,因此在编写应用程序时需要充分利用Linux的进程、线程、信号等机制,实现程序的并发执行和资源的合理管理。 其次,Linux提供了丰富的系统和网络编程接口,使得应用程序可以直接访问底层的系统资源和网络功能。例如,可以利用Linux提供的文件操作接口读写文件,使用套接字编程实现网络通信,调用系统调用实现进程间通信等。 另外,Linux应用编程还需要掌握一些常用的开发工具和库。例如,用C/C++编写应用程序时,可以使用GNU工具链来进行编译和调试;可以使用标准库和第三方库来简化开发过程。此外,还可以使用Shell脚本编写一些自动化任务,提高开发效率。 在开发过程中,还需要注意Linux的安全性和权限管理。Linux提供了严格的权限控制机制,要求应用程序必须拥有适当的权限才能执行某些操作。因此,在编写应用程序时要遵循最小权限原则,以及避免一些常见的安全漏洞。 总而言之,Linux应用编程是一门有挑战性的技术,需要掌握Linux操作系统的原理和特性,熟悉系统和网络编程接口,以及使用相关的开发工具和库。掌握了这些知识和技能,可以为Linux平台上的应用程序开发和优化提供支持,提高开发效率和程序性能。 ### 回答3: Linux 应用编程是指在Linux操作系统上进行软件开发和编程的过程。Linux是一种开源的操作系统,它提供了丰富的开发工具和库,使得开发人员可以轻松地编写各种应用程序。 Linux 应用编程可以分为两个主要方面:系统编程和应用编程。 系统编程是指与操作系统直接进行交互,使用系统调用和库函数来开发底层的系统功能。如文件和进程管理,网络编程,设备驱动程序等。系统编程需要深入了解操作系统的内部工作原理和相关概念。 应用编程是指开发各种应用程序,例如桌面应用、服务器应用、嵌入式应用等。开发者可以使用各种编程语言如C、C++、Python等来进行应用编程,在Linux操作系统上构建功能丰富、高效稳定的应用程序。Linux提供了众多开发工具和库,如GTK+、Qt等图形界面开发库,以及MySQL等数据库工具,方便开发者进行应用程序开发。 Linux应用编程的好处有很多。首先,Linux是开源的,开发者可以使用自己的方式自由定制和修改系统。这使得开发者能够更好地理解系统和进行调试。其次,Linux提供了丰富的开发工具和库,使得开发者可以轻松地编写各种应用程序。再次,Linux系统的稳定性和高效性使得开发的应用程序能够在一个可靠的环境下运行。 总结来说,Linux应用编程是基于Linux操作系统进行软件开发和编程的过程。通过系统编程和应用编程,开发者可以构建功能丰富、高效稳定的应用程序。Linux的开源特性和丰富的开发工具使得开发者能够更好地掌握和使用系统,从而开发出更好的应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值