操作系统第二课:CPU知识补充、操作系统基本知识

CPU知识补充

CPU的乱序执行

在这里插入图片描述
乱序的证明

package com.mashibing.jvm.c3_jmm;

public class T04_Disorder {
    
    
    private static int x = 0, y = 0;
    private static int a = 0, b =0;

    public static void main(String[] args) throws InterruptedException {
    
    
        int i = 0;
        for(;;) {
    
    
            i++;
            x = 0; y = 0;
            a = 0; b = 0;
            Thread one = new Thread(new Runnable() {
    
    
                public void run() {
    
    
                    //由于线程one先启动,下面这句话让它等一等线程two. 读着可根据自己电脑的实际性能适当调整等待时间.
                    //shortWait(100000);
                    a = 1;
                    x = b;
                }
            });

            Thread other = new Thread(new Runnable() {
    
    
                public void run() {
    
    
                    b = 1;
                    y = a;
                }
            });
            one.start();other.start();
            one.join();other.join();
            String result = "第" + i + "次 (" + x + "," + y + ")";
            if(x == 0 && y == 0) {
    
    
                System.err.println(result);
                break;
            } else {
    
    
                //System.out.println(result);
            }
        }
    }


    public static void shortWait(long interval){
    
    
        long start = System.nanoTime();
        long end;
        do{
    
    
            end = System.nanoTime();
        }while(start + interval >= end);
    }
}

乱序执行可能会出现问题(存在于多线程)->DCL为什么要volatile?->禁止指令重排序

创建对象时,会有一个中间态,此时成员变量都是默认值
在这里插入图片描述
如果不加volatile,可能会发生指令重排,对象指针提前指向赋默认值的变量,此时其他线程来拿该对象,则会返回半初始化状态的对象
在这里插入图片描述
CPU层面如何禁止指令重排序?
在这里插入图片描述

有序性保障

CPU层级

  • X86CPU内存屏障

    • sfence:在sfence指令前的写操作必须在sfence指令后的写操作前完成
    • Infence:在sfence指令前的读操作必须在sfence指令后的读操作前完成
    • mfence:在mfence指令前的读写操作必须在mfence指令后的读写操作前完成
  • intel lock汇编指令
    原子指令,如X86上的“lock……”指令是一个Full Barrier,执行时会锁住内存子系统来确保执行顺序,甚至跨多个CPU。
    SoftWare Locks通常使用了内存屏障或原子指令来实现变量可见性和保持程序顺序。

JVM层级

JSR内存屏障

  • LoadLoad屏障:
    对于这样的语句Load1,LoadLoad,Load2
    ​ 在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。

  • StoreStore屏障:
    对于这样的语句Store1,StoreStore,Store2
    在Store2及后续写入操作执行前,保证Store1的写入操作对其他处理器可见。

  • LoadStore屏障:
    对于这样的语句Load1,LoadStore,Store2
    在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。

  • StoreLoad屏障:
    对于这样的语句Store1,StoreLoad,Load2
    在load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。

拓展:volatile的实现细节

在JVM层面
在这里插入图片描述
底层屏障由lock指令实现的

拓展:happens-before原则

在这里插入图片描述
as if serial:不管如何重排序,单线程执行结果不会改变,看上去像是serial。

JVM层级:8个hanppens-before原则 4个内存屏障 (LL LS SL SS)

合并写

在这里插入图片描述
一般是4个字节
由于ALU速度太快,所以在写入L1的同时,写入一个WC Buffer,满了之后,再直接更新到L2。

package com.mashibing.juc.c_029_WriteCombining;

public final class WriteCombining {
    
    

    private static final int ITERATIONS = Integer.MAX_VALUE;
    private static final int ITEMS = 1 << 24;
    private static final int MASK = ITEMS - 1;

    private static final byte[] arrayA = new byte[ITEMS];
    private static final byte[] arrayB = new byte[ITEMS];
    private static final byte[] arrayC = new byte[ITEMS];
    private static final byte[] arrayD = new byte[ITEMS];
    private static final byte[] arrayE = new byte[ITEMS];
    private static final byte[] arrayF = new byte[ITEMS];

    public static void main(final String[] args) {
    
    

        for (int i = 1; i <= 3; i++) {
    
    
            System.out.println(i + " SingleLoop duration (ns) = " + runCaseOne());
            System.out.println(i + " SplitLoop  duration (ns) = " + runCaseTwo());
        }
    }

    public static long runCaseOne() {
    
    
        long start = System.nanoTime();
        int i = ITERATIONS;
		
		//这个循环一共写6次
        while (--i != 0) {
    
    
            int slot = i & MASK;
            byte b = (byte) i;
            arrayA[slot] = b;
            arrayB[slot] = b;
            arrayC[slot] = b;
            arrayD[slot] = b;
            arrayE[slot] = b;
            arrayF[slot] = b;
        }
        return System.nanoTime() - start;
    }

    public static long runCaseTwo() {
    
    
        long start = System.nanoTime();
        int i = ITERATIONS;
        //每个循环4次写操作
        while (--i != 0) {
    
    
            int slot = i & MASK;
            byte b = (byte) i;
            arrayA[slot] = b;
            arrayB[slot] = b;
            arrayC[slot] = b;
        }
        i = ITERATIONS;
        while (--i != 0) {
    
    
            int slot = i & MASK;
            byte b = (byte) i;
            arrayD[slot] = b;
            arrayE[slot] = b;
            arrayF[slot] = b;
        }
        return System.nanoTime() - start;
    }
}

NUMA

UMA:多个CPU共享同一个内存
缺点:不易拓展,CPU数量增多后会引起内存访问冲突加剧。CPU很多资源浪费在争夺内存地址上。4颗合适
在这里插入图片描述
NUMA(non uniform memory access):非统一访问内存。从主板上会将cpu与内存分为不同的槽
在这里插入图片描述
ZGC使用NUMA-Aware:分配内存会优先分配该线程所在CPU的最近内存

操作系统基本知识

启动

通电 -> bios uefi 工作 -> 自检 -> 到硬盘固定位置加载bootloader -> 读取可配置信息 -> CMOS
在这里插入图片描述

什么是操作系统

在这里插入图片描述

操作系统主要做什么

在这里插入图片描述

简要结构

在这里插入图片描述

内核kernel

在这里插入图片描述

宏内核

用途:PC phone
在这里插入图片描述

微内核

用途:弹性部署 5G IoT
核心就是进程调度
在这里插入图片描述

外核(了解即可)

用途:科研 实验中 为应用定制操作系统 (多租户 request-based GC JVM)
在这里插入图片描述

VMM

虚拟机监控器
在这里插入图片描述

用户态与内核态

cpu分不同的指令级别
在这里插入图片描述

linux内核跑在ring 0级,用户程序跑在ring 3级,至于ring 1、ring 2都不使用;对于系统的关键访问,需要经过kernel的同意,保证系统健壮性

内核执行的操作 - > 只有200多个系统调用 sendfile read write pthread fork

JVM -> 站在OS老大的角度,就是个普通程序,因此在用户态

猜你喜欢

转载自blog.csdn.net/upset_poor/article/details/123191143