地平线面试知识点补充

1.vector.resize()和vector.reserve()区别

先明确vector中的两个属性:

size: 表示实际容器中保存元素的个数

capacity: 表示在发生重新分配之前允许存放多少元素

vector<int>vc(5);
	cout << "初始化分配5个元素----------" << endl;
	cout << &vc[0] << endl;
	cout << vc.size() << endl;
	cout << vc.capacity() << endl;
	cout << "reserve(6)----------" << endl;
	vc.reserve(6);
	cout << &vc[0]<<endl;
	cout << vc.size()<<endl;
	cout << vc.capacity() << endl;
	cout << "resize(6)----------" << endl;
	vc.resize(6);
	cout << &vc[0] << endl;
	cout << vc.size() << endl;
	cout << vc.capacity() << endl;
	cout << "reserve(4)----------" << endl;
	vc.reserve(4);
	cout << &vc[0] << endl;
	cout << vc.size() << endl;
	cout << vc.capacity() << endl;
	cout << "resize(4)----------" << endl;
	vc.resize(4);
	cout << &vc[0] << endl;
	cout << vc.size() << endl;
	cout << vc.capacity() << endl;

地平线面试知识点补充_第1张图片
当reverse的大小大于当前容量时,对size不改变,改变capacity
当reverse的大小小于当前容量时,对size不改变,对capacity不改变
由于vector的插入效率低下,reverse主要用于预先分配空间
当resize的大小大于当前容量时,将size与capacity都改为resize的大小
当resize的大小小于当前容量时,只将size改变,而对capacity不改变

reserve(size_type)只是改变了capaciy的值,此时这些内存空间可能还是“野”的。如果使用[]操作符进行访问,可能出现数组越界问题,如果使用[]操作符进行访问,可能出现数组越界问题。

2.虚函数原理与实现

虚函数原理与实现

3.四种类型转换

四种类型转换

4.四种智能指针

四种智能指针

5.进程与线程的切换,上下文切换原理,保存在什么地方

简要概述:
进程的上下文即CPU的寄存器,他们在内存中保存在PCB中

CPU的寄存器:
在CPU中至少要有六类寄存器:
数据寄存器(DR):主要功能是作为CPU和主存、外设之间信息传输的中转站,用以弥补CPU和主存、外设之间操作速度上的差异
指令寄存器(IR):用来保存当前正在执行的一条指令
程序计数器(PC):用来指出下一条指令在主存储器中的地址。
地址寄存器(AR):用来保存CPU当前所访问的主存单元的地址
累加寄存器(AC):当运算器的算术逻辑单元ALU执行算术或逻辑运算时,为ALU提供一个工作区,可以为ALU暂时保存一个操作数或运算结果。
程序状态字寄存器(PSW):用来表征当前运算的状态及程序的工作方式
页表寄存器:里面存放着当前进程页表的起始地址和页表长度。将上述计算的页表号和页表长度进行对比,确认在页表范围内,然后将页表号和页表项长度相乘,得到目标页相对于页表基地址的偏移量,最后加上页表基地址偏移量就可以访问到相对应的框了,CPU拿到框的起始地址之后,再把页内偏移地址加上,访问到最终的目标地址。

通过保存这些信息,可以满足线程或者进程切换时的一切需求,根据切换的模式不同,保存的寄存器中的类型也不同。如系统调用不用保存存放用户态数据的寄存器;同一个进程中的线程切换时不用保存页表寄存器等

此外用户进程既有用户空间信息也有内核空间信息,在进程切换过程中,进程的所有信息都要保存,但用户空间的信息(用户堆栈、用户数据等)是保存存在用户空间,传递地址(用户栈指针)给内核,只有内核空间的信息保存在内核态。

地平线面试知识点补充_第2张图片
如图所示的是个上下文切换的过程,上面的内存颗粒存放的是线程1的上下文,下面的内存颗粒存放的是线程2的上下文,中间的颗粒存放的是内核态。
当从线程1切换到线程2时,
01CPU先切换到内核态
02先将此时CPU寄存器中的值保存到上面的内存颗粒(pcb)中
03在02中使用的PCB中检索下一个进程的上下文,并将其在CPU的寄存器中恢复。

进程在创建时会创建一个PCB,其存放在内存中的内核空间,其中有以下数据:
01进程描述信息:PID号,用户标识符
02进程控制和管理信息:当前进程的状态,优先级,阻塞原因,下一个PCB地址
03资源分配清单:当前进程拥有的文件,内存,IO设备
04CPU寄存器的值,上下文切换之后的数据保存在这个地方

进程切换与系统调用的区别:
首先每个进程都拥有两个堆栈 用户态栈和内核态栈
进程的上下文切换就比系统调用时多了一步:在保存内核态资源(当前进程的内核状态和 CPU 寄存器)之前,需要先把该进程的用户态资源(虚拟内存、栈等)保存下来;而加载了下一进程的内核态后,还需要刷新进程的虚拟内存和用户栈。

进程切换与线程切换的区别:
前后两个线程属于不同进程:此时,因为资源不共享,所以切换过程就跟进程上下文切换是一样的。
前后两个线程属于同一个进程:
地平线面试知识点补充_第3张图片

当进程拥有多个线程时,这些线程会共享相同的虚拟内存和全局变量等资源。这些资源在上下文切换时是不需要修改的。
线程也有自己的私有数据,比如栈和寄存器等,这些在上下文切换时也是需要保存的。

linux性能优化实战
进程线程的上下文切换

6.GDB用法

GDB 全称“GNU symbolic debugger”,从名称上不难看出,它诞生于 GNU 计划(同时诞生的还有 GCC、Emacs 等),是 Linux 下常用的程序调试器。发展至今,GDB 已经迭代了诸多个版本,当下的 GDB 支持调试多种编程语言编写的程序,包括 C、C++、Go、Objective-C、OpenCL、Ada 等。实际场景中,GDB 更常用来调试 C 和 C++ 程序。
gdb命令
视频教学
先在linux中写一段简单的代码:
地平线面试知识点补充_第4张图片
使用gcc -g加入gdb编译,再用gdb打开编译好的a.out:
地平线面试知识点补充_第5张图片
用list命令可以展示出代码并标识出行号,先用b 4在第四行打上断点,再用r发出运行指令,发现停在了第四行,然后不停输入n可以运行到下一行:
地平线面试知识点补充_第6张图片
输入c表示continue,一直运行到程序结束
地平线面试知识点补充_第7张图片
第一个print i结果为21845是因为此时int i=0还未执行,这块内存还没有被初始化
后面可以看到每经过一次for(int i=0;i<10;i++)i的值就会自加1
地平线面试知识点补充_第8张图片
先在第六行第七行打上断点,再info b展示当前的断点信息,再r开启,然后用s(step)进入最近的函数fun():
地平线面试知识点补充_第9张图片
用watch观察变量是否变化:先打断点到第8行,再n让i初始化,此时watch i的值,再c,i的值发生变化,用old value与new value展示变化,用info watch可展示观察点,图中放不下
地平线面试知识点补充_第10张图片

7.CPU的三级缓存,作用,谁来管理,怎么管理

为什么cpu需要用到缓存 简单的说,因为CPU太快,内存太慢,需要有缓存来减少CPU的等待时间,变相地提高CPU性能。

先看CPU执行程序的过程:
地平线面试知识点补充_第11张图片
那 CPU 执行程序的过程如下:

第一步,CPU 读取「程序计数器」的值,这个值是指令的内存地址,然后 CPU 的「控制单元」操作「地址总线」指定需要访问的内存地址,接着通知内存设备准备数据,数据准备好后通过「数据总线」将指令数据传给 CPU,CPU 收到内存传来的数据后,将这个指令数据存入到「指令寄存器」。
第二步,CPU 分析「指令寄存器」中的指令,确定指令的类型和参数,如果是计算类型的指令,就把指令交给「逻辑运算单元」运算;如果是存储类型的指令,则交由「控制单元」执行;
第三步,CPU 执行完指令后,「程序计数器」的值自增,表示指向下一条指令。这个自增的大小,由 CPU 的位宽决定,比如 32 位的 CPU,指令是 4 个字节,需要 4 个内存地址存放,因此「程序计数器」的值会自增 4;
简单总结一下就是,一个程序执行的时候,CPU 会根据程序计数器里的内存地址,从内存里面把需要执行的指令读取到指令寄存器里面执行,然后根据指令长度自增,开始顺序读取下一条指令。

CPU 从程序计数器读取指令、到执行、再到下一条指令,这个过程会不断循环,直到程序执行结束,这个不断循环的过程被称为 CPU 的指令周期。

来源:https://blog.csdn.net/qq_34827674/article/details/109006026

再来看CPU缓存,我们先来举个例子,比如CPU做一个加法运算,需要1-2个时钟周期,那从内存中读取数据需要100-300个周期,这中间差距太大了,CPU不可能等待那么长时间,不然高速的CPU也变成了龟速,于是就想出了缓存Cache这个东西。

现在主流的CPU中,通常有三级缓存,分为L1、L2和L3,它们间的速度呈递减,容量呈递增,读取L1中的信息大概就3个周期,和CPU处理运算的速度无限接近了,读L2的周期大概10-15个周期,读L3的就更慢了,大概40-60个周期左右。

地平线面试知识点补充_第12张图片
如图所示,L1与L2缓存在CPU核中,多个核共用一个L3缓存
L1L2L3的速度越来越慢,价格越来越低(这里的速度是指CPU从中读写数据或指令的速度)

cpu缓存的作用:
CPU缓存(Cache Memory)是位于CPU与内存之间的临时存储器,它的容量比内存小的多但是交换速度却比内存要快得多。CPU高速缓存的出现主要是为了解决CPU运算速度与内存读写速度不匹配的矛盾。
当CPU需要读取数据并进行计算时,首先需要将CPU缓存中查到所需的数据,并在最短的时间下交付给CPU。如果没有查到所需的数据,CPU就会提出“要求”经过缓存从内存中读取,再原路返回至CPU进行计算。而同时,把这个数据所在的数据也调入缓存,可以使得以后对整块数据的读取都从缓存中进行,不必再调用内存,加快访问速度,这是基于时间局部性原理(如果某个数据被访问,那么在不久的将来它很可能再次被访问)和空间局部性原理(如果某个数据被访问,那么与它相邻的数据很快也可能被访问)因此,如果我们查看内存中的某个字节,我们可能很快就会访问其邻居。一旦高速缓存行出现在L1D中,装入指令就可以继续执行并执行其内存读取。

(2)

缓存一致性:
有个程序举例子,有一个数据结构其中有个long型数据,假设缓存行为64B,有两个线程都会对这个数据结构中的int型数据进行修改,循环100次,耗时100秒。
改进:将这个数据结构的int型数据前后都加上64B的数据,此时耗时将远小于100秒,这是因为在修改之前一个缓存行中有8个修改的数据,一个线程对缓存行修改时要通知其他线程对缓存行修改。

现在的计算机几乎都是多个核心的,多个线程运行在不同的核心中,每个核心都有自己的缓存。这时就会出现多个cpu同时修改了同一个数据的问题,这就是著名的缓存一致性问题。

8.判断链表有环
哈希表,快慢指针,略
9.Map和unorderedmap区别
实现一个数据结构,可以用O(1)的时间复杂度来随机访问map中的元素,获取其迭代器

你可能感兴趣的:(面试,面试)