嵌入式笔试面试题整理

XX研究所:

  1. 说一下静态库和动态库的优缺点
    在windows中静态库是以.lib为后缀的文件,共享库是以.dll为后缀的文件。在Linux中静态库是以.a为后缀的文件,共享库是以.so为后缀的文件。
    以linux下的静态库和动态库为例我们研究一下,首先我们看一下他们的生成方式
    静态库:
    首先将源文件编译成目标文件:gcc –c a.c b.c
    生成静态库:ar –rc libstatic.a a.o b.o
    共享库:
    同静态库一样编译成目标文件:gcc –c a.c b.c
    生成共享库:gcc –fPIC –shared –o libshared.so a.o b.o
    由此可见静态库和动态库都是对目标文件的处理,也可以说库文件已经是机器码文件了,静态库和共享库的加载过程有很大的区别。
    静态库的链接方法:
    gcc –o staticcode –L. –lstatic main.c –static(默认库在当前文件夹)
    共享库的链接方法:
    gcc –o sharedcode -L. –lshared main.c(默认库在当前文件夹)
    当程序与静态库连接时,库中目标文件所含的所有将被程序使用的函数的机器码被copy到最终的可执行文件中。这就会导致最终生成的可执行代码量相对变多,相当于编码器将代码补充完整了,这样运行起来就相对快些。不过会有个缺点:占用磁盘和内存空间。静态库会被添加到和它连接的每个程序中,而且这些程序运行时,都会被加载到内存中,无形中又消耗了更多的内存空间。
    与共享库连接的可执行文件中只包含它需要的函数的引用表,而不是所有的函数代码,只有在程序执行时,那些需要的函数代码才被拷贝到内存中。这样使可执行文件比较小,节省磁盘空间,更进一步,操作系统使用虚拟内存,使得一份共享库驻留在内存中被多个程序使用,也同时节约了内存。不过由于运行时要去链接库会花费一定的时间,执行速度相对会慢一些,总的来说静态库是牺牲了空间效率,换取了时间效率,共享库是牺牲了时间效率换取了空间效率,没有好与坏的区别,只看具体需要了。
  2. Linux下如何查询当前正在运行的Java进程。
    ps -ef | grep Java
    ps -e:显示所有进程
    ps -f:全格式
  3. 说一下进程的几种形式
    有5种状态:
    (1) 运行态:进程是可执行的或者正在执行,或者在运行队列中等待执行。
    (2) 可中断睡眠态:进程被阻塞,等待某些条件的完成。一旦完成这些条件,内核就会将该进程的状态设置位运行态
    (3) 不可中断睡眠态:进程被阻塞,等待某些条件的完成。与可中断睡眠态不同的是,该进程状态不可被信号唤醒。
    (4) 僵死态:该进程已经结束,但是其父进程还没有将其回收。
    (5) 终止态:进程停止执行。
  4. 内核态和用户态的区别
    Linux 进程有4Gb地址空间,用户空间0-3G,内核空间3-4G,这里存放整个内核的代码和所有的内核模块以及内核所维护的数据。
    对于任何操作系统来说,创建一个进程是核心功能。创建进程要做很多工作,会消耗很多物理资源,比如分配物理内存,父子进程拷贝信息,拷贝设置页目录页表等,这些工作得由特权级别的概念。最关键的工作交给特权级最高的进程执行,这样可以做到几种管理,减少有限的资源访问和使用冲突。
    当在系统中执行一个程序时,大部分时间是运行在用户态下的,在其需要操作系统帮助完成一些用户态自己没有特权和能力完成的操作时就会切换到内核态。
    用户切换到内核态的三种方式:
    (1) 系统调用:这是用户态进程主动要求切换到内核态的一种方式。用户态进程通过系统调用申请使用操作系统提供的服务程序完成工作。
    (2) 异常:当cpu在执行运行在用户态下的程序时,发生了一些没有预知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关进程中,也就是切换到了内核态,如缺页异常。
    (3) 外围设备的中断:当外围设备完成用户请求的操作后,会向CPU发出相应的中断信号,这时CPU会暂停执行下一条即将要执行的指令而转到与中断信号对应的处理程序去执行,如果前面执行的指令时用户态下的程序,那么转换的过程自然就会是 由用户态到内核态的切换。
  5. 进程间通信的方式
    管道、FIFO 、消息队列、信号量、共享内存、套接字
  6. 程序编译的几个阶段
    预编译、编译、汇编、链接
  7. 预编译过程是什么?
    预编译做代码文本的替换工作,处理以#开头的指令,比如拷贝#include包含的文件代码,#define宏定义的替换,条件编译等,就是为编译做的预备工作的阶段。
  8. 指针和引用的区别
    (1)指针是指向某块内存,引用仅是个别名
    (2)引用使用时无需解引用(*),指针需要解引用;
    (3)引用只能在定义时被初始化一次,之后不可变;指针可变
    (4)引用没有const,指针有const
    (5)引用不能为空,指针可以为空
    (6)“sizeof”引用得到的时所指向变量的大小,而”sizeof”指针得到的是指针本身的大小。
    (7)从内存分配上看:程序为指针变量分配内存区域,而引用不需要分配内存区域。

XX通信公司

整理面试题如下:

  1. arm架构和x86架构有什么区别?
    arm架构被称作进阶精简指令集机器(Advanced RISC Machine),是一个32位精简指令集(RISC)处理器架构。
    x86架构是CISC(复杂指令集计算)处理器架构。
    主要包括:CPU、北桥芯片、南桥芯片、内存、显卡、显示接口、网卡、声卡、SATA、
    硬盘、总线。
    区别如下:
     性能:X86结构的电脑无论如何都比ARM结构的系统在性能方面要快得多、强得多。但ARM的优势不在于性能强大而在于效率,ARM采用RISC流水线指令集,在完成综合性工作方面根本就处于劣势,而在一些任务相对固定的应用场合其优势就能发挥得淋漓尽致。
     扩展能力:X86结构的电脑采用“桥”的方式与扩展设备(如:硬盘、内存等)进行连接,而且x86结构的电脑出现了近30年,其配套扩展的设备种类多、价格也比较便宜,所以x86结构的电脑能很容易进行性能扩展,如增加内存、硬盘等。ARM结构的电脑是通过专用的数据接口使CPU与数据存储设备进行连接,所以ARM的存储、内存等性能扩展难以进行(一般在产品设计时已经定好其内存及数据存储的容量),所以采用ARM结构的系统,一般不考虑扩展。基本奉行“够用就好”的原则。
     操作系统的兼容性:x86系统在硬件和软件开发方面已经形成统一的标准,几乎所有x86硬件平台都可以直接使用微软的视窗系统及现在流行的几乎所有工具软件,所以x86系统在兼容性方面具有无可比拟的优势。ARM系统几乎都采用Linux的操作系统,而且几乎所有的硬件系统都要单独构建自己的系统,与其他系统不能兼容,这也导致其应用软件不能方便移植,这一点一直严重制约了ARM系统的发展和应用。
     软件开发的方便性及可使用工具的多样性:基于x86结构电脑系统平台开发软件比arm结构系统更容易、更简单、实际成本也更低,同时更容易找到第三方软件(免去自己开发的时间和成本),而且软件移植更容易。
     功耗: X86电脑因考虑要适应各种应用的需求,其发展思路是:性能+速度。x86发展的方向和模式,使其功耗一直居高不下,但是arm架构的产品对功耗做了较好的控制。
  2. 二叉树的遍历方式有几种?每一种遍历方式的时间复杂度和空间复杂度是多少?
    二叉树的四种遍历方式分别是:先序、中序、后序和层次。
    它们的时间复杂度都是O(n),因为它们只访问每个节点一次,不存在多余的访问。
    三种深度优先遍历方法(先序、中序和后序)的空间复杂度是O(h),其中h是二叉树的深度,额外空间是函数递归的调用栈产生的,而不是显示的额外变量。层次遍历的空间复杂度是O(w),其中w是二叉树的宽度(拥有最多节点的层的节点数),因为层次遍历通常是用一个queue来实现的。
  3. 堆和栈的区别是什么(从数据结构的角度分析)?
    堆:堆可以被看做是一颗树,如堆排序。
    栈:一种先进后出的数据结构。

XX通信公司

1.DSP的启动方式有哪几种?
下面这些方式是可选的:
NAND FLASH
NOR FLASH
HPI(HOST PORT INTERFACE)
I2C
SPI
UART0/1/2
MMC/SD
2.进程间通信
管道:速度慢,容量有限,只能父子进程之间通信。
FIFO:任何进程间都能通信,但速度慢。
消息队列:容易收到系统限制,且要注意第一次读的时候,要考虑上一次没有读完的数据。
信号量:不能传递消息,只能用来同步。
共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程本来就已经共享了同一进程内的一块内存。
3.uboot和内核的启动过程
/Uboot启动流程(分为两部分)/
第一部分(放在start.s中,汇编)
1).定义入口(通过链接器脚本来完成)
2)设置异常向量
3)设置CPU速度、时钟频率和中断控制寄存器
4)初始化内存控制器(MMU)
5)将ROM中的程序复制到RAM中
6)初始化堆栈
7)转到RAM中执行,该工作可使用指令 (ldr pc)来完成
第二部分(C语言部分)
1)调用一系列的初始化函数
2)初始化Flash设备
3)初始化系统内存
4)如果目标机有NAND设备,初始化
5)如果目标机有显示设备,初始化
6)初始化相关网络设备,填写IP,MAC地址等
7)进入命令循环(即整个boot的工作环境),接收用户从串口输入的命令,然后进行相应的工作
/kernel启动流程(分为两部分)****/
arm Linux 内核启动流程(分为三个阶段)
第一阶段:内核的重定向和内核的自解压
第二阶段:执行没有压缩的内核的汇编代码部分
1)获取CPU信息
2)检查平台设备号
3)创建页表
4)打开内存控制器(MMU)
5)清除BSS段
6)执行内核C语言部分入口函数
第三阶段:
1 ) 获取Uboot给内核传递的参数
2)控制台初始化
3)执行init命令
4)挂载文件系统
5)执行用户空间的第一个程序
4.OMAPL138的启动机制
 开机上电,用户在boot管脚上配置的启动方式被锁定采样到SYSCFG模块的BOOTCFG寄存器,从而确定处理器的启动方式.
 PSC模块中的启动默认值是:ARM核休眠,DSP核使能,这时候DSP启动,DSP从片内DSP L2 ROM 处读取指令执行,这里有TI已经固化的仅DSP核可以访问的ROM bootloader,简称DSP RBL。
 DSP的RBL做一些简单的初始化后,就通过PSC模块使能ARM核,休眠自己。然后ARM就从片内的RAM LOCAL ROM处读取指令并执行,这里有TI已经固化好的ARM bootloader.
 ARM的RBL做一些初始化后,根据BOOTCFG寄存器的设定,从指定的地方读取用于启动u-boot的Boot loader,简称UBL。UBL可以放在Nandflash,或者通过串口下载等方式取得,这里的UBL是(AIS Application Image Script)格式,而不是通常的bin格式。
 当ARM RBL 读取到boot.ais 后根据AIS格式中的命令初始化、加载并运行UBL。然后就是UBL加载运行u-Boot,u-Boot根据启动参数启动Linux,Linux根据启动参数加载根文件系统。
5.如何给一个数的某一位置位和清零。
写成宏,方便移植
#define setbit(x,y) x|=(1<<y) //将X的第Y位置1
#define clrbit(x,y) x&=!(1<<y) //将X的第Y位清0
5. 如何写程序判断大端小端?

解答1:
int checkCPU()
{
union w {
		int a;
		char b;
} c;
c.a = 1;
return (c.b == 1);
	}
	解答2:
	int checkCPU()
	{
	    int a = 0x12345678;
		char *ptr = NULL;
		ptr = (char*)&a;
		if (*ptr == 0x78) {
			return 1;
		}
		if (*ptr == 0x12) {
			return 0;
		}	

发布了83 篇原创文章 · 获赞 127 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_40788950/article/details/102769051