计算机组成原理

计算机组成原理

计算机中最主要的两个组成部分是:CPU和内存
其他都是一些外接设备,而这些设备之间依靠总线进行连接

总线

总线一共分为三类(当然还有一些链接外设的扩展总线,这些不需要了解太多):数据总线(传递数据)、控制总线(传递控制信号)、地址总线(传递命令地址)

CPU组成

ALU(Arithmetic & Logic Unit计算单元)
Register(寄存器,用来存储CPU从内存中拿到的数据)
PC(program countor 程序计数器,存储当前指令的地址)
相信学习java编程的小伙伴都知道,如果是多线程的程序,多个线程之间会抢时间片,其实时间片的概念就是PC中的地址不断切换的过程,线程切换时会把当前线程放到缓存中,切换到新的线程执行。
CU(Control Unit 控制单元)
MMU(Memory Management Unit 内存管理单元)
MMU的主要功能是内存映射,我们都知道现在我们的程序访问的内存都是虚拟内存(在下文中有讲到),虚拟内存中按照数据类型进行分段,数据在虚拟空间的地址(线性地址)=偏移量(数据存放在段中的地址)+段的基地址(分配在哪个段上),MMU+OS(操作系统)将线性地址映射到物理地址。
缓存(Cache)
缓存一共分为三级,其中一级和二级缓存是CPU独有的,一个CPU一个缓存,三级缓存是多个CPU共享的,数据的抓取过程大致是:先从一级缓存中取数据,如果一级缓存中没有,再到二级缓存中取数据,还没有再到三级缓存中取数据,如果三级缓存中再没有,最后才从内存(内存中没有从硬盘中取)中拿数据并把数据放到缓存中,缓存在读取数据的时候是按块(缓存行,Intel CPU缓存行大小是64字节)读取的,不是一个字节一个字节的读取(IO操作太浪费时间)。
核的概念
一个核中包含寄存器、PC、ALU。
超线程
网上一些超线程的概念其实就是在一个核中由一个ALU对应多个寄存器和PC,就是把指令地址的存储地址和数据的存储地址分成多份,由一个ALU计算,省去了线程切换的时间。
MESI缓存一致性协议
保证修改数据时缓存中的数据一致性,当一个CPU修改两个CPU缓存中共有的数据,另一个CPU缓存中的数据就会被标记为失效数据,必须在内存中再次读取。举个例子:
在内存中有一个缓存行,缓存行中有A和B两个数据,现在两个CPU同时读取了这个缓存行,一个CPU中的线程修改了A数据,那么另一个CPU中的A和B都是失效数据,需要从内存中重新读取。
缓存锁
还有一些无法被缓存的数据和跨多个缓存行的数据,就不能使用缓存一致性协议,必须使用缓存锁(缓存锁锁的是总线,锁住总线之后,内存只能单个CPU访问,当这个CPU修改完数据,其他CPU才能继续访问内存,效率低,但是没办法)
缓存行对齐
对于一些敏感的数据,就是高竞争,所有CPU都可以进行修改的数据,为了防止伪共享(两个敏感数据在一个缓存行,频繁修改),采用缓存行对齐,在JDK7中,会在这个数据的前面和后面都放7个long类型的数据(一个long类型占8个字节,缓存行64字节),这样就可以保证这个缓存行中只有这一个敏感数据,提高效率;而在JDK8中加入了一个@Contended(直接加到成员变量上)注解来解决伪共享要使用这个注解需要配置JVM参数 -XX:-RestrictContended。

CPU乱序执行(指令的重排序)
CPU在执行两条指令的时候,如果这两条指令没有相互依赖关系,那么在执行第一条指令等待返回的这段时间就会去执行第二条指令,增加程序执行效率,这就是指令的重排序,比如我们创建对象的时候经历了加载(半初始化,对象已经创建并赋默认值)、初始化(赋初始值)、链接(赋值给引用)的过程,而在程序真正执行的时候,链接和初始化的位置是可以互换的。
禁止指令重排序的方法:
CPU层面:Intel->原语(mfence、lfence、sfence),锁总线
JVM层面:8条hanppens-before(java中默认禁止重排序的规则)原则,4个内存屏障(volatile关键字 读读屏障、读写屏障、写读屏障、写写屏障)
美团面试题
DCL(Double Check Lock)单例需不需要使用volatile?
Volatile作用:禁止指令重排序、线程可见性
双检锁单例,单例就是在内存中只会创建一个这样的对象,在多线程中,DCL单例为什么要加双检锁,因为在多个线程争抢一个单例对象时,如果不加锁,会创建多个单例对象,所以在if判断时要加锁,而这样的话,多个线程争抢锁时会消耗CPU资源,所以在加锁之后在外层再加一层if判断。
DCL单例要加volatile关键字,如果不加,一个线程在创建对象时进行了代码重排序,先创建了一个对象,如果这个对象中有一些属性值,这些属性值被赋了默认值,还没有初始化的时候,发生指令重排,将半初始化的对象赋值给了他的引用,这个时候其他线程进来时判断对象不为null,就会把这个半初始化的对象当成已经初始化完成的对象。
UMA(Uniform Memory Access 统一内存访问)
所有CPU都参与竞争一个内存,竞争激烈。
NUMA(Non Uniform Memory Access 非统一内存访问)
分配内存会优先分配当前线程所在CPU最近的内存,提高效率。

内存管理

内存管理的发展历程

Windows9x - 多个进程装入内存问题:内存不够用、互相打扰
为了解决这两个问题,诞生了现在的内存管理:虚拟地址、分页装入、软硬件结合寻址。

分页(解决内存不够用问题)
内存中分成固定大小的页框(一般4k),把程序(硬盘上的)分成4k大小的块,用到那一块,加载那一块。
LRU算法(Least Recently Used)
如果在内存加载的过程中,内存满了,会把最不常用的一块加载到swap分区(交换分区),把新的一块加载进来,这就是LRU算法,想要实现LRU算法并不难,难的是让他的时间复杂度为O(1),这里采用哈希表(保证查询操作O(1))+双向链表(保证排序操作和新增操作O(1)),哈希表的value指向内存块,每个内存块再用双向链表连接起来,把最不常用的一个放到头部,依次进行排序,用到那个就把他放到尾部,这样在头部就永远时最不常用的一块,为什么要用双向链表,因为在执行排序操作时,单向链表只有单向的指针,无法同时确定左右两个元素。
虚拟内存(解决相互打扰问题)
1、为了保证程序之间互不影响,让进程工作在虚拟空间,程序中用到的空间地址不再是物理地址,而是虚拟地址,程序直接访问的内存是虚拟内存。
2、站在虚拟内存的角度,进程是独享整个系统+CPU的,虚拟内存比实际内存大得多
4、内存映射:偏移量(存放在段中的位置)+段的基地址=线性地址(虚拟地址)
5、线性地址通过OS(操作系统)+MMU(内存管理单元)映射到物理地址。
缺页中断
在内存中加载分页的时候,内存中没有,产生缺页异常,会由内核处理并去硬盘上加载。

进程、线程和纤程

面试题:进程和线程有什么区别?
进程(进程描述符:PCB Process Control Block,存储进程的一些基本信息)就是一个程序运行起来的状态,线程(线程就是特殊的进程)是一个进程中不同的执行路径。
专业回答:进程是OS分配资源的基本单位,一个进程拥有自己独立的内存空间;线程是执行调度的基本单位,线程共享进程的内存空间,没有自己独立的内存空间。
纤程的概念(目前Java没有内置的类库支持纤程)
纤程是线程中的线程,线程是运行在内核态,由操作系统进行调度的,而纤程是在线程的基础上运行在用户态,由虚拟机进行调度的,比线程更加轻量,适合计算并发多(10w+)、计算量小的操作
僵尸进程
如果一个父进程有多个子进程,父进程负责维护子进程的PCB,子进程执行结束,而PCB没有删除,就是僵尸进程。
孤儿进程
在子进程没有执行结束的时候,父进程退出,子进程就成为孤儿进程,孤儿进程会把公共父进程进程作为父进程。

Linux线程调度策略
Linux2.5:经典Unix O(1)调度策略,分配的时间完全相同,对服务器友好,对交互不友好。
Linux2.6:采用CFS完全公平的线程调度策略,根据线程优先级按照比例分配时间片的执行时间,如果一个线程实际的执行时间少于分配时间,该线程优先执行。
进程类型:
IO密集型:大部分时间用于等待IO
CPU密集型:大部分时间做计算
进程优先级:
实时进程(0-99)>普通进程(-20-19)
Linux默认调度策略:
实时进程(需要使用c语言进行编写):使用SCHED_FIFO(先进先出,谁先抢到谁执行)和SCHED_RR(优先级相同时采用,轮询机制)两种。
普通进程:CFS

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值