操作系统
硬件结构
64位cpu和32位cpu有什么区别?有什么优势:
①64位的一般来说一次是读取8个字节,32的则是一次取4个字节;
②二者的地址总线不一样,一般来说,64位的地址总线有48条,而32位的是32条地址总线,这也就决定了二者 的寻址能力不同,所以32位的最大寻址能力为4G,所以即使给32位的加了8G的内存,也没啥作用
③二者如果不参与运算的时候其实差别不算很大,但是64位的能够一次性操作超过32位数字的运算,而32位的cpu则需要将这个大数分为多次读取;
④64位的cpu其实只要一套兼容机制就能能够在32位的机器上运行,但是32位的却不行,因为32位的寄存器容不下64位的指令
存储器的执行快慢:速度和存储 容量有没有什么关系?
①速度为 寄存器>cpu Cache>内存>硬盘>机械盘
②越靠近cpu的存储器执行越快,但是相对来说存储容量也就越小,价格也就越贵
③用来描述这些存储器的运行快慢的其实是用cpu的时钟周期,CPU 时钟周期跟 CPU 主频息息相关,比如 2 GHz 主频的 CPU,那么它的时钟周期就是 1/2G,也就是 0.5ns(纳秒)
一般来说:
寄存器:一般要求在半个 CPU 时钟周期内完成读写
cpu Cache:
L1 Cache:一般要求在半个 CPU 时钟周期内完成读写
L2 Cache: 10~20 个时钟周期
L3 Cache: 20~60个时钟周期
内存:200~300 个 时钟周期之间
SSD(硬盘):内存的读写速度比 SSD 大概快 10~1000 倍。
HDD(机械硬盘):它的速度比内存慢 10W 倍左右。
请你讲讲 CPU Cache:
①CPU Cahce是由三个部分组成,最靠近CPU的是L1 Cache,其次是L2 Cahce, 然后才是L3 CPU Cache,值得一提的一点是,L1 Cache和L2 Cache都是在每个CPU核心独有的,而L3 Cache 则是多个CPU 核心共享的,所以相对来说L3 Cache的存储容量会大一点。
②然后我们来说一下 CPU Cache 的结构,CPU Cache其实是由多个CPU Line组成的,是CPU从内存读取数据的基本单位,由两个部分组成,各种标志(Tag)+数据块(Data Bock)
③再来了解一个概念叫Cache line(缓存块),这是指从内存中读取过来的数据块, 比方我们查询L1 Cache Line的字节大小为64字节,则就说明L1 Cache一次载入的数据是64字节,这些带入达到数据就被缓存道CPU Cache ,当缓存命中的时候,就会从CPU Cache中来获取这些数据。也就提升了性能。
如何提升指令缓存的命中率?
- 对于数据缓存,我们在遍历数据的时候,应该按照内存布局的顺序操作,这是因为 CPU Cache 是根据 CPU Cache Line 批量操作数据的,所以顺序地操作连续内存数据时,性能能得到有效的提升;
- 对于指令缓存,有规律的条件分支语句能够让 CPU 的分支预测器发挥作用,进一步提高执行的效率;
- 另外,对于多核 CPU 系统,线程可能在不同 CPU 核心来回切换,这样各个核心的缓存命中率就会受到影响,于是要想提高线程的缓存命中率,可以考虑把线程绑定 CPU 到某一个 CPU 核心。
把Cache中的数据写回到内存?
写直达:把数据和内存同时写入内存
这种方法虽然简单,但是仔细想想也能得出这种这种做法会降低性能,因为无论数据在不在Cache里面,每次写操作都会被写入内存
写回:前面说到写直达的缺点是,只要是写操作都会写到内存,为了减少这种不必要的性能损耗,提出了写回。
简单来说:这种方法会先检测数据是否会在CPU Cache里面,当不存在的时候,还要定位到相数据所在的Cache Block,检测是否为脏读,如果是才会写回内存,这种机制上减少了大量不必要的写回内存操作,大大提升了性能
如何解决缓存一致性问题:
为何会出现:
首先我们要知道缓存一致性问题出现在哪:前面我提到,L1 Cache和L2 Cache是每核CPU独有的,L3 Cache则是共享的,所以在L1 Cache 和L2 Cache 就会出现一致性问题。
如何解决 解决这个问题可以从两点入手:
- 写传播:在做改变的时候要广播给其他的核心Cache;
- 事务串行化: 务必让每一个Cache都有一样顺序的数据操作。
解决方案:
总线嗅探:这是写传播最常见的解决方式,其实就是当某个CPU核心更新Cache中的数据的时候,就应该广播告诉其他核心,但是这种方式其实没有做到事务串行化。
MESI协议:这种方式就能解决上面所说的事务串行化
这里要介绍四种状态:M(Modifies)已修改;E(Exclusive)独占;S(Shared)共享;I(Invalidated)已失效。
下面是处于不同状态下的情况
来聊聊伪共享
我理解的伪共享就是上面EMSI协议状态下的S没有得到真实的发挥作用。
我们来复现下这个场景:
此时CPU核心1和2分别只操作A和B
当核心1改变A后,根据EMSI协议,会把核心2里面的Cache Line状态修改为已失效,核心1里面的Cache Line状态则会变成已修改,试想如果此时核心2里面的再次修改B,那么核心2的状态是不是就会修改为已修改,核心1则为失效,这样交替下次,就会造成死循环。也就是伪共享。
如何解决: 其实思路大概也就是用空间来换时间。
解决方法:
法1:利用宏定义,让本该在同一个Cache line地址的变为对其地址,也就是完成以下地址的转变
法2:利用常量填充Cache Line,因为final定义的变量是不会被修改的所以当Cache Line 的其他位置被常量填充后,及时在不同的核进行修改,其实都不会出现伪共享。
任务的执行都是公平的吗?
其实不是,任务有优先级之分,优先级数值越小,优先级越高。一般来说实时任务会比普通任务的优先级高。
需要介绍三种调度类:如图所示
这几种调度类是有优先级的,优先级如下:Deadline > Realtime > Fair,这意味着 Linux 选择下一个任务执行的时候,会按照此优先级顺序进行选择,也就是说先从 dl_rq 里选择任务,然后从 rt_rq 里选择任务,最后从 cfs_rq 里选择任务。因此,实时任务总是会比普通任务优先被执行。
对于CFS,我们还得了解一个完全公平调度算法
对于普通任务而言,公平是最重要的,在 CFS 算法调度的时候,会优先选择 vruntime 少的任务,这个值的公式为:
虚拟运行时间vruntime=实际运行时间delta_exec *NICE_0_LOAD/权重;
因此权重高的vruntime值低,所以会先执行。
调整优先级: 我们已经知道vrutime值的公式,因此也可以知道,若想改变任务的优先级,可通过改变NICE的值进行修改。但是需要知道的是NiCE的调整的是普通任务的优先级。
讲讲你对中断的理解
在计算机中,中断是系统用来响应硬件设备请求的一种机制,操作系统收到硬件的中断请求,会打断正在执行的进程,然后调用内核中的中断处理程序来响应请求。
操作系统收到了中断请求,会打断其他进程的运行,所以中断请求的响应程序,也就是中断处理程序,要尽可能快的执行完,这样可以减少对正常进程运行调度地影响。
为了让中断尽快的执行,提出了将中断程序的分为上部分和下半部分。
- 上半部直接处理硬件请求,也就是硬中断,主要是负责耗时短的工作,特点是快速执行;
- 下半部是由内核触发,也就说软中断,主要是负责上半部未完成的工作,通常都是耗时比较长的事情,特点是延迟执行;
计算机如何存小数:
将存储的部分分为三个部分:
符号位:存储的是正负数的符号,正数为0,负数为1
指数位:指定了小数点在数据中的位置,指数可以是负数,也可以是正数,指数位的长度越长则数值的表达范围就越大;
尾数:小数点右侧的数字,也就是小数部分,比如二进制 1.0011 x 2^(-2),尾数部分就是 0011,而且尾数的长度决定了这个数的精度,因此如果要表示精度更高的小数,则就要提高尾数位的长度;
为了更好理解,我们举一个例子:
二进制:1010.0101
符号位:0
指数位:3+127(是浮点数需要加偏移量保证这个数为正数)
尾数:0100101
算法为: