您的位置:首页 > 运维架构 > Linux

苏嵌嵌入式Linux实训 第3天

2020-07-14 06:34 127 查看

嵌入式学习第三次课程,由梁老师讲述,本次的课程正式进入虚拟机进行编程和试写,首先让我们了解的是gcc、gbd、make等用法,介绍相关的命令和用法,让我们自己动手训练和练习,从实践中学到一点一滴。

1、课程内容

本次课程主要介绍编译器-gcc、调试器-gbd、工程管理器-make(脚本-shell,自己了解)等相关命令的作用和用法,通过一遍一遍的演示和编程,让我们了解和应用,同时给予充分的时间去练习去操作。

gcc:GNU CC (简称为gcc)是GNU项目中符合ANSI C标准的编译系统,能够编译用C、C+ +和Object C等语言编写的程序。gcc不仅功能强大,而且可以编译如C、C++。Object C、Java、和Ada等多种语言,而且gcc又是一个交叉平台编译器,它能够在当前CPU平台.上为多种不同体系结构的硬件平台开发软件,因此尤其适合在嵌入式领域的开发编译。

gbd:GBD是GNU开源组织发布的一个强大的UNIX下的程序调试工具。与Window下的IDE不同,GDB是纯命令行执行的,并没有图形界面方法。

make: make工程管理器也就是个“自动编译管理器”,这里的“自动”是指它能构根据文件时间戳自动发现更新过的文件而减少编译的工作量,同时,它通过读入Makefile文件的内容来执行大量的编译工作。

shell:Shell既是一种命令语言,又是一种程序设计语言。用户既可以输入命令执行,又可以利用 Shell脚本编程,完成更加复杂的操作。在Linux GUI日益完善的今天,在系统管理等领域,Shell编程仍然起着不可忽视的作用。

2、操作

应用gcc、gbd、make等相关命令

3、问题描述

  • 源文件到可执行文件经历哪几个步骤?

        预处理——>编译——>汇编——>链接

  • 上面每个步骤具体完成了哪些工作?

预处理:根据以字符#开头的命令,修改原始的C程序。比如:hello.c中的第1行#include<stdio.h>命令告诉预处理器读取系统头文件stdio.h的内容,并把它直接插入到程序文本中。结果得到了另一个C程序,通常以.i作为文件扩展名。像头文件展开、宏替换、#ifndef#else#end if(条件编译)

编译:将预处理文件编译生成汇编文件.s     语法检查、词法分析

汇编:将汇编文件编译生成目标文件.o

链接:将目标文件编译生成可执行文件       符号表的建立

  • 《#include<stdio.h>VS#include "stdio.h"》

用尖括号 #include <>:一般用于包含标准的库头文件,编译器会去系统配置的库环境变量和者用户配置的路径去搜索,而不会在项目的当前目录去查找
用双引号 #include "":一般用于包含用户自己编写的头文件,编译器会先在项目的当前目录查找,找不到后才会去系统配置的库环境变量和用户配置的路径去搜索
<>:仅在系统的头文件目录搜索/usr/include
"":现在当前目录查找,找不到,再去系统头文件目录查找

  • 什么是交叉编译?为什么需要交叉编译?

(CPU都有不同的汇编指令)-通过交叉编译来改变
在当前CPU平台下编译出在其他CPU平台运行的可执行文件

  • ##静态库和动态库##

(一)库文件的作用?
仓库:保存函数和变量
特点:只能使用不能看到其实现

(二)Linux库文件的分类:         文件保存到/usr/lib  /lib
静态库.a:在编译时加载;将需要的函数或者变量,在编译过程中将其从静态库中搬移到源文件
动态库.so:在运行时加载;将需要的函数或者变量,在运行过程中需要什么搬迁什么

(三)“使用动态库和使用静态库生成的可执行文件的区别(优缺点)”
大小:静态库>动态库 内存效率
运行速度:静态库>动态库 节省了运行时间
编译速度:静态库<动态库
升级的方便程度:静态库<动态库
程序的部署方便程度:静态库>动态库

4、问题解答

作业1:编译器三级优化分别优化了那些?

GNU编译器提供-O选项供程序优化使用:
-O 提供基础级别的优化
-O2 提供更加高级的代码优化,会占用更长的编译时间
-O3 提供最高级的代码优化
-O4 不优化,这是默认值

  • 第一级:代码调整

代码调整是一种局部的思维方式;基本上不触及算法层级;它面向的是代码,而不是问题; 所以:语句调整,用汇编重写、指令调整、换一种语言实现、换一个编译器、循环展开、参数传递优化等都属于这一级;这个级别试图执行9种单独的优化功能:

-fdefer-pop:一般情况下, 函数的输入值被保存在堆栈中并且被函数访问。 函数返回时, 输入值还在堆栈中。 
-fmerge-constans:使用这种优化技术, 编译器试图合并相同的常量。
-fthread-jumps:通过一连串跳转, 编译器确定多个跳转之间的最终目标并且把第一个跳转重新定向到最终目标。
-floop-optimize:通过优化如何生成汇编语言中的循环, 编译器可以在很大程序上  提高应用程序的性能。
-fif-conversion: if-then:简单的if-then语句可能在最终的汇编语言代码中产生众多的条件分支。 通过减少  或者删除条件分支, 以及使用条件传送 设置标志和使用运算技巧来替换他们, 编译  器可以减少if-then语句中花费的时间量。
-fif-conversion2:这种技术结合更加高级的数学特性, 减少实现if-then语句所  需的条件分支。
-fdelayed-branch:这种技术试图根据指令周期时间重新安排指令。
-fguess-branch-probability:这种技术试图确定条件分支最可  能的结果, 并且相应的移动指令, 这和延迟分支技术类似。
-fcprop-registers:因为在函数中把寄存器分配给变量, 所以编译器执行第二次检查以便减少调度依赖性并且删除不必要的寄存器复制操作。

  • 第二级:新的视角

新的视角强调的重点是针对问题的算法;即选择和构造适合于问题的算法;(冒泡排序还是快排的选择问题是这一级早就应该完成的)很多经典算法都对问题作了一些假设(包括我们当前已经完成的算法实现),而在面对实际问题时“新的视角”提示我们应该重新检视这些假设,并尝试不同的思考问题的角度,寻求适合于问题的新算法; 编译器还试图采用以下几种优化。
-fforce-mem:再任何指令使用变量前, 强制把存放再内存位置中的所有变量都复制到寄存器中。
-foptimize-sibling-calls:处理相关的和/或者递归的函数调用。
-fstrength-reduce:对循环执行优化并且删除迭代变量。
-fgcse:对生成的所有汇编语言代码执行全局通用表达式消除历程。 
-fcse-follow-jumps:通用子表达式消除技术扫描跳转指令, 查找程序中通过任何其他途径都不 
会到达的目标代码。
-frerun-cse-after-loop:对任何循环已经进行过优化之后重新运行通用子表达式消除例程。
-fdelete-null-pointer-checks:扫描生成的汇编语言代码, 查找检查空指针的代码。
-fextensive-optimizations:这种技术执行从编译时的角度来说代价高昂的各种优化技术,但是它可能对运行时的性能产生负面影响。
-fregmove:编译器试图重新分配mov指令中使用的寄存器, 并且将其作为其他指令操作数, 以便最大化  捆绑的寄存器的数量。
-fschedule-insns:编译器将试图重新安排指令, 以便消除等待数据的处理器。
-fsched-interblock:使编译器能够跨越指令块调度指令。
-fcaller-saves:指示编译器对函数调用保存和恢复寄存器, 使函数能够访问寄存器值, 而且不必  保存和恢复他们。
-fpeephole2:这个选项允许进行任何计算机特定的观察孔优化。
-freorder-blocks:这种优化技术允许重新安排指令块以便改进分支操作和代码局部性。
-fstrict-aliasing:强制实行高级语言的严格变量规则。
-funit-at-a-time:指示编译器在运行优化例程之前读取整个汇编语言代码。 
-falign-functions:使函数对准内存中特定边界的开始位置。
-fcrossjumping:对跨越跳转的转换代码处理, 以便组合分散在程序各处的相同代码。

  • 第三级:表驱动状态机

将问题抽象为另一种等价的数学模型或假想机器模型,比如构造出某种表驱动状态机;这一级其实是第二级的延伸,只是产生的效果更加明显,但它有其本身的特点(任何算法和优化活动都可以看作是他的投影);这一级一般可以产生无与伦比的快速程序, 要达到这一级需要大量修炼的;并且思考时必须放弃很多已有的概念或者这些概念不再重要,比如:变量、指针、空间、函数、对象等,剩下的只应该是那个表驱动状态机; 我想把这种境界描述为:空寂中,一些输入驱动着一个带有状态的机器按设定好的最短路线运转着;除此之外have nothing; 既:把解决一个问题的算法看作一个机器,它有一些可变的状态、有一些记忆、有一些按状态运行的规则,然后一些输入驱动这个机器运转;这就是第三级要求的思考优化问题的切入点,也就是寻找一部机器,使它运行经过的路径最短(可能是速度也可能是空间等等) 。编译器还试图采用以下这种优化方式。
-finline-functions:不为函数创建单独的汇编语言代码,而是把函数代码包含在调度程序的  代码中。
-fweb:构建用于保存变量的伪寄存器网络。
-fgcse-after-reload:在完全重新加载生成的且优化后的汇编语言代码之后执行第二次gcse优化,  帮助消除不同优化方式创建的任何冗余段。

作业二:总结gcc静态库和动态库的制作?

静态库:
制作步骤主要以下几步:
1)根据源文件使用gcc -c 自动生成.o文件
gcc -c *.c -I …/include
2)使用ar命令将.o文件归档
ar rcs libmylib.a *.o
动态库:
制作步骤主要以下几步:
1)使用-fpic参数将源文件编译成.o文件
gcc *.c -c -fpic
2)使用gcc -shared打包
gcc -shared *.o -o libmylib.so

作业三:总结CMake用法

  • 基本使用

安装:下载二进制包后可直接解压使用

从源码安装则执行命令:./bootstrap; make; make install

使用:cmake dir_path,生成工程文件或makefile文件

  • 概念

out-of-source build,与in-source build相对,即将编译输出文件与源文件放到不同目录中;

  • 基本结构

1,依赖CMakeLists.txt文件,项目主目标一个,主目录中可指定包含的子目录;

2,在项目CMakeLists.txt中使用project指定项目名称,add_subdirectory添加子目录

3,子目录CMakeLists.txt将从父目录CMakeLists.txt继承设置

  • 语法

1.       #注释

2.       变量:使用set命令显式定义及赋值,在非if语句中,使用${}引用,if中直接使用变量名引用;后续的set命令会清理变量原来的值;

3.       command (args ...)  #命令不分大小写,参数使用空格分隔,使用双引号引起参数中空格

4.       set(var a;b;c) <=> set(var a b c)  #定义变量var并赋值为a;b;c这样一个string list

5.       Add_executable(${var}) <=> Add_executable(a b c)   #变量使用${xxx}引用

6.       条件语句:if(var)

else()/elseif() … endif(var)

7.       循环语句

Set(VAR a b c)

Foreach(f ${VAR})       …Endforeach(f)

8.       循环语句

WHILE() … ENDWHILE()

  • 内部变量

CMAKE_C_COMPILER:指定C编译器

CMAKE_CXX_COMPILER:

CMAKE_C_FLAGS:编译C文件时的选项,如-g;也可以通过add_definitions添加编译选项

EXECUTABLE_OUTPUT_PATH:可执行文件的存放路径

LIBRARY_OUTPUT_PATH:库文件路径

CMAKE_BUILD_TYPE::build 类型(Debug, Release, ...),CMAKE_BUILD_TYPE=Debug

BUILD_SHARED_LIBS:Switch between shared and static libraries

内置变量的使用:

>> 在CMakeLists.txt中指定,使用set

>> cmake命令中使用,如cmake -DBUILD_SHARED_LIBS=OFF

  • 命令

project (HELLO)   #指定项目名称,生成的VC项目的名称;

>>使用${HELLO_SOURCE_DIR}表示项目根目录

include_directories:指定头文件的搜索路径,相当于指定gcc的-I参数

>> include_directories (${HELLO_SOURCE_DIR}/Hello)  #增加Hello为include目录

link_directories:动态链接库或静态链接库的搜索路径,相当于gcc的-L参数

       >> link_directories (${HELLO_BINARY_DIR}/Hello)     #增加Hello为link目录

add_subdirectory:包含子目录

       >> add_subdirectory (Hello)

add_executable:编译可执行程序,指定编译,好像也可以添加.o文件

       >> add_executable (helloDemo demo.cxx demo_b.cxx)   #将cxx编译成可执行文件——

add_definitions:添加编译参数

>> add_definitions(-DDEBUG)将在gcc命令行添加DEBUG宏定义;

>> add_definitions( “-Wall -ansi –pedantic –g”)

target_link_libraries:添加链接库,相同于指定-l参数

>> target_link_libraries(demo Hello) #将可执行文件与Hello连接成最终文件demo

add_library:

>> add_library(Hello hello.cxx)  #将hello.cxx编译成静态库如libHello.a

add_custom_target:

message( status|fatal_error, “message”):

set_target_properties( ... ): lots of properties... OUTPUT_NAME, VERSION, ....

link_libraries( lib1 lib2 ...): All targets link with the same set of libs

  • 其他

1)  怎样获得一个目录下的所有源文件

>> aux_source_directory(<dir> <variable>)

>> 将dir中所有源文件(不包括头文件)保存到变量variable中,然后可以add_executable (ss7gw ${variable})这样使用。

2)  怎样指定项目编译目标

>>  project命令指定

3)  怎样添加动态库和静态库

>> target_link_libraries命令添加即可

4)  怎样在执行CMAKE时打印消息

>> message([SEND_ERROR | STATUS | FATAL_ERROR] "message to display" ...)

>> 注意大小写

5)  怎样指定头文件与库文件路径

>> include_directories与link_directories

>>可以多次调用以设置多个路径

>> link_directories仅对其后面的targets起作用

等等

 

 

 

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: