八股第二期

volatile关键字的作用是什么?在什么情况下需要使用?

作用:防止编译器对变量进行优化,使用该变量需要从内存中获取而不是使用再寄存器中的备份;

使用情况:

指向设备寄存器的指针和映射的硬件寄存器通常加volatile;

一个中断服务程序中的变量

多线程应用中被几个任务共享的临界变量

一个参数可以同时是const和volatile吗?为什么?

这两个关键字修饰变量的作用不同,且不冲突,常量const关键字表示该变量的值不能被程序改变,而volatile是编译器不对其进行优化;并不冲突

一个指针可以是volatile吗?请解释。

当一个指针被修饰为volatile时,表示指针本身的值(即指针指向的内存地址)可能会被外部因素改变。

除了修饰指针本身,volatile还可以修饰指针所指向的数据,这意味着指针指向的数据可能在程序之外被改变。每次访问该数据时,都应从内存中读取最新的值。

临界区和临界资源的定义是什么?

临界区是指在多线程或多进程环境中,多个线程或进程访问共享资源的代码区域。

需要适当使用同步互斥机制来防止数据竞态,保证操作的原子性

临界资源是指在多线程或多进程环境中,所有线程或进程需要访问的共享资源。

临界资源通常包括:

  • 共享数据结构:如全局变量、静态变量、共享内存等。
  • 硬件资源:如设备寄存器、文件句柄、网络连接等。

需要适当使用同步互斥机制来确保线程安全;

什么是原子操作?原子性如何影响临界资源的保护?

原子操作是指一个不可分割的操作,中间没有任何其他操作能够干扰。原子操作的关键特性是:

  • 不可中断:在操作执行过程中,任何其他线程或进程都不能观察到操作的中间完成状态。
  • 不可分割:操作在执行时,系统会保证不被中断或分割。

典型的原子操作:简单变量的读取和写入,特殊的原子指令,原子操作库函数

原子性对临界资源保护的具体影响:

**避免竞态条件:**通过使用原子操作,可以确保对临界资源的访问是安全的,不会因为中断或其他线程的干扰导致数据不一致。

**简化同步机制:**有些操作如果是原子的,就不需要复杂的同步机制(如互斥锁、信号量)来保护。例如,简单的计数器递增操作如果是原子的,就不需要使用互斥锁。

**提高性能:**使用原子操作可以减少上下文切换和锁竞争,提高系统的整体性能和响应速度。

如何通过关中断来保护临界资源?

在进入临界区之前,禁用所有中断。这确保了在临界区内的代码不会被中断打断;执行对共享资源的操作,因为中断被禁用,所以不会有其他中断或任务干扰;在离开临界区之后,重新启用中断,以允许正常的中断处理。

如果是在嵌套中断中,需要保存当前中断状态,结束后再恢复之前的中断状态

什么是空任务控制块链表?它与任务控制块链表有什么区别?

任务控制块(TCB)链表是用于管理系统中所有任务的链表。每个任务都有一个任务控制块(TCB),它包含有关任务的各种信息;

空任务控制块链表是一个特殊的链表,用于管理系统中空闲或待分配的任务控制块。

区别:

  • 任务控制块链表:用于管理和调度系统中的所有任务,包含任务的状态、优先级等信息。
  • 空任务控制块链表:用于管理空闲的(未使用的)TCB,优化TCB的分配和释放。
  • 任务控制块链表:每个 TCB 包含任务的所有信息,如任务状态、栈指针等。
  • 空任务控制块链表:每个空 TCB 仅包含一个链表指针,用于组织空闲 TCB。
  • 任务控制块链表:用于任务调度、切换和管理,涉及任务的创建、销毁、优先级调整等操作。
  • 空任务控制块链表:用于高效管理未使用的 TCB,涉及 TCB 的分配和回收。

FreeRTOS的任务栈是如何设定的?参考依据是什么?

在创建任务时,必须指定任务的栈大小。栈大小通常以字(words)为单位

在调用 xTaskCreatexTaskCreateStatic 函数时,需要传入栈大小参数。

以Cortex-M4内核为例,在未使用FPU(浮点运算单元),将16个通用寄存器全部入栈,每个寄存器占用4个字节,一共是64字节,然后根据中断嵌套的次数乘以对应的值;

如果是使用FPU,需要额外将34个浮点寄存器入栈,应该是200字节,然后再根据中断嵌套层数来计算。

FreeRTOS移植到哪些平台,移植过程占用哪些硬件资源?

可以移植到多种平台,单片机、嵌入式处理器、微处理器等等

移植过程:

  • 选择对应目标处理器架构的FreeRTOS版本。
  • 安装相应的工具链。
  • 对FreeRTOS进行配置。
  • 实现FreeRTOS底层函数。
  • 搭建FreeRTOS应用程序,实现任务调度。

移植过程:

内存管理:需要为FreeRTOS分配一定的内存空间。

任务管理:需要配置任务的堆栈大小、优先级等。

时钟和定时器:需要配置FreeRTOS使用哪个时钟源和定时器。

信号量和队列:需要配置信号量和队列的大小和类型。

调度器配置:需要选择FreeRTOS的调度器类型和优化设置。

关键字static的作用是什么?

static是被声明为静态类型的变量,存储在静态区中,其生命周期为整个程序;如果是静态局部变量,其作用域为一对{}内;如果是静态全局变量,其作用域为当前文件静态变量;static修饰的全局变量,只能在本文件被调用;修饰的函数也只能在本文件调用

为什么 static变量只初始化一次?

对于所有的对象(不仅仅是静态对象),初始化都只有一次,而由于静态变量具有“记忆”功能,初始化后,一直都没有被销毁,都会保存在内存区域中,所以不会再次初始化存放在静态区的变量。

const和宏定义的区别?

执行的过程不一样:

宏-预编译 const编译

宏不会检查代码错误,只是替换,但是const会编译报错。

宏的好处:定义代码或字符串、方法、参数 const不能。 坏处:使用大量宏,容易造成编译时间久,每次都需要重新替换

const的用法:

  1. int *const p ; p为只读,*p为变量。
  2. const int *p ; *p为只读,p为变量。
  3. int const * const p ;*p、p都为只读。
  4. const int * const p ;*p、p都为只读。

引用和指针有什么区别?

指针和引用都是地址的概念,指针指向一块内存,它的内容是所指内存的地址;引用是某块内存的别名。

程序为指针变量分配内存区域,而不为引用分配内存区域

指针使用时要在前加 * ,引用可以直接使用

引用在定义时就被初始化,之后无法改变;指针可以发生改变。 即引用的对象不能改变,指针的对象可以改变

理论上指针的级数没有限制,但引用只有一级

++引用与++指针的效果不一样。

例如就++操作而言,对引用的操作直接反应到所指向的对象,而不是改变指向;而对指针的操作,会使指针指向下一个对象,而不是改变所指对象的内容

h头文件中的ifndef/define/endif 的作用

#define是C语言中定义的语法,它是预处理指令,在预处理时进行简单而机械的字符串替换

** #ifndef _ #define _是条件编译**:

一般放在头文件里面,作用就是以防你在.c文件里面不小心重复包含头文件的时候不会报错

底层:通过判断这个字符串是否被定义过,从而决定是否跳过某些语句来达到,条件编译,防止报错的效果的

全局变量和局部变量的区别?

全局变量定义在函数外,局部变量定义在函数内,两这作用域不同,生命周期也不同;

全局变量存储在全局区,局部变量存储在栈区;

C语言内存分区:

地址由低到高分别是栈区、堆区、全局区、常量区和代码区:

代码区:存放程序的代码,即CPU执行的机器指令,并且是只读的;常量区:存放常量;

静态区(全局区):静态变量和全局变量的存储区域是一起的,一旦静态区的内存被分配, 静态区的内存直到程序全部结束之后才会被释放;

堆区:由程序员调用malloc()函数来主动申请的,需使用free()函数来释放内存,若申请了堆区内存,之后忘记释放内存,很容易造成内存泄漏

栈区:存放函数内的局部变量,形参和函数返回值。栈区之中的数据的作用范围过了之后,系统就会回收自动管理栈区的内存(分配内存 , 回收内存),不需要开发人员来手动管理

为什么要进行内存对齐?

在C++中规定了空结构体和空类的内存所占小为1字节,因为c++中规定,任何不同的对象不能拥有相同的内存地址。

而在C语言中,空的结构体在内存中所占大小为0。(gcc中测试为0,其他编译器不一定)

1.平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

2.性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

ELF文件的作用?

更新完程序后,根据DBC生成的A2L文件的信号地址会发生改动,这需要从ELF文件里获取新版软件的地址来更新A2l中信号的地址。

A2L 文件包括:设备参数信息、接口数据信息和Mcu参数信息。

优先级反转?

优先级反转,是指在使用信号量时,当高优先级任务正等待信号量,一个介于两个任务优先之间的中等优先级任务开始执行,它导致一个高优先级任务在等待一个低优先级任务,而低优先级任务却无法执行的情况。

解决方法:优先级继承:让低优先级线程在获得同步资源的时候,临时提升其优先级,释放同步资源后再恢复其原来的优先级。

DMA介绍

DMA直接存储器存取/访问

可以提供外设和存储器之间,或存储器和存储器之间直接的高速数据传输,原理上DMA执行存储器到存储器的数据转运,就是把一个地址的数据取出,送到另一个地址去,而DMA的存在使得这个传输过程无需CPU干预,节省了CPU资源。

编写一个SPI设备驱动程序有哪些步骤

编写设备树

在makefile中添加对应的驱动文件配置和驱动文件,构造DTS节点

编写、并向内核注册spi_driver

虚函数和纯虚函数的区别

虚函数 (Virtual Function): 虚函数在基类中声明,并可以在派生类中被重写(或称为覆盖)。虚函数在基类中具有默认的实现,但该实现可以在派生类中被覆盖。

纯虚函数 (Pure Virtual Function): 纯虚函数在基类中声明,但没有在基类中实现。它必须在派生类中被重写和实现。

C struct、C++ struct的区别

C语言中:

Struct是用户自定义数据类型

C++语言中:

Struct是抽象数据类型,支持成员函数的定义。

其次是C++的struct可以定义访问权限。

C++ struct和class的区别

struct中的成员默认是public的,class中的默认是private的。

然后其实在c++中如果没有多态和虚函数继承,struct和class的存取效率完全相同,如果不需要兼容c,c++代码中尽量使用class。

TCP的三次握手和四次挥手

第一次握手:客户端先向服务器发送连接请求报文

第二次握手:服务器收到请求报文后,同意连接会发出确认报文

第三次握手:客户端收到确认报文后,需要像服务给出确认,此时TCO连接成功

第一次挥手:客户端发出连接释放报文,并且停止发送数据

第二次挥手:服务器端接收到连接释放报文后,发出确认报文

第三次挥手:客户端接收到服务器端的确认请求后,等待服务器发送连接释放报文,服务器将最后的数据发送完毕后,就向客户端发送连接释放报文。

第四次挥手:客户端收到服务器的连接释放报文后,发出确认,当客户端撤销对应的TCB后,TCP连接彻底断开;

RS232和RS485

232:

对输出:输出“1”时的电平应在-5~-15 V之间,输出“0”时的电平应在+5~+15 V之间对输入:输入电平在-3~-15 V之间被认为“1”,在+3~+15 V之间被认为“0”当线路上不传送数据(空闲)时,发送器输出为“1”

485:

对输出:逻辑"1"以两线间的电压差为+(2 至6)V 表示;逻辑"0"以两线间的电压差为-(2 至6)V 表示。对输入:A比B高200mV以上即认为是逻辑"1",A 比B 低200mV 以上即认为是逻辑"0"。485相对于232而言:最高传输速率高(但传输速率越高传输距离越短);采用差分法来传输信号,对共模干扰具有更强的抗干扰力;RS485允许连接128个收发器,具有多机通讯能力。

485相对于232而言:最高传输速率高(但传输速率越高传输距离越短);采用差分法来传输信号,对共模干扰具有更强的抗干扰力;RS485允许连接128个收发器,具有多机通讯能力。

FreeRTOS的中断管理:

中断的作用:

FreeRTOS 提供了一些用于在中断处理中使用的函数,以确保在中断上下文中正确使用实时操作系统。

ARM Cortex-M的中断处理机制

1.中断向量表(Interrupt Vector Table,IVT)

中断向量表是一个包含中断处理程序地址的表格,每个中断对应表中的一个入口

中断号通过硬件自动映射到中断向量表的相应位置

2.中断处理过程

当一个中断发生时,处理器会根据中断号查找 IVT 中相应位置的中断处理程序的地址;

处理器保存当前上下文,包括寄存器值和状态寄存器等,将控制权转移到中断服务例程;

中断服务例程执行完毕后,处理器会恢复之前保存的上下文,并返回到中断发生前的状态;

3.NVIC

  • Cortex-M 处理器通过 NVIC 管理中断优先级。
  • 可以设置每个中断的优先级,并通过 NVIC 控制中断的使能和屏蔽。

Bootloader

分为boot和loader

boot:

为了跳到C语言中;为了C语言运行程序会进行一系列的初始化;

loader:

开始执行应用逻辑,加载系统内核;

BootLoader就是单片机启动时候运行的一段小程序,这段程序负责单片机固件的更新,可以实现单片机选择性的自己下程序。可以更新,也可以不更新,更新的话,BootLoader更新完程序后,跳转到新程序运行;不更新的话,BootLoader直接跳转到原来的程序去运行

BootLoader更新完程序后并不擦除自己,下次启动后依然先运行BootLoader程序,又可以选择性的更新或者不更新程序,所以BootLoader就是用来管理单片机程序的更新

在实际的单片机工程项目中,如果加入了BootLoader功能,就可以给单片机日后升级程序留出一个接口,方便日后单片机程序更新,需要两个工程一个是app工程一个是bootloader工程

BootLoader工程生成的.hex通常下载到ROM或Flash中的首地址,这样可以保证上电后先运行BootLoader程序

#牛客创作赏金赛##牛客解忧铺##牛客在线求职答疑中心##晒一晒我的offer##我的实习求职记录#
秋招嵌入式八股 文章被收录于专栏

心得和八股文(含面试真题) 全部免费

全部评论

相关推荐

不愿透露姓名的神秘牛友
11-15 12:24
已编辑
点赞 评论 收藏
分享
评论
7
8
分享
牛客网
牛客企业服务