嵌入式软件开发:笔试总结

时间:2022-06-28 21:06:34

刚参加了某个公司的关于嵌入式软件开发的一场笔试。虽然公司规模一般,但是每参加一次笔试,收获还是有的,在接下来一段时间内,笔者将会尽量把每一次参加过的笔试试题做一个分析:

参加的笔试大体上分为几种:

1)编程语言的基础考察

2)ARM嵌入式的基本概念

3)数据结构

4)Linux系统相关知识


1:编程语言的基础考察

这部分主要包括两个部分:1)出现在选择题中,对一些基本概念的考察。

印象比较深的有两个问题:

问题描述一:以下代码结果是多少?

#include <iostream>
using namespace std;
int func(int x)
{
int count=0;
while(x)
{
count++;
x=x&(x-1);
}
return count;
}
int main()
{
cout<<(9867)<<endl;
return 0;
}

分析:本题func函数返回值是形参x转化成二进制后包含1的数量。理解这一点很容易解答出来。

是因为x&(x-1)每执行一次就会消去一个1,这样只要判断x的二进制有多少个1就可以了。

这个问题在《程序员面试宝典》中已经多次出现。其实本题考察的一个重点还是10进制如何转换成其他进制的一个方法。


问题描述二:

#include <stdio.h>
int main()
{
unsigned int a = 6;
int b=-20;
printf("%d",(a+b)>a?(6):(1));
}
分析:这个问题其实是涉及 了类型转换问题,其实试题中还有一个比较简单类型转换问题,就是把double型转换成int型。关于类型转换的问题,我在http://blog.csdn.net/gzbaishabi/article/details/38590025中已经介绍的比较详细。但是恰恰在说到有符号和无符号之间的转换时说的不是很清楚。

先针对上述的这个问题来说,当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式 计算出的结果是6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是非常重要的。


2:ARM嵌入式的相关基本概念

考察这类题,着实把笔者愣住了半天,因为笔者最近一段时间一直在准备Linux相关内容。相反把嵌入式的基本知识给忘了。索性题目还算简单。

问题描述三:简述一下boot loader的作用。

分析:其实简单的来说boot loader 的作用启到一个系统引导的作用和硬件初始化的作用。具体的就在此问题处把boot loader给简单的介绍下。

------------------------------补充---------------嵌入式软件结构与分布------------------------------------------------

关于嵌入式软件结构与分布

一般情况下嵌入式Linux软件主要包括以下几个部分:

1)引导加载程序:其中包括ROM中的固化启动代码和boot loader两个部分

而内部固化ROM是厂家芯片生成的时候固化的,作用基本上是引导boot loader。

2)Linux的kernel和drivers

3)文件系统

 包括根文件系统和建立于Flash内存设备之上的文件系统(EXT4、UBI、CRAMFS等等)。它是提供管理系统的各种配置文件以及系统执行用户应用程序的良好运行环境的载体。

4)应用程序。 用户自定义的应用程序,存放于文件系统之中。

那么嵌入式Linux中为什么要有boot loader呢?

1. CPU 寄存器的设置: R0=0;
R1=Machine ID(即Machine Type Number,定义在linux/arch/arm/tools/mach-types);
R2=内核启动参数在 RAM 中起始基地址;
 2. CPU 模式: 必须禁止中断(IRQs和FIQs);
CPU 必须 SVC 模式;
 3. Cache 和 MMU 的设置: MMU 必须关闭;
指令 Cache 可以打开也可以关闭;
数据 Cache 必须关闭;
但是在CPU刚上电启动的时候,一般连内存控制器都没有配置过,根本无法在内存中运行程序,更不可能处在Linux内核的启动环境中。为了初始 化CPU及其他外设,使得Linux内核可以在系统主存中跑起来,并让系统符合Linux内核启动的必备条件,必须要有一个先于内核运行的程序,他就是所 谓的引导加载程序(Boot Loader)。
而Boot Loader并不是Linux才需要,是几乎所有的运行操作系统的设备都具备的。我们的PC的BOIS就是Boot Loader的一部分(只是前期引导,后面一般还有外存中的各种Boot Loader),对于Linux PC来说,Boot Loader = BIOS + GRUB/LILO。

这就是关于boot loader 的用处,关于上面提到的CPU寄存器以及CPU模式问题。笔者将会在接下里的ARM 体系结构中一一作出总结

---------------------------------------------------------------------------------------分割线---------------------------------------------------------------------------------------问题描述四:嵌入式处理器的分类。

分析:这个问题是在填空题中出现的。具体的应该是有四个空,是按照结构分类的。对于这个基础型的问题。在笔试时未能第一反应就答上来,着实有点惭愧。因此笔者在这地方。详细的介绍下嵌入式处理器的分类,以及不同处理器的基本介绍。

嵌入式微处理器(MPU):它是由计算机中的CPU演变而来。它的特征是具有32位以上的处理器,具有较高的性能,价格也相对比较昂贵。但与计算机处理器不同的不是,它保留和嵌入式应用紧密相关的功能硬件,去除其他的冗余功能部分,这样就以最低的功耗和资源实现嵌入式应用的特殊要求。

嵌入式微控制器(MCU):其典型代表是单片机。

嵌入式DSP处理器(EDSP):专门处理信号方面的处理器,其系统结构和指令算法方面进行了特殊的设计。具有很高的编译效率和代码执行能力。在数字滤波、FFT、谱分析等各种仪器上DSP获得了大规模的运用。

嵌入式片上系统(SOC):一般来说称之为系统芯片。它是一个产品,是一个有专用目标的集成电路。其中包含完整系统和嵌入软件的全部。从广义上而言,SOC是一个微小型系统,如果说CPU是大脑,那么SOC就包括大脑、眼睛和鼻子等。

FGPA处理器:即现场可编程门阵列,它是在PAL、GAL、CPLD等可编程器件的基础上进一步发展的产物。它是作为专用集成电路(ASIC)领域中的一种半定制电路而出现的,既解决了定制电路的不足,又克服了原有可编程器件门电路数有限的缺点。

问题描述五:嵌入式交叉编译环境有哪两部分组成?简述交叉编译过程。

分析:同样是考察嵌入式交叉编译的问题。这个在做过嵌入式开发的同学们都比较熟悉的一个过程。但是对于初学者而言,虽然知道一步步的执行过程,但是对嵌入式交叉编译工具链却理解的不透彻。

交叉编译:交叉编译指的是在某一个平台上用交叉编译器编译出可在其他平台上运行的代码过程。

以Linux-arm而言,我们在PC上开发的程序,只能够在Linux平台上能够运行,那我们的终极目的是能够在ARM产品上进行运行。对于这种夸平台而言,就需要交叉编译工具(arm-linux-gcc)进行编译,这样的代码才能够在ARM上进行运行。

至于为什么进行交叉编译?

原因有两个:一是运行程序的目标平台具有有限的存储空间和运算能力,二是宿主机与目标机的运行环境并不相同,所以必须要提供不同的类库,使得宿主机上开发的代码能在目标机上运行。

交叉编译工具链:一般由编译器、连接器、解释器和调试器组成。

arm-linux-gcc与gcc有什么区别?

arm-linux-gcc和gcc是两个不同的概念,arm-linux-gcc是编译到arm CPU架构的gcc,gcc是编译到CPU架构的gcc。

交叉编译的基本过程:

我们用一个图来详解一下:

嵌入式软件开发:笔试总结

问题描述六:此题的具体题目记不清楚了,大体上是考察了ARM的中断与异常问题。

分析:关于这个问题,ARM的中断处理是一个非常重要的一个知识点。当时做这个问题的时,是按照以前学习单片机的方法写的。关于ARM的中断处理问题,笔者会在之后的总结中详细给出。

其实从这一题我们可以把整个嵌入式处理器的知识体系结构划分的更加详细。总得的来说关于ARM的体系的话要了解以下几个知识点:

  • ARM系统寄存器
  • ARM寻址方式
  • ARM汇编指令
  • ARM环境C语言编程
  • ARM中断与异常
  • ADS集成开发环境
  • 裸机程序开发(串口、LCD、时钟、LED、按键灯)。
3:数据结构相关问题

数据结构印象中考察了两个类型的问题,虽然笔者在做题时都完成了,但是却发现过于关注后面的结构,却很少深入研究时间复杂度和空间复杂度的问题。

问题描述七:算法的空间复杂度指的是:________________________。

分析:关于空间复杂度和时间复杂度其实就是概念的问题。

空间复杂度是通过计算算法所需的存储空间实现的,简单的来说,“空间复杂度”指空间需求。

时间复杂度:指的是运行时间的需求

问题描述八:使用二分法的时,最坏的时间复杂度是?

分析:其实这个问题考察了两点:一是时间复杂度的计算公式,二是二分法的算法思想。也故意混淆概念“最坏”这个概念。

关于时间复杂度这个问题,推导大O介的方法:

  • 用常数1取代运行时间中所有加法常数
  • 在修改后的运行次数函数中,只保留最高阶项
  • 如果最高阶项存在且不是1,则去除与这个项相乘的常数
得到大O阶。

关于二分法的时间复杂度的具体求解过程,其实就是了解整个二分算法的思想。笔者在折半查找的章节中已有过分析。

http://blog.csdn.net/gzbaishabi/article/details/34838001


4:Linux系统相关知识

问题描述九:给了一个TCP/IP的通信例程,画出socket通信的流程图。

分析:关于网络编程的知识总结中已经给出,此处不再赘述。

http://blog.csdn.net/gzbaishabi/article/details/39338917