搜集整理了一波嵌入式软件开发岗位在面试过程中可能会问到的基础问题,方便自己在需要时查阅,现发布出来。
大部分是摘录于已有的高浏览量文章并使用超链接注明参考和引用处,侵删。
问题主要引用自:
https://blog.nowcoder.net/n/418373944177428fb02ed05aae11ab4b
Linux和Windows的换行符有什么不同?
UNIX/Linux 使用的是 0x0A(LF)
DOS/Windows 使用 的是0x0D0A(CRLF)
Shell脚本及应用情况
Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务
Shell 脚本(Shell Script),是一种为 Shell 编写的脚本程序
引用:Shell 教程
在不联网的电脑上,怎么找关于grep的用法?
方法1:grep --help
方法2:man grep(查询man page)
方法3:info grep(查询info page)
如何编译镜像?
在 Linux 内核中增加程序需要完成以下三项工作:
1)将编写的源代码复制到 Linux 内核源代码的相应目录
2)在目录的 Kconfig 文件中增加新源代码对应项目的编译配置选项
3)在目录的 Makefile 文件中增加对新源代码的编译条目
引用:编译Linux内核并运行一个最小镜像
如何编译设备树?
设备树包含DTC(device tree compiler),DTS(device tree source)和DTB(device tree blob)
DTC编译器可以把DTS文件编译成为DTB
make dtbs可单独编译DTB
引用:Linux内核设备树及编译
怎么把编译的镜像放到板子上?
NXP芯片使用MfgTool 烧写系统,烧写内容包括:
1)移植编译出来的 uboot 可执行文件: u-boot.imx;
2)移植编译出来的 zImage 镜像文件和开发板对应的.dtb(设备树);
3)构建的根文件系统 rootfs
Uboot的启动流程
第一阶段:主要是SOC内部的初始化,板级的初始化比较少,所以移植的修改量比较小。此阶段由汇编语言编写,代码主体分布在/uboot/cpu/s5pc11x/start.S和/uboot/board/samsung/x210/lowlevel_init.S中。
第二阶段:主要是板级的初始化,SOC内部的初始化比较少,移植的修改量主要在此。此阶段由c语言编写,代码主体分布在/uboot/lib_arm/board.c中。
应用:Uboot的启动过程分析
为什么有时候Linux需要关闭中断?想一个场景。
在解决中断引起的并发而带来的竞态时需要关闭中断
参考:Linux的中断屏蔽
简介Linux的MMU
如果一个系统采用了虚拟内存技术,那么它就存在着两个内存空间:虚拟内存空间和物理内存空间。
由于存在两个内存地址,因此一个应用程序从编写到被执行,需要进行两次映射。第一次是映射到虚拟内存空间,第二次时映射到物理内存空间。在计算机系统中,第二次映射的工作是由硬件和软件共同来完成的。承担这个任务的硬件部分叫做存储管理单元MMU,软件部分就是操作系统的内存管理模块了。
引用:Linux的虚拟内存详解(MMU、页表结构)
Linux epoll简介
epoll 是Linux内核中的一种可扩展IO事件处理机制
引用:linux之epoll
怎么确定模块有无正常工作?
lsmod或cat /proc/modules命令查看
参考:Linux 内核模块查看命令
一个设备,在单片机中使用和在Linux中的使用的区别?
1)应用开发环境的硬件设备不同
2)程序下载方式不同
3)芯片的硬件资源不同
4)固件的存储位置不同
5)启动方式不同
引用:单片机开发与Linux开发区别
Linux设备有几种类型?
字符设备:以字节流形式被访问的设备,比如字符终端和串口设备
块设备:以数据块形式被访问的设备,比如硬盘、光盘等
网络设备:主机与主机之间进行数据交换的设备
引用:Linux设备类型有哪些
怎么写一个字符设备的驱动?
3种方法:
1)驱动代码中写死;
2)总线设备驱动模型;
3)使用设备树
引用:字符设备驱动程序的三种写法
参考:如何编写字符设备驱动
驱动能不能有中断?
能。外设慢于CPU,轮循会长期占用CPU,固采用中断
参考:Linux驱动中的中断
怎么写一个LCD驱动
LCD驱动程序编写的流程:
1)分配 一个fb_info结构体:framebuffer_alloc
2)设置
设置固定的参数
设置可变的参数
设置操作函数:和我们自己的fb_ops联系起来
其他设置:比如设置调色板
3)硬件相关的设置
配置GPIO用于LCD
根据LCD手册设置LCD控制器,比如VCLK的频率等
分配显存(framebuffer),并把地址告诉LCD控制器
4)注册:register_framebuffer()
引用:Linux的LCD驱动
key_report的底层实现
参考案例:Linux之解析鼠标input事件数据
写一个key驱动,其中的中断函数怎么实现?
参考代码:Linux驱动之按键驱动编写(中断方式)
μC/OS移植要点
移植与3部分内容有关:CPU、操作系统和板级硬件相关代码(BSP)
μC/OS-III的移植工作包括四个内核相关文件(os_cpu.h、os_cpu_a.asm、os_cpu_a.inc和os_cpu_c.c)的代码编写或修改。
移植工作还需要编写或修改三个CPU相关文件:cpu.h、cpu_a.asm和cpu_c.c。
最后,用户需要针对所用的评估板或目标板编写或修改板级支持包(BSP)。
μC/OS-III和μC/OS-II的区别
1)μC/OS-II是一个定位于8/16位以及低端32位CPU的RTOS内核,μC/OS-III则是定位于高端32位(及个别高端16位)CPU的RTOS内核;
2)μC/OS-III同时支持优先级与时间片调度算法;
3)μC/OS-III将中断级人物调度交给系统任务处理,缩短中断执行时间;
4)μC/OS-III采用哈希散列表机制处理时钟街拍,真正做到了硬实时;
5)μC/OS-III可直接向任务发信号量,简化了程序,提高了实时性;
6)μC/OS-III支持的任务数理论上不受限制;
7)μC/OS-III支持同优先级任务
μC/OS如何避免优先级翻转
设置天花板优先级
优先级继承
参考:uCOS-II中任务的优先级翻转现象
优先级调度算法
非抢占式优先权调度算法
抢占式优先权调度算法
参考:优先级调度算法
Uart怎么确定数据正确性?
硬件上:
改变其对0电位以及1电位的定义,吸收差分信号的优点做改进。对于差模信号改怎么抑制的问题,可以吸收类似NEC码的方式,给0与1信号一个容差时间,存在较大的差模信号存在的情况下,对电平的传输也影响不大。
软件上:
采取固定帧头帧尾+数据码正反码+CRC校验的方式。事实上一般的通信增加正反码固定帧头帧尾的方式已经很稳定了。只是在其基础上对数据进行了一个二次保险而已。
引用:UART怎样保证数据的实时性与可靠性
RS232、RS485、RS422、串口的区别
参考:详解RS232、RS485、RS422、串口和握手
CAN通讯简介
Controller Area Network
CAN 控制器根据两根线上的电位差来判断总线电平。总线电平分为显性电平(0)和隐性电平(1)。发送方通过使总线电平发生变化,将消息发送给接收方
CAN协议特点
1)多主控制.在总线空闲时,所有单元都可以发送消息(多主控制),而两个以上的单元同时开始发送消息时,根据标识符(Identifier 以下称为 ID)决定优先级
2)系统的若软性。与总线相连的单元没有类似于“地址”的信息
3)通信速度较快,通信距离远。最高1Mbps(距离小于40M),最远可达10KM(速率低于5Kbps)
4)具有错误检测、错误通知和错误恢复功能
5)故障封闭功能。CAN 可以判断出错误的类型是总线上暂时的数据错误(如外部噪声等)还是持续的数据错误(如单元内部故障、驱动器故障、断线等)
6)连接节点多。CAN 总线是可同时连接多个单元的总线,可连接的单元总数理论上是没有限制的
引用:CAN通信详解
I2C通信协议通信信号
起始信号
终止信号
写数据
读数据
应答信号
非应答信号
参考:对 IIC 总线的理解、调用函数以及常见面试问题
I2C设备的地址多少位?
I2C 协议规定设备地址可以是 7 位或 10 位,实际中 7 位的地址应用比较广泛。
I2C读写时序
写时序:
开始信号→主机+从设备地址+写命令→从机应答→主机+设备内部寄存器地址→从机应答→主机写入数据→从机应答→是否继续发送,不发送的话,发送停止信号P
读时序:
开始信号→主机+从设备地址+写命令→从机应答→主机+设备内部寄存器地址→从机应答→主机+写入从机地址+读命令→从机应答→从设备将数据放入到SDA上,主机读取数据
引用:I2C读写时序
I2C的特点、速度?
特点:
1)简单性、有效性
2)支持多主控
速度:
最高传送速率100kbps
引用:I2C特点
项目开发用的软件I2C还是硬件的?怎么考虑的?软件I2C和硬件I2C有什么异同?
硬件I2C:
对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚也是专用的
软件I2C:
一般是用GPIO管脚,用软件控制管脚状态以模拟I2C通信波形
异同:
1)硬件I2C的效率要远高于软件的,而软件I2C由于不受管脚限制,接口比较灵活
2)硬件I2C用法比较复杂,软件I2C的流程更清楚一些
3)硬件I2C速度比软件I2C快,并且可以用DMA
4)软件I2C可以在任何管脚上,而硬件I2C只能在固定管脚上
5)软件I2C模拟协议的时序,一般较硬件I2C稳定
引用:硬件I2C和软件I2C的区别
I2S简介
I2S(Inter—IC Sound)总线, 又称集成电路内置音频总线,是飞利浦公司为数字音频设备之间的音频数据传输而制定的一种总线标准,该总线专门用于音频设备之间的数据传输,广泛应用于各种多媒体系统。它采用了沿独立的导线传输时钟与数据信号的设计,通过将数据和时钟信号分离,避免了因时差诱发的失真,为用户节省了购买抵抗音频抖动的专业设备的费用。
I2S特点
1)支持全双工/半双工
2)支持主/从模式
3)和PCM相比,I2S更适合立体声系统。I2S的变体也支持多通道的时分复用,因此可以支持多声道。
I2S三个主要信号
1)串行始终SCLK,也叫位始终(BCLK),对应数字音频的每一位数据,SCLK都有一个脉冲。SCLK的频率=2 * 采样频率 * 采样位深。
2)帧时钟LRCK(也称WS),用于切换左右声道的数据。LRCK为‘1’表示传输右声道数据,为“0”则是左声道。LRCK的频率 = 采样频率
3)串行数据(SDATA),就是用二进制补码表示的音频数据,(MSB —> LSB:数据由高位到低位依次传输)
4)一般还有MCLK,主时钟。
引用:I2S协议详解
SPI简介
Serial Peripheral interface 串行外围设备接口
SPI是Motorola首先在其MC68HCXX系列处理器上定义的
SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间
SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线
引用:SPI详细解释
SDIO协议概述
Secure Digital Input and Output ,即安全数字输入输出接口
SDIO特点
1)与多媒体卡系统规格书版本4.2全兼容。支持三种不同的数据总线模式:1位(默认)、4位和8位
2)与较早的多媒体卡系统规格版本全兼容(向前兼容)
3)与SD存储卡规格版本2.0全兼容
4)与SD I/O卡规格版本2.0全兼容:支持两种不同的数据总线模式:1位(默认)和4位
5)完全支持CE-ATA功能(与CE-ATA数字协议版本1.1全兼容)。 8位总线模式下数据传输速率可达48MHz
6)数据和命令输出使能信号,用于控制外部双向驱动器
引用:SDIO协议
DMA概述
Direct Memory Access,即直接存储器访问。
DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输
DMA定义
DMA用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU的干预,通过DMA数据可以快速地移动。节省了CPU的资源来做其他操作
DMA传输方式
主要涉及四种情况的数据传输:
1)外设到内存
2)内存到外设
3)内存到内存
4)外设到外设
DMA传输参数
1)数据的源地址
2)数据传输位置的目标地址
3)传递数据多少的数据传输量
4)进行多少次传输的传输模式
引用:DMA原理
宏函数定义写一个交换数据,不用temp
#define swap(x, y) \
{ \
if (sizeof(x) == sizeof(int)) { \
int *_p1 = (int *) &x; \
int *_p2 = (int *) &y; \
*_p1 ^= *_p2; \
*_p2 ^= *_p1; \
*_p1 ^= *_p2; \
} else { \
long long *_p1 = (long long *) &x; \
long long *_p2 = (long long *) &y; \
*_p1 ^= *_p2; \
*_p2 ^= *_p1; \
*_p1 ^= *_p2; \
} \
}
参考:宏定义swap(x, y) 实现任何数据类型交换
宏定义写循环
#define loop(x,n) for (int x = 0; x < n; ++x)
参考:定义一个循环的 loop 宏方法
一个.c文件从编写到运行到开发板上的整个过程
预处理 → 编译 → 汇编 → 链接
参考:一个C语言程序到执行完文件的全过程
怎么理解C++的封装、继承、多态?
封装:可以隐藏实现细节,使得代码模块化 —— 代码重用
继承:可以扩展已存在的代码模块(类) —— 代码重用
多态:允许将父对象设置成和一个或更多的他的子对象相等的技术 —— 接口重用
引用:C++封装、继承、多态
fgets等读取文件的函数
引用:fgets函数及其用法,C语言fgets函数详解
open函数传入什么参数?什么模式?参数怎么写?
int open(const char*pathname,int flags);
int open(const char*pathname,int flags,mode_t mode);
参数说明:
1.pathname
要打开或创建的目标文件
2.flags
打开文件时,可以传入多个参数选项,用下面的
一个或者多个常量进行“或”运算,构成falgs
参数:
O_RDONLY: 只读打开
O_WRONLY: 只写打开
O_RDWR: 读,写打开
这三个常量,必须制定一个且只能指定一个
O_CREAT: 若文件不存在,则创建它,需要使
用mode选项。来指明新文件的访问权限
O_APPEND: 追加写,如果文件已经有内容,这次打开文件所
写的数据附加到文件的末尾而不覆盖原来的内容
open函数具体使用那个,和具体应用场景相关,如目标文件存在,使用两个参数的open,如果目标文件不存在,需要open创建,则第三个参数表示创建文件的默认权限
引用:open函数详解与close函数详解
C语言标准函数库
引用:C语言标准库函数大全(ctype、time 、stdio、stdlib、math、string)
C语言程序代码优化方法
1)选择合适的算法和数据结构
2)使用尽量小的数据类型
3)使用自加、自减指令
4)减少运算的强度
求余运算(a=a%8改为a=a&7)
平方运算(a=pow(a,2.0)改为a=a*a)
用移位实现乘除法运算
5)延时函数的自加改为自减
6)switch语句中根据发生频率来进行case排序
引用:嵌入式面试题
volatile
作用:
volatile 的作用 是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值
volatile 可以保证对特殊地址的稳定访问
使用场景:
1)并行设备的硬件寄存器(如:状态寄存器)
2)一个中断服务子程序中会访问到的非自动变量
3)多线程应用中被几个任务共享的变量
引用:C语言关键字volatile详解
const
作用:
1)const修饰全局/局部变量:
限定全局变量的作用范围到其定义时所在的编译单元;
指定了一个语义约束,即被修饰的全局变量不允许被修改,编译器会强制实施这个约束
2)constant修饰指针
const int* p1 = &a; // p1是个指针(*),指向一个int型对象(int),该对象是个常量(const)。 是一个底层const(指针本身是个常量),*p1 = 其他值 时报错
int* const p2 = &a; // p2是个常量(const),p2是个指针(*),指向一个int对象(int)。是一个顶层const(指针指向对象是一个常量),p2 = 其他地址 时报错
3)const修饰函数参数
防止函数体内可能会修改参数原始对象
4)const修饰函数返回值
令函数返回一个常量,可以有效防止因用户错误造成的意外
引用:C++ const关键字的总结
inline
作用
解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题
使用inline修饰符,表示为内联函数
inline使用限制
inline只适合函数体内代码简单的涵数使用
不能包含复杂的结构控制语句例如while、switch
内联函数本身不能是直接递归函数(即,自己内部还调用自己的函数)
inline仅是一个对编译器的建议
inline函数仅仅是一个对编译器的建议,最后能否真正内联,由编译器决定
编译器如果认为函数不复杂,能在调用点展开,就会真正内联,并不是说声明了内联就会内联,声明内联只是一个建议而已
建议:inline函数的定义放在头文件中
内联函数要在调用点展开,所以编译器必须随处可见内联函数的定义,这要求每个调用了内联函数的文件都出现了该内联函数的定义。
因此,将内联函数的定义放在头文件里实现是合适的
inline 是一种“用于实现的关键字”
关键字inline 必须与函数定义体放在一起才能使函数成为内联,仅将inline 放在函数声明前面不起任何作用
引用:C语言 - inline关键字的用法详解
怎样进行多线程多进程编程?
并发技术,就是在同一时间同时执行多条任务的技术
引用:多线程和多进程的区别
参考:多线程与多进程
进程和线程的区别?
进程是资源分配的基本单位;
线程是程序执行和调度的最小单位
参考:线程和进程有什么区别
父进程和子进程的区别?
子进程是父进程的复制品,两个进程各自有自己的地址空间
父进程先执行fork()系统调用,调用的结果是系统中多出了一个跟父进程内容完全一样的进程,这个新进程被称为子进程
再执行exec(B )系统调用,这个调用可以让当前进程转而执行另一个可执行代码(一个新的程序)
在父进程中可以使用子进程的进程ID(在执行fork()时的返回值中得到)来中止子进程的执行
在fork之后,父进程先执行,一个时间片到达之后再执行子进程
引用:父进程和子进程
死锁产生的条件及规避方法
产生死锁的4个条件
1)互斥条件;
2)请求和保持条件;
3)不剥夺条件;
4)环路等待条件。
规避死锁的方法
1)破坏请求条件:资源一次性分配,确保不会再有请求;
2)破坏保持条件:只要有一个资源得不到分配,也不给这个进程分配其他的资源;
3)破坏不可剥夺条件:当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源;
4)破坏环路等待条件:资源有序分配法,每一个进程按编号递增的顺序请求资源,释放则相反
引用:死锁面试题
线程的同步互斥
同步:
是指散布在不同任务之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。最基本的场景就是:两个或两个以上的进程或线程在运行过程中协同步调,按预定的先后次序运行。比如 A 任务的运行依赖于 B 任务产生的数据。
互斥:
是指散布在不同任务之间的若干程序片断,当某个任务运行其中一个程序片段时,其它任务就不能运行它们之中的任一程序片段,只能等到该任务运行完这个程序片段后才可以运行。最基本的场景就是:一个公共资源同一时刻只能被一个进程或线程使用,多个进程或线程不能同时使用公共资源
引用:多线程的同步与互斥(互斥锁、条件变量、读写锁、自旋锁、信号量)
并发编程
参考:并发编程面试题
进程间通信方式
1)管道;
2)命名管道;
3)消息队列;
4)共享内存;
5)信号;
6)信号量;
7)套接字
参考:进程间通讯的7种方式
大小端模式
大端模式(big endian):
数据的高位字节保存在内存的低地址中,低位字节保存在内存的高地址中
小端模式(little endian):
数据的高位字节保存在内存的高地址中,低位字节保存在内存的低地址中
引用:大小端
堆和栈的区别
程序内存布局场景下,堆与栈表示两种内存管理方式
1)栈由操作系统自动分配释放 ,用于存放函数的参数值、局部变量等,其操作方式类似于数据结构中的栈
2)堆由开发人员分配和释放, 若开发人员不释放,程序结束时由 OS 回收,分配方式类似于链表
区别点:
1)管理方式不同;
2)空间大小不同;
3)生长方向不同;
4)分配方式不同;
5)分配效率不同;
6)存放内容不同。
数据结构场景下,堆与栈表示两种常用的数据结构
1)栈是一种运算受限的线性表,其限制是指只仅允许在表的一端进行插入和删除操
2)堆是一种常用的树形结构,是一种特殊的完全二叉树,当且仅当满足所有节点的值总是不大于或不小于其父节点的值的完全二叉树被称之为堆
引用:堆与栈的区别
内存不free会怎样?
对系统没有影响,如果不free的话,只是该程序在运行时一直占有一定量的内存,只要程序不运行了,内存就会自动释放
若栈的长度较小,有什么注意事项?
1)不要在函数内部定义过大的局部变量,如过大的结构体变量,联合变量,过大的字符串,数组等;
2)函数调用的深度也需要注意,如果函数 A 调用 B, B 再调用 C,而A/B/C每个函数定义了 10 K的局部变量,则总的栈空间需求将超过 30K;
3)不要直接将大的结构变量通过函数参数传递,这样也会消耗栈空间,可以通过指针或者引用的方式传递
引用:关于线程堆栈大小的注意事项
程序还可以存放在哪里?
ARM的程序即可以在FLASH里运行也可以在RAM里运行,不过能运行程序的FLASH只能使NorFlash,也就是说只要能挂载到ARM的程序地址空间的设备都可以直接放运行程序。
如一般带LINUX或Wince的ARM板,一般会把主系统程序放在NorFlash或NANDFLASH中,上电后用Loader程序吧主系统程序加载到RAM或SDRAM的可执行地址去,然后跳到主程序去执行。
引用:单片机中的程序是在哪里执行的
没定义的变量使用了,报错的根本原因是什么?
C语言是一种强类型语言,需要先定义,否则报变量未定义错误
Malloc使用要注意什么?
1)使用malloc函数分配内存后判断内存分配是否成功,一旦不成功就需要做相关处理
2)在内存使用结束后将malloc分配的内存free释放掉
3)将释放内存后的指针指向nullptr
引用:malloc函数使用时注意的点
Malloc和new的区别?
1) 属性
new/delete是C++关键字,需要编译器支持
malloc/free是库函数,需要头文件支持
2) 参数
new申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算
malloc则需要显式地指出所需内存的尺寸
3)返回类型
new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符
malloc内存分配成功则是返回void * *,需要通过强制类型转换将void**指针转换成我们需要的类型
4)分配失败
new内存分配失败时,会抛出bac_alloc异常
malloc分配内存失败时返回NULL
5)自定义类型
new会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)
malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作
6)重载
C++允许重载new/delete操作符,特别的,布局new的就不需要为对象分配内存,而是指定了一个地址作为内存起始区域,new在这段内存上为对象调用构造函数完成初始化工作,并返回此地址
malloc不允许重载
7)内存区域
new操作符从自由存储区(free store)上为对象动态分配内存空间
malloc函数从堆上动态分配内存
引用:经典面试题之new和malloc的区别
除了局部变量和全局变量,还有什么变量?分别有什么作用?
自动变量
静态变量
外部变量
寄存器变量
引用:C语言函数中的变量(包括:作用域、存储类型)
逻辑地址、线性地址、物理地址分别是什么?
逻辑地址(logical address)
包含在机器语言指令中用来指定一个操作数或一条指令的地址
线性地址(linear address)
(也称虚拟地址 virtual address):
是一个32为无符号整数,可以用来表示高达4GB的地址
物理地址(physical address)
用于内存芯片级内存单元寻址。它们与从微处理器的地址引脚发送到内存总线上的电信号相对应。物理地址由32位或36位无符号整数表示。
引用:关于逻辑地址、线性地址、虚拟地址、物理地址的理解
参考:LINUX 逻辑地址、线性地址、虚拟地址和物理地址
链表和顺序表的区别?
顺序表存储(典型的数组)
原理:
顺序表存储是将数据元素放到一块连续的内存存储空间,相邻数据元素的存放地址也相邻(逻辑与物理统一)。
优点:
1)空间利用率高。(局部性原理,连续存放,命中率高)
2)存取速度高效,通过下标来直接存储。
缺点:
1)插入和删除比较慢,比如:插入或者删除一个元素时,整个表需要遍历移动元素来重新排一次顺序。
2)不可以增长长度,有空间限制,当需要存取的元素个数可能多于顺序表的元素个数时,会出现"溢出"问题.当元素个数远少于预先分配的空间时,空间浪费巨大。
时间性能 :
查找 O(1) ,插入和删除O(n)。
链表存储
原理:
链表存储是在程序运行过程中动态的分配空间,只要存储器还有空间,就不会发生存储溢出问题,相邻数据元素可随意存放,但所占存储空间分两部分,一部分存放结点值,另一部分存放表示结点关系间的指针。
优点:
1)存取某个元素速度慢。
2)插入和删除速度快,保留原有的物理顺序,比如:插入或者删除一个元素时,只需要改变指针指向即可。
3)没有空间限制,存储元素的个数无上限,基本只与内存空间大小有关.
缺点:
1)占用额外的空间以存储指针(浪费空间,不连续存放,malloc开辟,空间碎片多
2)查找速度慢,因为查找时,需要循环链表访问,需要从开始节点一个一个节点去查找元素访问。
时间性能 :
查找 O(n) ,插入和删除O(1)
引用:顺序表和链表的区别及其优缺点
怎么确定该用链表还是数组?
为了通用和扩展,建议用分级索引和动态数组结合,否则不管是数组还是链表,做起增删改查都是很耗性能的。
引用:数组还是链表评论区
怎么判断链表有没有环?
方法1:穷举遍历
方法2:哈希表缓存
方法3:快慢指针
方法4:Set集合大小变化
引用:如何判断链表有环
用数组的形式实现链表,伪代码/思路
参考:用数组实现链表结构
环形缓冲区的概念及使用场景
环形缓冲区是一个先进先出的循环缓冲区,可以向通信程序提供对缓冲区的互斥访问。
其内存一直只用了一块,可避免频繁的内存创建取消、分配。
可用于通讯接收数据的缓存等。
引用:环形缓冲区
讲讲数组和链表的异同(插入增删的复杂度、使用场景、内存分配等等)
数组特点:
1)在内存中,数组是一块连续的区域
2)数组需要预留空间,在使用前要先申请占内存的大小,可能会浪费内存空间
3)插入数据和删除数据效率低,插入数据时,这个位置后面的数据在内存中都要向后移。删除数据时,这个数据后面的数据都要往前移动
4)随机读取效率很高
5)不利于扩展,数组定义的空间不够时要重新定义数组
链表特点:
1)在内存中可以存在任何地方,不要求连续
2)每一个数据都保存了下一个数据的内存地址,通过这个地址找到下一个数据
3)增加数据和删除数据很容易
4)查找数据时效率低,因为不具有随机访问性,所以访问某个位置的数据都要从第一个数据开始访问,然后根据第一个数据保存的下一个数据的地址找到第二个数据,以此类推
5)不指定大小,扩展方便。链表大小不用定义,数据随意增删
引用:数组和链表的区别
排序算法
插入排序
希尔排序
选择排序
冒泡排序
堆排序
快速排序
六大排序算法
ABC类网络分类?
A类IP地址
一个A类IP地址由1字节的网络地址和3字节主机地址组成,网络地址的最高位必须是“0”, 地址范围从1.0.0.0 到126.0.0.0。可用的A类网络有126个,每个网络能容纳1亿多个主机
B类IP地址
一个B类IP地址由2个字节的网络地址和2个字节的主机地址组成,网络地址的最高位必须是“10”,地址范围从128.0.0.0到191.255.255.255。可用的B类网络有16382个,每个网络能容纳6万多个主机
C类IP地址
一个C类IP地址由3字节的网络地址和1字节的主机地址组成,网络地址的最高位必须是“110”。范围从192.0.0.0到223.255.255.255。C类网络可达209万余个,每个网络能容纳254个主机
引用:什么是A.B.C类网络 怎么区别和划分
TCP中大端小端的问题
参考:网络数据的大小端问题
假如中断函数要返回一个值怎么写?
中断服务函数带不带形参和返回值主要是看中断是发生在裸机上还是实时系统中
引用:中断服务函数能不能带形参和返回值
中断要传入什么参数?中断可以有返回值吗?
同上
外部中断应用在什么场景?
如使用外接的按键来作为触发源,使得控制器产生中断,并在中断服务函数中实现控制 RGB 彩灯的任务
参考:STM32系统学习——EXTI(外部中断)
MPU6050怎么读取数据的?读的是模拟量还是数字量?
惯性测量单元(IMU):IMU原理
引用:MPU6050工作原理及STM32控制MPU6050
PPG、ECG传感器
PPG(Photoplethysmography 光体积变化描记图法)
ECG(Electrocardiograph 心电图)
引用:ECG技术原理和PPG技术原理对比
引用:光学心率测量原理
平时单片机应用用的裸机开发多还是RTOS?裸机开发和RTOS的区别?
使用RTOS开发的优势:
1)模块化
使用了操作系统以后,整个软件的工作被拆分成了由多个任务来构成(也会被称为线程),每个线程有自己独立的运行空间
2)并发性
从并发的角度来看,各个线程在使用 delay/事件等待 这类函数时,会自动的让出 CPU 给其他有需要的线程
3)实时性
重要的线程可以设为高优先级,不重要的线程可以降低优先级,做好全局的统筹规划后,这样整个软件的实时性也能得到保证
4)开发效率
操作系统提供了统一的抽象接口层,方便了可重用组件的积累,提高开发效率
5)软件生态
生态的丰富带来了量变到质变的过程(自己玩->大家一起玩)
引用:MCU开发中,你选”裸奔“还是RTOS
程序架构
3种主流程序架构:
顺序执行的前后台系统;
时间片轮训系统;
多任务操作系统
引用:详解嵌入式开发中的三种程序架构
对于一个变量取地址,再读取其值CPU内部是怎样运行的?
存储器的读操作。例如,若要将存储器40H中的内容50H读出,其过程如下:
1)CPU将地址码40H送到地址总线上,经存储器地址译码器选通地址为40H的存储单元;
2)CPU发出“渎”信号,存储器读/写控制开关将数据传输方向拨向“读”;、
3)存储器将地址为40H的存储单元中的内容50H送到数据总线上;
4)CPU将数据总线上的数据50H读人指定的某一寄存器
用过的单片机是什么参数?
平时用过的单片机有哪些主要的资源、参数应当熟记,面试时可能会问到。个人开发过的单片机参数汇总如下:
启动代码
参考:Cortex-M启动代码
Cortex-M0架构
3级流水线结构
适用于低功耗应用
相比M3逻辑门较少,体积较小
基于ARMv6-M架构
M0只有特权模式,并且不支持MPU
关键特点:
1)处理器流水线
Cortex-M0处理器具有3级流水线(取指、解析、执行)
Cortex-M0+处理器具有2级流水线(取指 + 预解析,解析 + 执行)
2)指令集
指令集基于Thumb指令集架构(ISA),但是只使用了Thumb ISA的一个子集(56条指令),多数指令是16位,只有少数一些是32位。可被归为精简指令集架构。
支持可选的单周期32位 * 32位乘法或用于小硅片面积设计的更小的多周期乘法
3)存储器寻址
32位寻址,支持最多4GB的存储空间。
系统总线接口基于名为AHB-Lite的片上总线协议,支持8位、16位、32位的数据传输。
AHB-Lite协议是流水线结构,支持高运行频率。外设可以通过AHB到APB总线桥连至基于APB协议(高级外设总线)的简单总线。
4)中断处理
处理器内部有一个名为嵌套向量中断控制器(NVIC)的中断控制器,其具有控制中断优先级和掩码功能,并且支持各外设产生最多32个中断请求(和芯片厂商有关)、一个不可屏蔽中断(NMI)输入以及多个系统异常。
每个中断都可被设置为4个可编程优先级中的1个,NMI的优先级是固定的。
5)操作系统(OS)支持
处理器中的两个系统异常(SVCall 和 PendSV)用于操作系统
一个名为SYSTick(系统节拍定时器)的24位硬件定时器用于OS周期定时。
Cortex-M0+处理器支持特权和非特权(芯片设计人员可选),OS可以在非特权等级下执行某些应用任务,并可以给这些任务设置存储器访问权限。
Cortex-M0+处理器中存在一个可选的存储器保护单元(MPU),OS可以借此在运行期间定义应用任务的存储器访问权限。
6)低功耗支持
架构定义了普通休眠和深度休眠两种休眠模式,这些休眠模式的实际表现是和设备相关的(取决于你所使用的芯片)。芯片设计人员还可以添加节省功耗的控制寄存器以增加休眠模式的数量,这样可以定义芯片中每个部分的休眠行为。
可以使用WFI(等待中断)或WFE(等待事件)进入休眠模式,也可以利用一种名为退出休眠的特性让处理器自动进入休眠。
芯片设计人员可以基于休眠模式特性、利用其他的硬件等级进行进一步降低功耗,例如唤醒中断控制器等(WIC).
7)调试
调试系统基于ARM CoreSight调试架构,支持简单的单处理器设计到复杂的多处理器设计。
调试接口可以基于JTAG协议(4个或5个引脚)或串行线调试协议(2个引脚),软件开发人员可以利用调试接口访问处理器的调试特性。
支持最多4个硬件断点、2个数据监视点以及用BKPT(断点)指令实现的不限数量的软件断点。
支持通过调试连接程序计数器(PC)采样得到的基本程序执行概况。
Cortex-M0+处理器中有一个名为微跟踪缓冲(MTB)的可选特性,可以利用它实现指令跟踪。
引用:Cortex-M0和Cortex-M0+简介
程序跑飞定位
参考:教你如何找到导致程序跑飞的指令
分散加载文件
参考:Keil MDK的分散加载文件
对未来有规划么?
有没有对自己有个职业规划?
平时怎么学习?
平时怎么总结、汇报一个项目?
介绍自己的学习和技术方面的经历
职业生涯的最激情澎湃的时刻?
你更喜欢跟人打交道还是跟计算机?
你怎么选择一个行业?选择一个公司?
你最期望的工作氛围是怎么样的?
以后想在哪里发展?
期望的薪资范围?
目前还有没有其它的offer?
假如这边也给你发了offer,怎么排序?
你能说说你是怎样考量这两份offer的吗?怎么比较?
3个词概括你自己。
嵌入式面试题
嵌入式100题