一、关键字
1、volatile
防止编译器对变量进行优化。什么是编译器优化?适用场景:1、并行设备的硬件寄存器,2、一个中断服务程序中修改的供其他程序检测的变量,3、多线程应用中被几个任务共享的变量。
2、static
作用:声明为静态类型变量,分为静态全局变量和静态局部变量,静态变量如果没有被初始化 ,则自动初始化为0。
为什么只初始化一次?
其初始化后一直没有被销毁,保存再内存区域中,所以不会再次初始化。
static修饰的全局变量,只能在本文件被调用;修饰的函数也只能在本文件调用。
3、const
定义变量为常量;
修饰指针:常量指针(const int *p;int const *p;),指针常量(int* const p),指向常量的常指针(const int*const p);
修饰参数,不能改这个参数值;
修饰返回值。
4、typedef 和 define
原理不同,#define是预处理指令,typedef是关键字,具有类型检查的功能。
功能不同,typedef用来定义类型的别名,#define还可以定义常量、变量、编译开关等。
作用域不同,#define无限制,
对指针的操作不同
5、malloc和new的区别
他们动态申请的内存都位于堆中,无法被操作系统回收,需要对应的free/delete来释放空间。
delete会调用析构函数,释放指针成员变量的空间,再销毁对象本身的空间;而free只释放了对象本身的空间,而指针成员所指向的空间没有被释放
malloc 特点函数,需要指定大小(手动计算)返回void指针,需要类型转化,不调用构造函数。需要库文件<stdlib.h>
new 特点运算符,自动计算大小返回具体类型指针,自动调用构造函数,可进行数组初始化。可重载,自定义内存分配。
6、inline
用来声明内联函数(编译时将函数调用替换位函数体,减少开销提高效率)
7、 extern”C”
extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。
8、 C语言宏中#和##
#:可以把一个宏参数直接转换成相应的字符串 (#define con(a) #a)
##:将宏定义的多个形参转换成一个实际参数名。(#define con(a,b) a##b)
9、 strlen("\0") =? sizeof("\0")=?
strlen("\0") =0,sizeof("\0")=2。
strlen 从内存的某个位置开始扫描到碰到第一个\0为止
a、定义常量谁更好?# define还是 const?
define只是单纯的文本替换,止于编译器,只是一个常数,const在堆栈中分配了空间,可以被调用、传递。
const常量有数据类型,而define常量没有数据类型。编译器可以对const常量进行类型安全检査。很多IDE支持调试 const定义的常量。
b、全局变量和局部变量的区别是什么?
作用域:程序块/当前函数
内存存储方式:全局数据区/栈区
生命周期:随主程序创建销毁/局部函数内部
使用方式:程序的各个部分都可以用到/在局部使用
c、全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?
可以,在不同的C文件中以static形式来声明同名全局变量。只能有一个初始化
d、局部变量能否和全局变量重名?
能,局部会屏蔽全局。
e、struct(结构体) 和 union(联合体)的区别
union所有成员共用一块地址空间,不同时存在,对联合体的不同成员赋值,将会对其他成员重写
struct成员占空间是累加的,同时存在,不同成员赋值是互不影响的
假设为32位机器,int型占4个字节, double型占8个字节,char型占1个字节,而DATE是一个联合型变 量,联合型变量共用空间,uion里面最大的变量类型是int[5],所以占用20个字节,它的大小是20,而 由于 union中 double占了8个字节,因此 union是要8个字节对齐,所占内存空间为8的倍数。为了实现 8个字节对齐,所占空间为24.而data是一个结构体变量,每个变量分开占用空间,依次为 sizeof(int) + sizeof(DATE)+ sizeof( double)=4+24+8=36按照8字节对齐,占用空间为40,所以结果为 40+24=64。
二、数组
1、数组指针
指向数组的指针,它表示的是一个指针,这个指针指向的是一个数组
int b[12]={1,2,3,4,5,6,7,8,9,10,11,12};
int (*p)[4];
p=b;
这里*(*(++p)+0)为5,++p相当于把p所指向的地址向后移动4个int所占用的空间。
2、指针数组
指针数组表示的是一个数组,而数组中的元素是指针。int *p[4]
数组下标可以为负数。
三、指针
1、函数指针
函数的地址是函数存储空间的首地址,用函数名表示,这个地址可以定义一个指针变量来存放,这个叫函数指针 int (*p)(int, int)。指向函数的指针变量没有 ++ 和 --运算。
2、指针函数
返回值是一个地址的函数。函数返回值必须用同类型的指针变量来接受。Int *p(int, int)。
a、数组和指针的区别与联系是什么?
存储方式:数值存在静态存储区或栈,连续,根据下标访问;指针指向任意类型的内存块。
sizeof:数组内存sizeof(数组名);数组大小sizeof(数组名)/sizeof(数据类型)
指针sizeof(指针名)根据32位/64位平台来
数据访问方面:指针对数据的访问方式是间接访问,数组对数据的访问则是直接访问。
使用环境:指针多用于动态数据结构(如链表,等等)和动态内存开辟。 数组多用于存储固定个 数且类型统一的数据结构(如线性表等等)和隐式分配。
b、指针和引用的异同是什么?如何相互转换?(C++)
相同:
都是地址的概念,指针指向某一内存、内容是所指内存的地址;引用则是某块内存的别名。
两者都占内存,程序为指针会分配内存
区别:
1. 指针是实体,而引用是别名。
2. 指针和引用的自增(++)运算符意义不同,指针是对内存地址自增,引用是对值的自增。
3. 引用使用时无需解引用(*),指针需要解引用;
4. 引用只能在定义时被初始化一次,之后不可变;指针可变。
5. 引用不能为空,指针可以为空。
6. “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小,在32 位系统指针变量一般占用4字节内存。
转换
1. 指针转引用:把指针用*就可以转换成对象,可以用在引用参数当中。
2. 引用转指针:把引用类型的对象用&取地址就获得指针了。
c、野指针是什么?
(1) 野指针是指向不可用内存的指针,当指针被创建时,指针不可能自动指向NULL,这时,默认值是随机的,此时的指针成为野指针。
(2) 当指针被free或delete释放掉时,如果没有把指针设置为NULL,则会产生野指针,因为释放掉的仅仅是指针指向的内存,并没有把指针本身释放掉。
(3) 第三个造成野指针的原因是指针操作超越了变量的作用范围。
d、C++中的智能指针是什么?
智能指针是一个类,用来存储指针(指向动态分配对象的指针)。
其如何解决内存泄漏?
引入了弱指针 weak_ptr , weak_ptr 的构造函数不会修改引用计数的值,从而不会对对象的内存进行管理,但是可以检测到所管理的对象是否已经被释放,从而避免非法访问。
e、this指针是什么?
this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针
f、数组名和指针的区别?
数组名:表示整个数组的起始地址,编译时分配连续空间
指针:可变的地址变量,运行时可动态分配
四、内存
a、C语言中内存分配的方式有几种?
静态存储区分配(程序编译前),栈上分配(函数执行时),堆上分配 New开辟的空间在堆上
b、堆与栈有什么区别?
申请方式:栈的空间由操作系统自动分配/释放,堆上的空间手动分配/释放。
申请大小的限制栈空间有限:栈是向低地址扩展的数据结构,是一块连续的内存的区域,最大容量已规定。堆是向高地址扩展的数据结构,是不连续的内存区域。
申请效率:栈由系统自动分配,速度较快。但程序员是无法控制的。 堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。
c、队列和栈的区别
原理 后进先出 先进先出
访问元素 栈顶元素 队首元素
实现方式 可用数组和列表
内存利用 更高效 较低效
应用场景 函数调用 任务调度
表达式求值 消息缓冲
d、栈在c语言中有什么作用
C语言中栈用来存储临时变量;
多线程编程的基础是栈,用来存储本线程运行时各个函数的临时变量 和 维系函数调用和函数返回时的函数调用关系和函数运行场景。
栈的范围是由 ss * 10H 至 ss * 10H + sp(ss指堆栈寄存器:存放堆栈段起始地址的高16位,sp指堆栈指针)
e、C语言函数参数压栈顺序是怎样的?
从右至左,主要原因是为了支持可变长参数形式。
f、C++的内存管理是怎么样的?
代码段:只读存储区(cang'l)和文本区(机械代码)
数据段,BSS段
堆区:调用new/malloc函数时在堆区自动分配内存
映射区:存储动态链接库以及调用mmap函数进行的文件映射
栈:使用栈空间存储函数的返回地址、参数、局部变量、返回值
g、什么是内存泄漏?
申请的内存未被释放,导致内存减少。
h、什么是内存对齐?
cpu访问内存是按照特定字节大小进行访问,提高存储效率,可能炸藕才能空间浪费
五、预处理
a、预处理器标识#error的目的是什么
编译程序时,遇到#error生成一个编译错误提示消息,并停止编译。
b、 # include< filename. h>和#include" filename. h"有什么区别?
<>编译器先从标准库路径开始搜索filename.h,使得系统文件调用较快。
" “编译器先从用户的工作路径开始搜索filename.h,然后去寻找系统路径,使得自定义文件较快。
c、头文件的作用
通过头文件来调用库;加强类型安全检查。
d、在头文件中定义静态变量是否可行,为什么?
不可行,如果在头文件中定义静态变量,会造成资源浪费的问题,同时也可能引起程序错误。
原理:
编译阶段,预处理器会将头文件的内容原封不动地复制到每一个包含它的.c文件中。假设header.h定义了int g = 0;,a.c和b.c都包含了它,那么编译后,a.o和b.o中各自都生成了一个名为g的全局符号。
链接阶段,链接器负责将多个.o文件合并为一个可执行文件。当链接器发现a.o里有一个g,b.o里也有一个g,且这两个都是“强符号”(全局变量默认是强符号)时,它会认为程序员意图不明确(到底该用哪个地址?),于是直接报错:error: multiple definition of 'g'。
六、其他
a、左值和右值是什么?编辑 文章
左值:出现在等号左边的变量或表达式,可写(值可改)
右值:出现在等号右边的变量或表达式,可读
通常,左值可以作为右值,但是右值不一定是左值。
b、有符号和无符号的运算
会强制转化位无符号的运算。
int a = -20, unsigned int b = 6,a+b是否大于6? 大于
c、短路求值
|| 和 && 如果看左边可以判断后,不用再计算右边
七、ARM
1、CPU、MPU、MCU、SOC、SOPC
2、交叉编译,为什么
3、ROM(只读存储器,易失),RAM(读写寄存器,非易失)
4、ARM有几种工作模式
用户模式(用户程序的工作模式)、系统模式(特权模式)、一般中断模式
快速中断模式、管理模式(CPU上电后的默认模式)、终止模式、未定义模式
5、ARM指令集分为几类
2类,Thumb类(16位),ARM类(32位)
6、Cortex_M3寄存器组
R1-R12 通用寄存器 :R0-R3传参,R4-R11保存局部变量,R12内部调用暂时寄存器(IP)
R13 主推栈指针(MSP),进程堆栈指针(psp):用于出栈和入栈操作
R14 连接寄存器(LR):保存函数返回地址
R15 程序计数器(PC):跳转下一个执行的命令
R16 程序状态寄存器(PSR)
特殊功能寄存器
7、单片机启动流程
- 1、内核初始化
- 2、强制PC指向复位中断执行复位中断函数
- 3、在复位中断函数中调用SystemIint函数,初始化时钟,配置中断向量表
- 4、调用_main函数完成变量初始化和重定位,初始化堆栈和库函数
- 5、调转到main执行
8、中断
、中断(外中断)和异常(内中断)异同
都有cpu发给内核去处理
产生源不同(异常cpu,中断硬件设备);中断异步,异常时钟同步;上下文不同
、中断和DMA
DMA不需cpu参与,中断需要。
、什么是中断,工作原理?
中断是一种硬件或软件机制,用于暂停CPU当前执行的程序,转而执行特定的服务程序(中断服务程序),处理完成后再返回原程序继续执行。
原理:
- 中断源产生中断请求信号
- cpu检测到中断请求,完成当前指令执行
- CPU保存当前程序状态
- CPU跳转到中断服务程序执行
- 中断服务程序执行完成后,恢复之前保存的程序状态
- 继续执行被中断程序
、中断向量表是什么,其作用
存储中断服务程序入口地址的数组或表格,每个中断源对应一个表项。
作用:
- 提供中断源与对应服务程序之间的映射关系
- 使cpu能快速找到并跳转正确的中断服务程序
- 支持多种中断源的统一管理
、优先级反转
优先级反转是指低优先级任务持有高优先级任务所需的资源,而中优先级任务抢占低优先级任务,导致高优先级任务被中优先级任务间接阻塞的现象。
避免方法:优先级继承、优先级天花板、禁用中断、资源设计优化
9、GPIO
定义:为控制器上可以由用户程序控制的引脚,可配置为输入输出。
工作模式:
- 1) GPIO_Mode_AIN 模拟输入;
- 2) GPIO_Mode_IN_FLOATING 浮空输入;
- 3) GPIO_Mode_IPD 下拉输入(将引脚默认拉至低电平);
- 4) GPIO_Mode_IPU 上拉输入(将引脚默认拉至高电平);
- 5) GPIO_Mode_Out_OD 开漏输出(输出高电平时需要外部上拉电阻);
- 6) GPIO_Mode_Out_PP 推挽输出(可主动输出高电平和低电平);
- 7) GPIO_Mode_AF_OD 复用开漏输出;
- 8) GPIO_Mode_AF_PP 复用推挽输出。
八、通信协议
1、串口UART
通信标准
RS232 单端TXD、RXD、GND 全双 100m 两个节点
RS485 差分 半双工 1200m 多节点
RS422 差分 中等 点对点
比特率>=波特率
UART是一种串行通信协议,特点:异步通信不需要时钟线,全双工,点对点通信,数据格式灵活
a、波特率选择
- 通信距离:距离越长,波特率应越低
- 抗干扰要求:波特率越低抗干扰性越好
- 数据吞吐量需求:高吞吐量需要高波特率
- 硬件支持能力:考虑时钟精度和误差
b、校验方式
无校验、奇校验、偶校验、标记校验、空格校验
c、UART如何实现异步通信?如何保证数据同步?
- 异步通信原理:不使用时钟线,发送方和接收方各自使用本地时钟通过起始位和停止位标记数据帧边界
- 同步机制:起始位触发接收方开始采样,接收方通常使用比波特率高的时钟(如16倍)进行过采样在每个位的中间点采样,减少误差影响双方必须预先约定相同的波特率和帧格式。
2、12C
基本时序:
- START:SCL 高时,SDA 高→低
- STOP:SCL 高时,SDA 低→高
- 数据传输:SCL 低时 SDA 可变化;SCL 高时 SDA 必须稳定。MSB 先发。
- ACK/NACK:每 8 位数据后跟 1 位应答——ACK 拉低,NACK 保持高。
传输时主机产生时钟,在第9个时钟时,数据发送端会释放SDA的控制权,由数据接收端控制SDA,若SDA为高电平,表示非应答信号 (NACK),低电平表示应答信号(ACK)。
R/W:传输方向选择位,1为读,0为写
地址是多少位:
- 7位(最常用):0x00~0x7F(112个部分保留),传输占1字节(7个地址位+1位R/W)
- 10位(拓展):0x000~0x3FF,占 2 字节:第一字节
11110XX+ R/W(XX 为地址高 2 位),第二字节为低 8 位
开漏输出:高电平无驱动能力(SDA和SCL只能被拉低),需借助外部上拉输出高电平。
SDA线在输入时配置成上下拉输入模式。
多主机怎么通信:
- 仲裁:两个主机同时发,各自读 SDA。自己发 1、线上却是 0,说明输了,立刻停发。低电平优先。
- 时钟同步:SCL 也是线与。任一主机拉低 SCL,线上就是低。大家跟着最慢的那个走。
上拉电阻具体作用:
- 定义空闲状态:无通信时SDA和SCL为高
- 实现线与:多设备共享总线,谁都可以拉低发信号,不会硬碰短路
- 配合开漏输出:设备只负责拉低,拉高靠电高阻(4.7千欧)
为什么要求开漏输出:
- 多设备共享:开漏才能线与,推免输出多设备同时驱动会冲突
- 电平兼容:3.3v和5.5v设备可以挂同一总线,上拉接到最高电压即可
- 时钟拉伸:从机可以拉低SCL,拖慢时钟等自己处理完数据
- 多主机总裁:多主机同时发时,谁发高、线上却是低,谁就退出的——低电平优先
时钟同步 vs 时钟拉伸:
- 时钟同步(多主机):SCL 是所有设备的"与"结果——有一个拉低,线上就低;全部释放,才变高。
- 时钟拉伸(从机控速):从机处理不过来,可以一直拉着 SCL 不放。主机必须等 SCL 真正变高,才能继续发下一个 bit。这是从机主动降速的唯一手段。
适用场景:
- 设备多、引脚少(就两根线)
- 速率要求不高(传感器、EEPROM、RTC)
- 需要标准寻址,不想额外接 CS 线
- 不适:大数据量、高吞吐(Flash 读写、屏幕刷新)→ 用 SPI。对延迟极度敏感、要全双工 → 用 SPI。
3、SPI
物理层
- CS线(NSS线):从设备选择线,也称为片选信号线。哪个NSS线设为低电平,就是选中对应的模块。NSS线为高电平时,为结束信号。(独立片选方式,菊花链方式)
- SCK:时钟信号线,用于通讯主机产生,决定了通讯的速率,STM32的SPI时钟频率最大为fPCLK/2,一般通讯过程中,通讯速率受限于低速设备。
- MOSI:主机发送,从机接收端口。
- MISO:主机接收,从机发送端口。
协议层
a、什么是SPI通信协议?其基本特点是什么?
- spi是一种同步串行通信协议。
- 特点:全双工,主从架构,使用专用的片选线选择从设备,无应答机制
b、SPI与I2C、UART相比有哪些优缺点?
- 优:速度快,全双工效率高,协议简单硬件实现容易,无寻址开销传输效率高
- 缺:需要更多信号线,无应答机制,通信距离有限,多从设备时需要多条片选线,引脚占用多
c、SPI有几种工作模式,其区别?
SPI有4种工作模式(Mode 0-3),由CPOL和CPHA两个参数决定:
- CPOL(Clock Polarity):时钟极性,决定空闲状态下SCLK的电平 CPOL=0:空闲状态为低电平CPOL=1:空闲状态为高电平
- CPHA(Clock Phase):时钟相位,决定数据采样的时刻 CPHA=0:在第一个时钟边沿采样CPHA=1:在第二个时钟边沿采样
常见设备的默认模式: SD卡:初始化时使用Mode 0;大多数FLASH存储器:Mode 0或Mode 3;某些传感器:Mode 1或Mode 2
d、SPI通信的基本时序
- 主设备将对应从设备的CS线拉低(激活)
- 主设备开始产生时钟信号SCLK
- 主设备通过MOSI线发送数据,同时可以通过MISO线接收数据
- 数据传输完成后,主设备停止时钟,将CS线拉高(释放)
e、SPI通信中数据是如何传输的?
- 数据传输是基于移位寄存器的,主设备和从设备各有一个8位移位寄存器
- 每个时钟周期,两个寄存器同时移位一位,8个时钟周期后,主从设备完成一个字节的交换
- 通常MSB(最高有效位)先传输,但也有LSB先传输的设备
f、影响spi通信速度因素
主设备时钟频率,信号线长度和布线数量,设备之间电平兼容性,从设备的最大支持频率,软件实现效率
g、如何提高SPI通信的效率?
- 硬件优化:使用更高的时钟频率(在设备支持范围内)优化PCB布线,减少干扰使用适当的上拉/下拉电阻确保信号质量
- 软件优化:使用DMA进行数据传输,减少CPU干预批量传输数据,减少CS切换开销使用中断而非轮询方式处理数据优化缓冲区管理,减少数据拷贝
h、 SPI通信中常见的问题及解决方法?
- 时序问题:症状:数据传输错误或不稳定。解决:确认正确的工作模式,检查时钟频率是否过高
- 多主机冲突:症状:总线数据混乱。解决:SPI不支持多主机,需要使用额外的仲裁机制
- 长线传输问题:症状:高速时数据错误。解决:降低时钟频率,使用差分信号,增加缓冲器
- 电平不兼容:症状:通信不稳定或无法通信。解决:使用电平转换器匹配不同设备的电平要求
I、在嵌入式RTOS环境中,如何管理SPI资源?
- 使用互斥量或信号量保护SPI总线访问
- 实现设备管理器,处理多个任务对SPI的访问请求
- 优化任务优先级,确保关键任务能及时访问SPI
- 考虑使用消息队列缓冲SPI请求,减少任务切换开销
- 实现超时机制,防止某个任务长时间占用SPI资源
4、can
物理层:开环网络和闭环网络
CAN 通讯节点由一个 CAN 控制器及 CAN 收发器(负责把 CAN 控制器的逻辑信号转换成 CAN 总线上的差分信号。)组成,控制器与收发器之间通过 CAN_Tx 及 CAN_Rx 信号线相连,收发器与 CAN 总线之间使用 CAN_High 及 CAN_Low 信号线相连。其中 CAN_Tx 及 CAN_Rx 使用普通的类似 TTL 逻辑信号,而 CAN_High 及 CAN_Low 是一对差分信 号线,使用比较特别的差分信号。
can总线的基本特点
- 多主控制:任何节点都可以在总线空闲时发送消息
- 非破坏性总线仲裁:基于消息优先级的仲裁机制
- 错误检测与处理:具有5种错误检测机制
- 高可靠性:抗电磁干扰能力强
- 实时性:确定性延迟,适合实时控制
- 物理层:差分信号传输(CAN_H和CAN_L)
CAN总线的仲裁机制是如何工作的?
CAN总线采用非破坏性总线仲裁机制:
- 基于消息标识符的优先级仲裁,标识符越小优先级越高
- 发送节点在发送每一位的同时监听总线状态
- 当节点发送隐性位(1)而检测到显性位(0)时,自动退出仲裁
- 退出仲裁的节点转为接收模式,不会破坏优先级更高的消息
- 确保在冲突情况下,优先级最高的消息能够成功发送
CAN总线的帧类型有哪些?各自特点是什么?
- 数据帧:用于传输数据包含标识符、控制域、数据域和CRC校验标准帧(11位标识符)或扩展帧(29位标识符)数据长度0-8字节(CAN FD可达64字节)
- 远程帧:请求特定标识符的数据与数据帧结构类似,但无数据域RTR位为隐性(1)
- 错误帧:检测到错误时发送由错误标志和错误界定符组成主动错误标志:6个连续显性位被动错误标志:6个连续隐性位
- 过载帧:请求延迟发送下一帧结构类似错误帧只能在帧间空间发送
CAN总线的错误处理机制有哪些?
答:CAN总线具有5种错误检测机制:
- 位错误:发送节点监听自己发送的位,检测到不一致时报错
- 填充错误:连续5个相同位后必须插入一个相反的位,违反此规则时报错
- CRC错误:接收方计算的CRC与发送方不一致时报错
- 格式错误:帧中固定格式的位(如界定符)不符合规范时报错
- 应答错误:发送方在应答槽未检测到显性位时报错
CAN节点有三种错误状态:
- 错误主动:正常工作状态,可主动发送错误帧
- 错误被动:当错误计数器超过127时进入,只能发送被动错误帧
- 总线关闭:当发送错误计数器超过255时进入,节点停止通信
can过滤器
五、TCP和UDP
应用场景 | TCP 可靠性要求高 | UDP 实时性要求高,游戏数据传输,物联网设备通信 |
| 连接性 | 面向连接,通信前需建立连接, | 无连接 |
| 可靠性 | 提供可靠传输,保证完整性和顺序性 提供流量控制和拥塞控制机制 | 不提供可靠传输(可靠性可在应用层自定义实现) |
| 传输方式 | 面向字节 | 面向报文 |
| 效率 | 相对较低,有连接管理开销 | 开销小 |
| 头部大小 | 头部20-60字节 | 头部固定8字节 |
TCP的三次握手和四次挥手:
- 三次握手意义:确认双方发送和接收能力正常,同步双方初始序列号,防止历史连接请求突然到服务器造成错误
- 四次挥手意义:确保双方都完成数据传输后再关闭连接,实现全双工连接的单独关闭,TIME_WAIT状态确保最后的ACK能到达服务器(TIME_WAIT:保证最后一个ACK能到达对方,2MSL:报文最大生存时间的两倍,足以确保网络中的报文都消失)
a、TCP如何保证可靠传输?
序列号和确认应答、超时重传、校验和、流量控制(通过滑动窗口机制防止接收方缓冲区溢出),拥塞控制(避免网络拥塞,动态调整发送速率,包括:慢启动、拥塞避免、快重传、快恢复)、数据排序和去重(处理乱序和重复的数据包)
丢包问题处理:超时重传、快速重传、选择性重传、动态调整RTO,拥塞控
九、操作系统
a、什么是进程,什么是线程?区别
进程是资源分配的基本单位,他是程序执行时的一个实例,在程序运行时创建。有自己的独立空间,线程启动系统为他分配空间。开销较小。之间的通信需要一通信方式进行。有一个程序运行的入口、顺序执行序列和程序入口
线程是程序执行时的最小单位,是进程的一个执行流,一个进程由多个线程组成。无自己的独立空间,共享进程数据,有自己的堆栈。涉及频繁切换。不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
b、内核线程和用户线程的区别?
- 内核支持线程是OS内核可感知的,而用户级线程是OS内核不可感知的。
- 用户级线程的创建、撤消和调度不需要OS内核的支持,是在语言(如Java)这一级处理的;而内核支持线程的创建、撤消和调度都需OS内核提供支持,而且与进程的创建、撤消和调度大体是相同的。
- 用户级线程执行系统调用指令时将导致其所属进程被中断,而内核支持线程执行系统调用指令时,只导致该线程被中断。
- 在只有用户级线程的系统内,CPU调度还是以进程为单位,处于运行状态的进程中的多个线程,由用户程序控制线程的轮换运行;在有内核支持线程的系统内,CPU调度则以线程为单位,由OS的线程调度程序负责线程的调度。
- 用户级线程的程序实体是运行在用户态下的程序,而内核支持线程的程序实体则是可以运行在任何状态下的程序。
十、FreeRtos
任务的概念:
- 任务是FreeRTOS中的执行单元,类似于线程
- 每个任务有自己的栈空间和上下文
- 任务通常包含一个无限循环,不应该退出
任务的状态:运行,就绪,阻塞,挂起,删除
a、FreeRTOS的调度算法是怎样的?如何实现时间片轮转?
FreeRTOS默认使用抢占式优先级调度,高优先级任务可以抢占低优先级任务。同优先级任务通过时间片轮转方式调度。
时间片轮转通过以下方式实现:
- 配置
configUSE_TIME_SLICING为1(默认)(是否启用时间片轮转调度) - 每个系统时钟节拍,调度器检查是否有同优先级的就绪任务
- 如果有,则切换到下一个同优先级任务
- 时间片长度等于系统时钟节拍周期
b、FreeRTOS中任务间通信有哪些机制?各有什么特点?
- 队列(Queue):支持多个数据项的FIFO传输可用于任务间和中断与任务间通信,支持阻塞等待和超时机制
- 信号量(Semaphore):二值信号量:用于同步计数信号量:用于资源管理
- 互斥量(Mutex):带优先级继承的特殊信号量用于解决优先级反转问题
- 事件组(Event Group):支持多事件等待和同步可实现"与"和"或"逻辑等待
- 任务通知(Task Notification):轻量级通信机制,性能最高每个任务有一个32位通知值可替代二值信号量、计数信号量和事件组(每个任务只有一个通知值,只能有一个任务等待特定任务的通知)