【嵌入式问题汇总】

面试问题积累

20230208

1:系统刚上电后 ,看到的第一段代码是什么?

嵌入式系统上电后,第一段代码是:BootLoader程序(引导加载程序)。 嵌入式系统通常没有像BIOS的固化程序,所以整个系统的加载过程有BootLoader来完成。主要完成cpu, flash,ddr 和外部管脚的初始化,为下一阶段代码的执行环境做准备。

2:裸机开发和RTOS开发的区别?

裸机开发:采用轮询的方式执行任务。 CPU使用率低,多任务时,会互相干扰。
RTOS: 采用时间片轮询的方式执行任务(即:线程),采用定时器,在每个定时中断进行任务的切换,使用时间片轮询的方式执行任务,但任务执行的时间到时,则保存现场,去执行另外一个任务,就像CPU的多核的意义。

3:代码的运行过程?

预编译—编译(Complication)—汇编(Assemble)----链接(Link)
预编译:编译的是#include以及宏定义
编译:将预处理后的程序转换成特定的汇编代码
汇编:将上一步的汇编码转成成机器码,产生的文件叫做目标文件(.o)
链接:将多个目标文件以及所需要的库文件(.so,.dll等) 链接成最终的可执行文件(.exe)

4:启动流程为:

上电后,CPU内置程序会从NAND Flash的特定地址(一般是第一个block块地址)读出Boot-Loader程序到CPU的内部内存中。
CPU将控制权交给内部存储器中的Boot-Loader;
Boot-Loader初始化SDRAM,再从NAND Flash中将主程序载入到SDRAM中;
Boot-Loader将控制权交给主程序。

5:volatile的作用是什么?

1:易变的,保证一个变量操作的原子性。
volatile定义的变量存放在:主存。即:每次读取volatile变领,都从主存里读取,而不是从cpu缓存读取。写入volatile变量时也是,写入到主存中。
如果变量a没有声明volitale,则线程a更改变量a值之后,并不能保证刷入主内存,就会出现缓存和主内存不一样的情况。可能会读取到线程a更改前的值。

6:const int* 与int* const的区别?

const int * : 一个指针,指向一个 const int类型的数据,不可通过改变其指向的内容,但可以改变指针本身所指向的地址。
int const *: const作用于左边int, 及一个指针指向一个const 整型。
int * const : const 作用于 *, 所以:是const pointer to an integer,不可改变指向的地址。
const int * const: 左边的const 左边没东西, 右边有int, 则此const 修饰int, 右边的const 作用于 * ,就是指针是const, 不可指向其他地址。
const默认作用于其左边的东西,如果左边没东西,则作用于其右边的东西。
int const * const: 左边的const 作用于int, 右边的const 作用于 * , 就是a const pointer to a const integer

7:TCP/IP协议中IP数据保报文格式?

source IP, target IP 长度 + header + 内容 + CRC等校验位。
包括首部(报头)和数据两部分。首部的前面部分是固定的20字节,包括soureIP ,target IP ,长度,IP版本号等。

20230209

1:数组和链表的区别, 类和结构体的区别。

数组和链表的区别:
1:物理存储结构不同,数组是顺序存储结构,链表是链式存储。 但逻辑结构是相同的,都是线性表。
2:内存分配方式不同。数组一般是静态分配,分配前,已经确定拿了数组的大小,一般在栈上。而链表是动态分配的,申请的内存区域一般在堆区。
3:插入和删除的方式不同。
4:读取方式不同。数组直接通过下标读取。
类和结构体的区别:
类成员默认是private, 结构体成员默认是public。
2:结构体不能被继承,而类可以。
3:结构体是在栈上,类存在于堆上。
4:结构体成员不能使用;protected访问修饰符,而类可以。
在表现抽象和多级别的对象层次时,一般使用类。 如果是数据的集合,如位置,坐标等使用结构体。

2: synchronized 的作用。解释反射机制?

synchronized 的作用:在多线程访问时,保证一个时刻只能有一个线程在使用。即原子性。有序性。
反射机制:通过反射机制,可以在运行时获得程序中每个类型的成员的信息。
动态获取和调用对象方法的功能就是反射机制。
作用:设计出更通用和灵活的架构,保证通用性。

3:项目中使用过哪些设计模式? 介绍下工厂模式和单例模式,写出单例模式的代码。单例模式的作用,应用场景。单例模式如何保证线程安全,确保对象唯一性?

常用的设计模式有: 工厂模式,单例模式,观察者模式。
单例模式的目的: 保证一个系统中仅存在一个实例。能避免频繁创建某个对象,在一定程度上可以减少内存占用。
饿汉式:顾名思义,他是一个饿汉,他很勤快就怕自己饿着。他总是先把食物准备好,什么时候需要吃了,他随时拿来吃,不需要临时去搞食物。

public class Singleton{
	private static Singleton instance = new Singleton();
	public Singleton(){
	}
	public static Singleton getInstance()
	{
		return instance;
	}
}

优点:在初始化时就会加锁执行所有静态方法,避免了在使用时的多线程同步问题。 一定是线程安全的。
缺点:在正式使用之前创建实例对象,占用内存。

懒汉式:解决浪费内存的问题。顾名思义,他是一个懒汉,他不愿意动弹。什么时候需要吃饭了,他就什么时候开始想办法搞点食物。即懒汉式一开始不会实例化,什么时候用就什么时候new,才进行实例化。

public class Singleton{
	private static Singleton instance;
	public Singleton(){
	}
	public static Singleton getInstance()
	{
		if(instance == null)
		{
			instance = new Singleton();
		}
		return instance;
	}
}

优点:只有在需要的时候才会实例化对象,是一种牺牲时间换取空间的策略。
缺点:线程不安全。

如何保证线程安全:加同步锁,

public class SingletonDCL{
	private volatile static SingletonDCL instance;
	private SingletonDCL{}
	public static SingleDCL getInstance(){
		if(instance == null)
		{
			synchronized(SingletonDCL.class)
			{
				if(instance == null)
				{	
					instance = new SingletonDCL();
				}
			}
		}
	}
	return instance;
}

synchronized: 是获取锁,在获取锁后,需要再判断一次是否为null

堆和栈的区别。

单播、多播(组播)和广播的差别

单播:点对点,Unicast。 收发邮件,浏览网页时,必须与网页的server建立连接,此时使用的就是单播。
组播或多播:MultiCast,网上视频会议等。通过多播IP地址来实现。多播地址IP一般是D类地址,即:
即224.0.0.0至239.255.255.255之间的IP地址。
广播: 一个网络向本网络内所有设备进行通信。

众所周知,TCP是可靠传输(先与另一个通信端点建立可靠连接,再传输数据),因此TCP一般只支持单播这种通信方式,而DUP通信不需要建立连接就可以发送数据,因此,通常我们说的广播、组播,都是在UDP下概念。

20230213

1:什么是网络拥塞?什么原因造成的,如何解决?

原因:网络中被共享的资源有限,不能满足用户的要求。
1:缓冲区容量有限。
2:传输线路的频带有限
3:结点处理能力有限
4:网络中某部分发生故障

拥塞是一种持续过载的网络状态,此时用户对网络资源的需求超过了固有的容量。
解决办法:影响网络服务质量的四个要素:带宽,时延,抖动,丢包率。
1:缓冲区分配。
2:分组消灭。如果某个节点上出现分组过于聚集,按照一定规则,将丢弃一部分。
3:流量控制 。控制发送端的数据发送速率。

https://www.docin.com/p-2000394584.html

2:套接字? 数据透传,应用场景?

套接字: socket,插座的意思。是IP和端口的组合。可以看做是一个API,通过API可以进行类似于文件一样的打开,读写,关闭等操作。
数据透传:不需要进行MAC地址,IP地址的转换,不对数据做任何处理,相当于一条数据线或串口线。机场验证身份证,网吧上网,都是数据透传,将数据透传到公安局,在公安局平台上验证。 手机验证码的发送过程也是透传:前端—软件后台—腾讯云等接口, 软件后台对数据不做修改,直接传给接口。

3: 进程和线程的区别,进程间通信(IPC通信)的方式?

进程:每个进程的用户地址空间是独立的,一般是不能互相访问,但内核空间是每个进程共享的。所以,进程之间通信必要要通过内核。
在这里插入图片描述

1:管道pipe: 半双工的通信方式,数据只能单向流动,而且只能在父子进程中使用。 linux 命令里 | 就是管道,如:
ps auxf | grep mysql , 作用是将前一个命令的输出,作为后一个命令的输入。 | 是匿名管道,还有一种FIFO,命名管道。
管道通信方式效率低,不适合进程间频繁交换数据。 优点是: 简单。
匿名管道:只能存在于父子关系进程中。 对于命名管道,可以在不相关的进程间相互通信。

2:消息队列: A进程的数据要给B进程发送消息。 A进程把数据放到对应的消息队列里就正常返回, B进程需要的时候,再读取数据。
消息队列是保存在内核中的消息链表。如果进程从消息队列中读取了消息体,内核就把这个消息体删除。
缺点: 通信过程中,存在用户态和内核态的数据拷贝开销。 读写都要开销。
3:信号量:
有时被称为信号灯,是操作系统用来解决并发中的互斥和同步问题的一种方法。进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。
最简单的信号量是只能取0和1的变量。

4:共享内存:
解决了消息队列方式通信过程中的数据拷贝开销的问题。现代操作系统,对于内存管理,采用的是虚拟内存技术,每个进程都有自己独立的虚拟内存空间,不同进程的虚拟内存映射到不同的物理内存中,所以,即可进程A,B的虚拟地址是一样的,其实访问的是不通的物理内存地址,对于数据的增删改查互不影响。
共享内存
共享内存机制: 就是拿出一块虚拟地址空间来,映射到相同的物理内存中。这样一个进程写入的东西,另外一个进程马上就可以看到,大大提高了进程间通信的速度。
但是: 当多个进程向同一个共享内存中写入数据时可以产生覆盖。(如多人共同修改一个共享文档)

6:Socket:
管道,共享内存,信号,消息队列,信号量都是在同一台主机上进行进程间通信。 Socket:还可以跨网络与不同主机之间进程间通信
在这里插入图片描述

20230214

1:未初始化的int类型的变量,数值是多少?

1:全局int变量(包括static)未初始化,默认值是0。
2:局部int变量未初始化,默认值未知(除了加static前缀的局部变量,因为此时的局部变量是放在静态存储区,跟全局变量一样)
局部变量是存储在栈中,数据量大,且声明周期短,若未对局部变量进行初始化,开销会很大。
所以:我们在定义局部变量时,最好直接初始化。

2: 深拷贝和前拷贝,如何实现深浅拷贝。

本质:内存是如何分配和回收的。
浅拷贝: 两个指针指向同一个地址。 实现方法: b = a
深拷贝:拷贝指针和里面的内容。更改原对象。深拷贝会在堆内存中另外申请空间来存储数据,从而解决了指针悬挂问题,当数据成员中有指针时,必须要用深拷贝。
在这里插入图片描述
1:如果拷贝的对象里元素只有值,没有引用,那么深拷贝和浅拷贝是相同的。
2:如果拷贝的对象里的元素包含引用(像一个列表中存储着另外一个列表,存的就是另一个列表的引用),那么深拷贝和前拷贝就不同。
浅拷贝虽然将原来的对象拷贝一份,但是依然保存的是引用,所以对新对象里的引用里的值进行修改,依然会改变原对象里的列表的值,新对象与原对象没有完全分开。
内存里分为:堆,栈,全局区(静态区),文字常量区,程序代码区

  • 栈区: 存放局部变量和对象的引用。
  • 堆区: 又叫自由存储区,是动态分配,有new分配的内存块,new 和delete要一一对应,否则会出现野指针。
    堆(Heap)往高地址方向生长,栈(Stack)往低地址方向生长。

3:#define宏定义与typedef的区别

1:原理不一样。
define是预处理中的宏定义命令,在预处理时不做正确性检查,只有在编译时,才会发现可能的错误,并报错。typeof:是关键字,在编译时处理。
2:作用域不同
define没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用,因此使用#define之后为了防止错误,要将其解除掉。但是typedef有自己的作用域。

20230215

1: public protected private的区别。 在类外部如何访问类内的私有成员?

public: 说明该数据成员,成员函数对所有用户都开放,可直接调用
protected: 该类及其继承的子类可调用。
private:私有,除了自己可以使用,任何人不可直接使用。
访问私有成员方法:
1:通过set 和get函数实现调用。
2:通过类的友元函数调用该类的private成员函数,但是该成员函数必须设定为static。
友元函数:当函数被声明为类的友元函数,可以赋予该函数与类的成员函数相同的访问权限。友元函数在类中用friend修饰。

class point
{
	public:
		point(int x = 0, int y = 0):x(),y() {};
		int getX() { return x;}
		int getY() { return y;}
		frient float dist(point &p1 ,point &p2); //友元函数声明
	private:   //私有数据成员
		int x,y;
};

float dist(point &p1,point &p2)  //友元函数实现
{
	double x = p1.x - p2.x; //通过对象访问私有数据成员
	double y = p1.y -p2.y;
	return static_cast<float>(sqrt(x*x + y* y));
}
int main()
{
	point myp1(1,1) , myp2(4,5); //定义point类的对象
	cout<<"the distance is:";
	cout<<dist(myp1,myp2) <<endl; //计算两点间的距离
	system("pause");
	return;
}

友元类: 若A类是B类的友元类,则A类的所有成员函数都是B类的友元函数,都可以访问B类的私有和保护成员。
声明友元类是建立类与类之间的联系,实现类之间数据共享的一种途径

class B
{
	...........  //B类的成员声明
	friend class A; //声明A为B的友元类
}

class A
{
	public:
		void display() {cout<< x<<endl};
		int getX() {return x;}
		friend class B;
	private:
		int x;
}
class B
{
	public:
		void set(int i);
		void display();
	private:
		A a;
}
void B::set(int i)
{
	a.x =i; //由于B是A的友元,所以在B的成员函数中可以访问A类对象的私有成员
}

注意:
1:友元关系不能传递
2:友元关系是单向的
3:友元关系是不被继承的

2: 构造函数和析构函数。

3: new delete 和 malloc free 的区别

20230216

1:在代码的前面部分为什么 #include .h ,而没有见过 #include .c ?
理论上,两者都可以,h是接口,c是实现。 但是,C中可能会引入其他的函数,模块,这样就会很危险。

2:你知道智能指针吗?智能指针的原理。
3:常用的智能指针。
4:智能指针的实现。

20230217

1:堆和栈的区别

栈: 函数的形参,函数里定义的局部变量,函数返回值,都存储在栈里。 栈的增长方向是从高地址到低地址。
栈区的数据作用范围过了之后,系统就会回收自动管理。 栈区就像是一个客栈。客人来了自动分配房间,房间里的客人可以变动,是一种动态的数据变动。
堆: 通过new malloc 分配的内存块,编译器不会负责它们的释放工作,需要手动释放。 内存泄漏通常说的就是堆区。
二者的区别:
1:分配和管理方式不同。
堆是动态分配的。就是说,堆的大小并不固定,可扩充或缩减,又malloc等实现。 malloc就扩充,free 就缩减。
栈是编译器自动管理,器分配方式有两种:静态分配和动态分配。静态分配是由编译器完成,如局部变量的分配。
2:申请的大小限制不同
栈是:向低地址扩展的数据结构,是一块连续的内存区域。 栈顶的地址就决定了栈的空间大小是固定的。
堆是向高地址扩展的,不连续的内存区域。
3:申请效率不同
堆:是程序员自己分配,速度慢,容易产生碎片。
4:增长方向不同。
栈是从高到低。

20230227

1:RTOS的实时性是如何实现的?

FreeRTOS就是一款支持多任务运行的实时操作系统,具有时间片、抢占式和合作式三种调度方式。
合作式调度,主要用在资源有限的设备上面,现在已经很少使用了。出于这个原因,后面的FreeRTOS版本中不会将合作式调度删除掉,但也不会再进行升级了。
抢占式调度,每个任务都有不同的优先级,任务会一直运行直到被高优先级任务抢占或者遇到阻塞式的 API 函数,比如 vTaskDelay。
时间片调度,每个任务都有相同的优先级,任务会运行固定的时间片个数或者遇到阻塞式的 API函数,比如vTaskDelay,才会执行同优先级任务之间的任务切换。

2:任务之间是如何通信的?

在FreeRTOS中所有的通信和同步机制都是基于队列(FIFO)实现的。但是也可以使用 LIFO 的存储缓冲,也就是后进先出,FreeRTOS 中的队列也提供了 LIFO 的存储缓冲机制

3:二值信号量和互斥量的区别?

信号量一般用来进行资源管理和任务同步,FreeRTOS中信号量又分为二值信号量、计数型信号量、互斥信号量和递归互斥信号量,不同的信号量其应用场景不同。
二值信号量通常用于互斥访问或同步,二值信号量和互斥信号量非常类似,但是还是有一些细微的差别,互斥信号量拥有优先级继承机制,二值信号量没有优先级继承。因此二值信号另更适合用于同步(任务与任务或任务与中断的同步),而互斥信号量适合用于简单的互斥访问。

https://github.com/xiaowenxia/embedded-notes
https://github.com/CodeAllen999/Linux-C-CPP-Collection

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值