主要是根据项目问八股自我介绍1、static1. 修饰函数内部局部静态变量存储位置存静态数据段不占栈空间初始化程序运行只初始化一次函数多次调用不会重置生命周期整个程序运行期间一直存在函数退出值保留作用域仅当前函数内可用外部访问不到。 适用场景IO 计数、按键消抖计数、保存上次采集传感器数值。2. 修饰全局变量 / 全局函数不加 static全局变量 / 函数工程所有文件都能 extern 调用容易重名冲突 加 static作用域限制在当前.c 文件其他源文件无法访问隔离代码、避免命名冲突。 嵌入式常用驱动内部辅助函数、模块私有参数。3. C 修饰类静态成员嵌入式 Linux/RTOS 面试偶尔问静态成员变量归属于类所有对象共用同一块内存类外初始化静态成员函数没有 this 指针只能访问静态变量 / 静态函数不能操作普通成员。2、volatilevolatile 是用来阻止编译器优化的关键字强制变量每次操作都访问原始内存地址。 嵌入式主要三个使用场景第一操作外设硬件寄存器第二中断与主函数共享全局标志位第三 RTOS 多任务共享变量。 它只解决编译缓存优化问题无法保证读写原子性多任务并发修改还需要关中断或者互斥信号量保护。可以和 static 组合使用用于中断内持续保存的计数变量。3、inline 内联函数inline 是内联函数关键字作用是编译时将函数代码嵌入调用位置省去函数跳转、压栈出栈的开销提升程序实时性适合寄存器操作、位运算这类简短高频调用函数。 它只是给编译器的建议函数过长、递归时编译器会失效变回普通函数。相比宏定义inline 有类型检查避免运算优先级 bug更安全。 缺点是多处调用会重复拷贝代码增加 Flash 占用。工程里一般搭配 static 使用放在头文件防止多文件重定义。 另外 inline 只优化调用耗时不会节省 RAM会增加 Flash 代码量。4、结构体与联合体的区别struct 结构体所有成员独立分配内存内存空间是所有成员占用大小之和存在内存对齐填充所有成员可同时存储有效数据。union 联合体共用体所有成员共享同一块内存总大小等于占用空间最大的成员同一时刻只有一个成员有效赋值一个会覆盖其他。5、strlen与sizeof区别sizeof 是编译运算符计算变量、数组、类型占用的全部内存字节strlen 是运行库函数仅统计字符串有效字符到\0停止不含结束符。 数组名给 sizeof 能算出整个数组空间数组当函数参数退化成指针sizeof 只能得到指针宽度。 strlen 必须保证字符串带\0否则内存越界嵌入式中 sizeof 用来获取缓冲区总大小strlen 用来获取实际有效数据长度。6、堆与栈区别栈Stack分配主体编译器自动管理函数调用自动分配、退出自动释放无需手动操作存储内容局部变量、函数形参、返回地址、中断现场、临时寄存器内存地址高地址向低地址增长向下生长大小编译 / 启动时固定配置单片机在启动文件设置 Stack_Size一般几 KB分配速度极快仅移动栈指针无复杂管理生命周期函数执行期间有效函数结束立刻销毁风险递归太深、局部大数组、中断嵌套过多 →栈溢出 HardFault 死机嵌入式场景普通局部变量、临时缓存、函数参数堆Heap分配主体程序员手动管理malloc/calloc/realloc申请free释放存储内容动态内存运行时大小不确定的数据内存地址低地址向高地址增长向上生长大小启动文件配置 Heap_Size资源有限 MCU 常用几 KB 甚至不用堆分配速度慢需要内存块查找、碎片整理生命周期申请后一直存在直到手动 free不释放造成内存泄漏风险忘记 free内存泄漏堆耗尽崩溃重复 free、野指针、越界访问破坏堆管理链表系统死机频繁 malloc/free 产生内存碎片大块内存分配失败嵌入式场景不定长串口缓存、动态创建任务、可变长度协议帧7、TCP服务创建过程socket () 创建套接字参数 AF_INET IPv4SOCK_STREAM TCP返回监听 fd在内核创建 socket 结构体。bind () 绑定地址端口填充 sockaddr_in 结构体指定本机 IP、端口INADDR_ANY 代表监听本机所有网卡。将 fd 和端口绑定否则系统随机分配端口客户端无法固定连接。listen () 开启监听第二个参数是挂起连接队列长度。调用后端口进入监听状态内核自动处理 TCP 三次握手维护半连接、全连接队列。accept () 阻塞获取连接阻塞函数当有客户端完成三次握手后返回全新的客户端通信 fd。recv/send 数据收发使用 accept 返回的 fd 和客户端交互。close () 关闭客户端 fd通信结束关闭触发四次挥手释放连接之后继续循环 accept 等待新客户端。TCP 服务端创建流程首先调用 socket 创建 TCP 流式套接字bind 绑定本机 IP 和端口listen 开启监听设置等待连接队列accept 阻塞等待客户端连接成功返回新通信套接字使用新 fd 收发数据传输完成关闭客户端套接字循环等待下一个连接。 三次握手由内核在 listen 后自动完成监听套接字只负责接入数据交互使用 accept 返回的套接字。8、客户端与服务端自动重连实现过程关闭旧套接字close (fd)释放原有 socket 资源防止句柄泄漏。延时等待防频繁重连风暴加延时比如 500ms~2s不能立刻循环重连避免 CPU 占满。重新创建 socket旧 fd 关闭失效必须重新 socket 分配描述符。重新 connect 连接服务器 IP 端口connect 阻塞 / 非阻塞尝试建立 TCP 连接。判断 connect 返回值成功进入正常收发、心跳检测流程失败回到延时步骤循环重试。4. 心跳保活配套必备单纯收发判断无法检测静默断网网线物理断开、基站掉线TCP 无报文需要心跳客户端定时发送心跳包设置超时计数器多次未收到服务器回复主动断开触发重连。9、select/epollselect 和 epoll 都是 IO 多路复用实现单线程监听多个套接字。select 存在最大监听数量限制每次调用需要重新填充 fd 集合内核线性遍历所有 fd连接多性能差但跨平台嵌入式 LWIP 普遍使用。epoll 是 Linux 特有没有连接上限fd 只需注册一次内核只返回就绪的文件描述符高并发性能更好分为水平触发和边缘触发两种模式边缘触发需要一次性读完缓冲区防止丢数据。10、linux与rtos区别RTOSFreeRTOS/UCOS/RT-Thread实时操作系统面向单片机、资源小、硬实时设备RAM/Flash 几 KB~ 几百 KB。Linux嵌入式 Linux分时通用操作系统带完整文件系统、网络、驱动资源需求高属于软实时多用于 ARM Cortex-A 平台。11、rtos优先级反转优先级反转是 RTOS 调度中的实时性问题低优先级任务持有互斥锁高优先级任务因等待锁阻塞中间优先级无关任务持续抢占运行导致高优先级任务迟迟无法执行。 解决方式有两种优先级继承持有锁的低任务临时提升到等待任务的最高优先级优先级天花板预先给锁设置最高优先级获取锁直接升顶。 普通二值信号量不具备该保护机制共享资源同步推荐使用互斥锁。12、MQTT中的qosMQTT 的 QoS 分为 0、1、2 三个等级控制消息投递可靠度。QoS0 最多一次无应答速度快但可能丢包适合高频采集QoS1 至少一次收到消息回复 ACK超时重传会产生重复消息QoS2 恰好一次四次握手交互不丢不重复但是开销大延迟高。发布和订阅两端 QoS 不一致时取较小等级作为实际传输等级。13、IIC的具体通信过程起始信号 / 停止信号起始信号 SSCL 高电平期间SDA 由高拉低代表通信开始。停止信号 PSCL 高电平期间SDA 由低拉高代表通信结束。写操作流程主机向从机写入数据主机发送起始信号 S发送 7 位从机地址 读写位写 0共 8bit释放 SDA等待从机拉低 SDA 返回ACK 应答无 ACK从机不存在 / 未响应直接发停止终止通信发送寄存器子地址要操作的内部寄存器8bit等待 ACK发送要写入的数据 Byte每发 1Byte 等待一次 ACK全部发送完成主机发送停止信号 P总线释放读操作流程主机读取从机数据I2C 读分为两步先写寄存器地址再重新起始读数据主机发送起始 S发送从机地址 写位 0等待 ACK发送要读取的寄存器子地址等待 ACK重复起始信号 Sr不发停止直接再次起始发送从机地址 读位 1等待 ACK从机输出数据到 SDA主机逐位读取 8bit主机控制应答还要继续读下一字节主机拉低 SDA发送 ACK这是最后一字节主机拉高 SDA发送 NACK 非应答读完所有数据主机发送停止 P应答规则ACK接收方在第 9 个时钟拉低 SDA表示收到数据NACKSDA 保持高代表未收到 / 不再接收数据读数据时主机发 NACK通知从机不再发送数据。时序关键点面试官常问数据变化只能在 SCL 低电平SCL 高电平时 SDA 必须稳定用于判断启停信号高位先传8bit 数据 1bit 应答一个字节占 9 个时钟重复起始 Sr不需要停止直接切换读写连续读取寄存器必备开漏特性设备只能拉低总线拉高靠外部上拉电阻。14、OTA双分区 为什么不能单分区单分区 OTA 是直接覆盖当前运行固件存在四大致命问题第一升级断电会导致固件损坏设备变砖第二擦写运行中的 Flash 扇区会造成程序崩溃第三新固件异常时没有旧版本可以回滚第四固件校验失败后无可用程序。 双分区把运行固件和待升级固件分开存放下载写入备份区不影响当前程序断电不会损坏原有固件新版本启动异常 Bootloader 自动切回旧分区实现安全升级与故障回滚是工业设备标准可靠方案。15、数据库的事物机制事务是什么事务是一组不可分割的 SQL 操作集合要么全部执行成功要么全部失败回滚不会出现只执行一半的中间状态。 多用于设备上报存储、订单、充值、批量日志写入等场景。事务四大特性 ACID必背原子性 Atomic事务是最小执行单元全部成功或全部撤销不存在中间半成品。 例扣设备余额 下发指令扣钱成功但指令下发失败必须把余额恢复。一致性 Consistency事务执行前后数据库数据合法、业务逻辑不变。 转账 A 扣 100、B 加 100总和始终不变不会凭空少钱 / 多钱。隔离性 Isolation多个事务并发执行互相不可见对方未提交的数据互不干扰。 一个事务修改数据未提交其他事务读不到临时脏数据。持久性 Durability事务提交成功后修改永久写入磁盘断电、重启数据不会丢失。16、如何快速大批量存入数据单条循环插入性能极差频繁开启 / 提交事务、频繁 IO 刷盘是瓶颈优化核心合并批量写入、减少 IO 交互、降低事务开销。17、线程池设计 内部机制线程池由任务队列、一组工作线程、互斥锁、条件变量、控制参数构成。初始化时创建最小数量线程线程无任务时通过条件变量阻塞休眠。外部提交任务存入队列并唤醒空闲线程执行任务积压时动态扩容至最大线程数长期空闲自动缩容。 所有队列操作加锁保证线程安全关闭时通过停止标记广播唤醒所有线程等待全部退出后释放资源。核心优势是避免频繁创建线程可控并发降低 CPU 开销。18、环形队列 读写管理环形队列采用数组存储依靠读写指针循环访问。通用实现空一格区分空与满wr 等于 rd 为空wr 下一个位置等于 rd 为满。 单写单读场景无需锁多任务并发读写必须加互斥锁保护指针操作。配合信号量可实现阻塞读写避免 CPU 轮询。入队更新写指针出队更新读指针指针通过取模运算循环回到数组头部广泛用于串口缓存、线程池任务缓冲。
海康威视嵌入式软开
主要是根据项目问八股自我介绍1、static1. 修饰函数内部局部静态变量存储位置存静态数据段不占栈空间初始化程序运行只初始化一次函数多次调用不会重置生命周期整个程序运行期间一直存在函数退出值保留作用域仅当前函数内可用外部访问不到。 适用场景IO 计数、按键消抖计数、保存上次采集传感器数值。2. 修饰全局变量 / 全局函数不加 static全局变量 / 函数工程所有文件都能 extern 调用容易重名冲突 加 static作用域限制在当前.c 文件其他源文件无法访问隔离代码、避免命名冲突。 嵌入式常用驱动内部辅助函数、模块私有参数。3. C 修饰类静态成员嵌入式 Linux/RTOS 面试偶尔问静态成员变量归属于类所有对象共用同一块内存类外初始化静态成员函数没有 this 指针只能访问静态变量 / 静态函数不能操作普通成员。2、volatilevolatile 是用来阻止编译器优化的关键字强制变量每次操作都访问原始内存地址。 嵌入式主要三个使用场景第一操作外设硬件寄存器第二中断与主函数共享全局标志位第三 RTOS 多任务共享变量。 它只解决编译缓存优化问题无法保证读写原子性多任务并发修改还需要关中断或者互斥信号量保护。可以和 static 组合使用用于中断内持续保存的计数变量。3、inline 内联函数inline 是内联函数关键字作用是编译时将函数代码嵌入调用位置省去函数跳转、压栈出栈的开销提升程序实时性适合寄存器操作、位运算这类简短高频调用函数。 它只是给编译器的建议函数过长、递归时编译器会失效变回普通函数。相比宏定义inline 有类型检查避免运算优先级 bug更安全。 缺点是多处调用会重复拷贝代码增加 Flash 占用。工程里一般搭配 static 使用放在头文件防止多文件重定义。 另外 inline 只优化调用耗时不会节省 RAM会增加 Flash 代码量。4、结构体与联合体的区别struct 结构体所有成员独立分配内存内存空间是所有成员占用大小之和存在内存对齐填充所有成员可同时存储有效数据。union 联合体共用体所有成员共享同一块内存总大小等于占用空间最大的成员同一时刻只有一个成员有效赋值一个会覆盖其他。5、strlen与sizeof区别sizeof 是编译运算符计算变量、数组、类型占用的全部内存字节strlen 是运行库函数仅统计字符串有效字符到\0停止不含结束符。 数组名给 sizeof 能算出整个数组空间数组当函数参数退化成指针sizeof 只能得到指针宽度。 strlen 必须保证字符串带\0否则内存越界嵌入式中 sizeof 用来获取缓冲区总大小strlen 用来获取实际有效数据长度。6、堆与栈区别栈Stack分配主体编译器自动管理函数调用自动分配、退出自动释放无需手动操作存储内容局部变量、函数形参、返回地址、中断现场、临时寄存器内存地址高地址向低地址增长向下生长大小编译 / 启动时固定配置单片机在启动文件设置 Stack_Size一般几 KB分配速度极快仅移动栈指针无复杂管理生命周期函数执行期间有效函数结束立刻销毁风险递归太深、局部大数组、中断嵌套过多 →栈溢出 HardFault 死机嵌入式场景普通局部变量、临时缓存、函数参数堆Heap分配主体程序员手动管理malloc/calloc/realloc申请free释放存储内容动态内存运行时大小不确定的数据内存地址低地址向高地址增长向上生长大小启动文件配置 Heap_Size资源有限 MCU 常用几 KB 甚至不用堆分配速度慢需要内存块查找、碎片整理生命周期申请后一直存在直到手动 free不释放造成内存泄漏风险忘记 free内存泄漏堆耗尽崩溃重复 free、野指针、越界访问破坏堆管理链表系统死机频繁 malloc/free 产生内存碎片大块内存分配失败嵌入式场景不定长串口缓存、动态创建任务、可变长度协议帧7、TCP服务创建过程socket () 创建套接字参数 AF_INET IPv4SOCK_STREAM TCP返回监听 fd在内核创建 socket 结构体。bind () 绑定地址端口填充 sockaddr_in 结构体指定本机 IP、端口INADDR_ANY 代表监听本机所有网卡。将 fd 和端口绑定否则系统随机分配端口客户端无法固定连接。listen () 开启监听第二个参数是挂起连接队列长度。调用后端口进入监听状态内核自动处理 TCP 三次握手维护半连接、全连接队列。accept () 阻塞获取连接阻塞函数当有客户端完成三次握手后返回全新的客户端通信 fd。recv/send 数据收发使用 accept 返回的 fd 和客户端交互。close () 关闭客户端 fd通信结束关闭触发四次挥手释放连接之后继续循环 accept 等待新客户端。TCP 服务端创建流程首先调用 socket 创建 TCP 流式套接字bind 绑定本机 IP 和端口listen 开启监听设置等待连接队列accept 阻塞等待客户端连接成功返回新通信套接字使用新 fd 收发数据传输完成关闭客户端套接字循环等待下一个连接。 三次握手由内核在 listen 后自动完成监听套接字只负责接入数据交互使用 accept 返回的套接字。8、客户端与服务端自动重连实现过程关闭旧套接字close (fd)释放原有 socket 资源防止句柄泄漏。延时等待防频繁重连风暴加延时比如 500ms~2s不能立刻循环重连避免 CPU 占满。重新创建 socket旧 fd 关闭失效必须重新 socket 分配描述符。重新 connect 连接服务器 IP 端口connect 阻塞 / 非阻塞尝试建立 TCP 连接。判断 connect 返回值成功进入正常收发、心跳检测流程失败回到延时步骤循环重试。4. 心跳保活配套必备单纯收发判断无法检测静默断网网线物理断开、基站掉线TCP 无报文需要心跳客户端定时发送心跳包设置超时计数器多次未收到服务器回复主动断开触发重连。9、select/epollselect 和 epoll 都是 IO 多路复用实现单线程监听多个套接字。select 存在最大监听数量限制每次调用需要重新填充 fd 集合内核线性遍历所有 fd连接多性能差但跨平台嵌入式 LWIP 普遍使用。epoll 是 Linux 特有没有连接上限fd 只需注册一次内核只返回就绪的文件描述符高并发性能更好分为水平触发和边缘触发两种模式边缘触发需要一次性读完缓冲区防止丢数据。10、linux与rtos区别RTOSFreeRTOS/UCOS/RT-Thread实时操作系统面向单片机、资源小、硬实时设备RAM/Flash 几 KB~ 几百 KB。Linux嵌入式 Linux分时通用操作系统带完整文件系统、网络、驱动资源需求高属于软实时多用于 ARM Cortex-A 平台。11、rtos优先级反转优先级反转是 RTOS 调度中的实时性问题低优先级任务持有互斥锁高优先级任务因等待锁阻塞中间优先级无关任务持续抢占运行导致高优先级任务迟迟无法执行。 解决方式有两种优先级继承持有锁的低任务临时提升到等待任务的最高优先级优先级天花板预先给锁设置最高优先级获取锁直接升顶。 普通二值信号量不具备该保护机制共享资源同步推荐使用互斥锁。12、MQTT中的qosMQTT 的 QoS 分为 0、1、2 三个等级控制消息投递可靠度。QoS0 最多一次无应答速度快但可能丢包适合高频采集QoS1 至少一次收到消息回复 ACK超时重传会产生重复消息QoS2 恰好一次四次握手交互不丢不重复但是开销大延迟高。发布和订阅两端 QoS 不一致时取较小等级作为实际传输等级。13、IIC的具体通信过程起始信号 / 停止信号起始信号 SSCL 高电平期间SDA 由高拉低代表通信开始。停止信号 PSCL 高电平期间SDA 由低拉高代表通信结束。写操作流程主机向从机写入数据主机发送起始信号 S发送 7 位从机地址 读写位写 0共 8bit释放 SDA等待从机拉低 SDA 返回ACK 应答无 ACK从机不存在 / 未响应直接发停止终止通信发送寄存器子地址要操作的内部寄存器8bit等待 ACK发送要写入的数据 Byte每发 1Byte 等待一次 ACK全部发送完成主机发送停止信号 P总线释放读操作流程主机读取从机数据I2C 读分为两步先写寄存器地址再重新起始读数据主机发送起始 S发送从机地址 写位 0等待 ACK发送要读取的寄存器子地址等待 ACK重复起始信号 Sr不发停止直接再次起始发送从机地址 读位 1等待 ACK从机输出数据到 SDA主机逐位读取 8bit主机控制应答还要继续读下一字节主机拉低 SDA发送 ACK这是最后一字节主机拉高 SDA发送 NACK 非应答读完所有数据主机发送停止 P应答规则ACK接收方在第 9 个时钟拉低 SDA表示收到数据NACKSDA 保持高代表未收到 / 不再接收数据读数据时主机发 NACK通知从机不再发送数据。时序关键点面试官常问数据变化只能在 SCL 低电平SCL 高电平时 SDA 必须稳定用于判断启停信号高位先传8bit 数据 1bit 应答一个字节占 9 个时钟重复起始 Sr不需要停止直接切换读写连续读取寄存器必备开漏特性设备只能拉低总线拉高靠外部上拉电阻。14、OTA双分区 为什么不能单分区单分区 OTA 是直接覆盖当前运行固件存在四大致命问题第一升级断电会导致固件损坏设备变砖第二擦写运行中的 Flash 扇区会造成程序崩溃第三新固件异常时没有旧版本可以回滚第四固件校验失败后无可用程序。 双分区把运行固件和待升级固件分开存放下载写入备份区不影响当前程序断电不会损坏原有固件新版本启动异常 Bootloader 自动切回旧分区实现安全升级与故障回滚是工业设备标准可靠方案。15、数据库的事物机制事务是什么事务是一组不可分割的 SQL 操作集合要么全部执行成功要么全部失败回滚不会出现只执行一半的中间状态。 多用于设备上报存储、订单、充值、批量日志写入等场景。事务四大特性 ACID必背原子性 Atomic事务是最小执行单元全部成功或全部撤销不存在中间半成品。 例扣设备余额 下发指令扣钱成功但指令下发失败必须把余额恢复。一致性 Consistency事务执行前后数据库数据合法、业务逻辑不变。 转账 A 扣 100、B 加 100总和始终不变不会凭空少钱 / 多钱。隔离性 Isolation多个事务并发执行互相不可见对方未提交的数据互不干扰。 一个事务修改数据未提交其他事务读不到临时脏数据。持久性 Durability事务提交成功后修改永久写入磁盘断电、重启数据不会丢失。16、如何快速大批量存入数据单条循环插入性能极差频繁开启 / 提交事务、频繁 IO 刷盘是瓶颈优化核心合并批量写入、减少 IO 交互、降低事务开销。17、线程池设计 内部机制线程池由任务队列、一组工作线程、互斥锁、条件变量、控制参数构成。初始化时创建最小数量线程线程无任务时通过条件变量阻塞休眠。外部提交任务存入队列并唤醒空闲线程执行任务积压时动态扩容至最大线程数长期空闲自动缩容。 所有队列操作加锁保证线程安全关闭时通过停止标记广播唤醒所有线程等待全部退出后释放资源。核心优势是避免频繁创建线程可控并发降低 CPU 开销。18、环形队列 读写管理环形队列采用数组存储依靠读写指针循环访问。通用实现空一格区分空与满wr 等于 rd 为空wr 下一个位置等于 rd 为满。 单写单读场景无需锁多任务并发读写必须加互斥锁保护指针操作。配合信号量可实现阻塞读写避免 CPU 轮询。入队更新写指针出队更新读指针指针通过取模运算循环回到数组头部广泛用于串口缓存、线程池任务缓冲。