主頁 >  其他 > 從案例到底層原理,徹底理解volatile可見性和禁止指令重排

從案例到底層原理,徹底理解volatile可見性和禁止指令重排

2021-04-12 12:02:13 其他

目錄

一. volatile保證可見性

二. Java記憶體模型(JMM)

1. JMM(Java Memory Model)

2. JMM 的抽象示意圖

3. 資料同步的八大原子操作

4. 流程圖解釋例1

二. volatile無法保證原子性

三. volatile禁止指令重排(保證有序性)

1. 通過例子窺探指令重排

2. 指令重排

3. as-if-serial語意

4. happens-before原則

5. 記憶體屏障

6. JMM提供的4種記憶體屏障指令

3. volatile的記憶體語意及其實作

四. 阿里巴巴Java開發手冊對餓漢式單例模式的規范

參考和參考


一. volatile保證可見性

public class TestMain {

    private static boolean flag = false;
    //private volatile static boolean flag = false;

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            flag = true;
            System.out.println("=======回圈之前=======");
            while (flag) {

            }
            System.out.println("=======回圈之后=======");
        }).start();

        Thread.sleep(2000);

        new Thread(() -> {
            System.out.println("修改flag之前...");
            System.out.println(flag); // true

            flag = false;
            System.out.println("修改flag之后...");
            System.out.println(flag);  // false 上面的執行緒沒有跳出回圈
        }).start();

    }

}

在這里,我們通過一個最簡單的例子,來引入可見性,

在運行程式之前,我們先來分析一下上述代碼的邏輯,推測一下結果,第1個執行緒啟動后,將flag置為true,然后會陷入死回圈,稍后,第2個執行緒啟動后,將flag置為false,按理來說,此時第1個執行緒應該會跳出死回圈才對,但運行結果卻不是這樣!flag是兩個執行緒的共享變數,但是第2個執行緒將flag置為false之后,并沒有被第1個執行緒所感知(不可見),

如何解決這個問題?只需要在用 volatile 來修飾flag,保證flag多個執行緒之間可見即可,

二. Java記憶體模型(JMM)

為了更容易理解可見性,有必要簡單引入一下JMM(Java Memory Model),對記憶體模型有大概的抽象了解,

1. JMM(Java Memory Model)

Java 記憶體模型,是 Java 虛擬機規范中所定義的一種記憶體模型,是一種抽象的概念,并不真實存在!它描述的是一組規則或規范,通過這組規范定義了程式中各個變數(包括實體欄位,靜態欄位和構成陣列物件的元素)的訪問方式,它屏蔽掉了底層不同計算機硬體架構下記憶體的區別,也就是說,JMM 是 JVM 中定義的一種并發編程的底層模型機制,

2. JMM 的抽象示意圖

JMM 規定:

  • 所有的共享變數都存盤于主記憶體,這里所說的變數指的是實體變數和類變數,不包含區域變數,因為區域變數是執行緒私有的,因此不存在競爭問題,
  • 每一個執行緒還存在自己的作業記憶體,執行緒的作業記憶體,保留了被執行緒使用的變數的作業副本,
  • 執行緒對變數的所有的操作(讀,取)都必須在作業記憶體中完成,而不能直接讀寫主記憶體中的變數,
  • 不同執行緒之間也不能直接訪問對方作業記憶體中的變數,執行緒間變數的值的傳遞需要通過主記憶體中轉來完成,

由于快取的存在,就可能會出現以下兩種情況而導致快取不一致:

  1. 執行緒對共享變數的修改沒有即時更新到主記憶體
  2. 執行緒沒能夠即時將共享變數的最新值同步到作業記憶體中,從而使得執行緒在使用共享變數的值時,該值并不是最新的

3. 資料同步的八大原子操作

以上關于主記憶體與作業記憶體之間的具體互動協議,即一個變數如何從主記憶體拷貝到作業記憶體、如何從作業記憶體同步到主記憶體之間的實作細節,Java記憶體模型定義了以下八種操作來完成,

簡單了解一下即可,方便我們畫圖來解釋上述的第一個例子,

  • lock (鎖定):作用于主記憶體的變數,把一個變數標記為一條執行緒獨占狀態
  • unlock (解鎖):作用于主記憶體的變數,把一個處于鎖定狀態的變數釋放出來,釋放后的變數才可以被其他執行緒鎖定
  • read (讀取):作用于主記憶體的變數,把一個變數值從主記憶體傳輸到執行緒的作業記憶體中,以便隨后的load動作使用
  • load (載入):作用于作業記憶體的變數,它把read操作從主記憶體中得到的變數值放入作業記憶體的變數副本中
  • use (使用):作用于作業記憶體的變數,把作業記憶體中的一個變數值傳遞給執行引擎
  • assign (賦值):作用于作業記憶體的變數,它把一個從執行引擎接收到的值賦給作業記憶體的變數
  • store (存盤):作用于作業記憶體的變數,把作業記憶體中的一個變數的值傳送到主記憶體中,以便隨后的write的操作
  • write (寫入):作用于作業記憶體的變數,它把store操作從作業記憶體中的一個變數的值傳送到主記憶體的變數中

https://note.youdao.com/yws/public/resource/e59837f57323a12defbb62fa837b330d/xmlnote/B6D8C1933B4546F887C48AA5C65D5A6A/14410

4. 流程圖解釋例1

如果對宣告了volatile的變數進行操作,JVM就立即會向處理器發送一條Lock前綴(硬體級別)的指令,立即將這個變數所在快取行的資料寫回到系統記憶體,但是,就算寫回到記憶體,如果其他處理器快取的值還是舊的,再執行計算操作就會有問題,所以,在多處理器下,為了保證各個處理器的快取是一致的,就會實作快取一致性協議,每個處理器通過嗅探在總線上傳播的資料來檢查自己快取的值是不是過期了(總線嗅探機制,這是實作快取一致性的常見機制), 當處理器發現自己快取行對應的記憶體地址被修改,就會將當前處理器的快取行設定成無效狀態,當處理器對這個資料進行修改操作的時候,發現快取無效,會重新從系統記憶體中重新讀取并更新到快取,

除了volatile,加鎖也能保證變數的記憶體可見性, 因為當一個執行緒進入 synchronized 代碼塊后,執行緒獲取到鎖,會清空本地記憶體,然后從主記憶體中拷貝共享變數的最新值到本地記憶體作為副本,執行代碼,又將修改后的副本值重繪到主記憶體中,最后執行緒釋放鎖,除了 synchronized 外,其它鎖也能保證變數的記憶體可見性,

二. volatile無法保證原子性

public class TestMain1 {

    public volatile static int i = 0;

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(10);

        for (int k = 0; k < 10; k++) { // 10個執行緒
            new Thread(() -> {
                for (int j = 0; j < 100000; j++) { // 10萬
//                    synchronized (TestMain1.class) {
                        i++;
//                    }
                }
                latch.countDown();
            }).start(); // 新建執行緒,并開始執行
        }

        latch.await(); // 阻塞,直到10個執行緒全部運行完成
        System.out.println(i);
    }

}

通過上述例子,可以證明volatile并不保證原子性!

上述代碼中,開啟了10個執行緒,每個執行緒對 i 自增 10 0000(10萬),如果不出現執行緒安全問題,那么最后的結果應該是 10 * 10 0000 = 100 萬,但運行結果總是不足100萬,并具有隨機性,說明了,代碼中出現了執行緒不安全的問題,

在并發場景下,變數 i 的任何改變都會立即被其他執行緒所感知,但是如果存在多條執行緒同時執行i++,仍然會出現執行緒安全問題,畢竟i++的操作,并不是原子操作,該操作是先讀取 i 的值,將 i 加1,然后將新值寫回主記憶體,如果第2個執行緒在第1個執行緒 讀取舊值寫回新值 期間讀取 i 的值,那么第2個執行緒就會與第1個執行緒一起看到同一個值,并執行相同值的加1操作,因此對于 i++ 這個非原子操作必須使用synchronized修飾,以便保證執行緒安全,需要注意的是一旦使用synchronized修飾方法后,由于synchronized本身也具備與volatile相同的特性,即可見性,因此在這種情況下就完全可以省去volatile修飾變數,

三. volatile禁止指令重排(保證有序性)

1. 通過例子窺探指令重排

我們先通過一個代碼例子,來證明一下在底層,是有可能發現指令重排的,

注:程式執行可能要花個十分鐘左右才能出結果,因為有100萬次回圈,而每次回圈都要創建執行緒(這是一個比較費時的操作)

public class TestMain3 {

    static int x = 0, y = 0;
    static int a = 0, b = 0;

    public static void main(String[] args) throws InterruptedException {
        Set<String> resultSet = new HashSet<>();

        for (int i = 0; i < 1000000; i++) { // 100萬

            x = 0; y = 0;
            a = 0; b = 0;

            Thread t1 = new Thread(() -> {
                a = y;  // 1
                x = 1;  // 2
            });

            Thread t2 = new Thread(() -> {
                b = x;  // 3
                y = 1;  // 4
            });

            t1.start();
            t2.start();

            t1.join();
            t2.join();

            resultSet.add(String.format("a=%d,b=%d", a, b));
        }

        System.out.println(resultSet);
    }

}

分析以下上述代碼,對于每一次回圈,a和b可能的值如下:

a=0,b=0:此時代碼的執行順序可能是這樣,① a=y,② b=x,③ x=1,④ y=1

a=0,b=1:此時代碼的執行順序可能是這樣,① a=y,② x=1,③ b=x,④ y=1

a=1,b=0:此時代碼的執行順序可能是這樣,① b=x,② y=1,③ a=y,④ x=1

a=1,b=1:從代碼上來看,是不可能出現的,因為從代碼上來看,代碼1 先于 代碼2,代碼3 先于 代碼4,這兩個先后次序,是我們從代碼中可以直觀看出來的,上面3種情況,代碼的執行順序都蘊含了這兩種先后次序!a為1,說明y必然為1(代碼4必然執行了),由于我們認為 “代碼3 先于 代碼4”,所以代碼3必然已經提前執行完了,那么b應該為0,不可能為1,

所以我們如果認為 “代碼1 先于 代碼2,代碼3 先于 代碼4”,那么就不可能會出現 a=1,b=1 的情況,但是程式運行結果卻出現了這種情況,說明了底層發生了指令重排!

2. 指令重排

Java語言規范規定JVM執行緒內部維持順序化語意,即只要程式的最終結果與它順序化情況的結果相等,那么指令的執行順序可以與代碼順序不一致,此程序叫指令的重排序,指令重排序的意義是什么?JVM能根據處理器特性(CPU多級快取系統、多核處理器等)適當的對機器指令進行重排序,使機器指令能更符合CPU的執行特性,最大限度的發揮機器性能,

從 Java 源代碼到最終執行的指令序列,會分別經歷下面3種重排序:

https://note.youdao.com/yws/public/resource/e59837f57323a12defbb62fa837b330d/xmlnote/D2C69B2435A64937868F86A1F127CCC0/14408

int a = 0;
// 執行緒 A
a = 1;           // 1
flag = true;     // 2

// 執行緒 B
if (flag) { // 3
  int i = a; // 4
}

單看上面的程式好像沒有問題,最后 i 的值是 1,但是為了提高性能,編譯器和處理器常常會在不改變資料依賴的情況下對指令做重排序,假設執行緒 A 在執行時被重排序成先執行代碼 2,再執行代碼 1;而執行緒 B 在執行緒 A 執行完代碼 2 后,讀取了 flag 變數,由于條件判斷為真,執行緒 B 將讀取變數 a,此時,變數 a 還根本沒有被執行緒 A 寫入,那么 i 最后的值是 0,導致執行結果不正確,那么如何程式執行結果正確呢?這里仍然可以使用 volatile 關鍵字,

這個例子中, 使用 volatile 不僅保證了變數的記憶體可見性,還禁止了指令的重排序,即保證了 volatile 修飾的變數編譯后的順序與程式的執行順序一樣,那么使用 volatile 修飾 flag 變數后,在執行緒 A 中,保證了代碼 1 的執行順序一定在代碼 2 之前,

3. as-if-serial語意

不管怎么重排序,單執行緒下程式的執行結果不能被改變,編譯器、runtime和處理器都必須遵守as-if-serial語意,為了遵守as-if-serial語意,編譯器和處理器不會對存在資料依賴關系的操作做重排序,因為這種重排序會改變執行結果,如果操作之間不存在資料依賴關系,這些操作就可能被編譯器和處理器重排序,

4. happens-before原則

只靠sychronized和volatile關鍵字來保證原子性、可見性以及有序性,那么撰寫并發程式可能會顯得十分麻煩,幸運的是,從JDK 5開始,Java使用新的JSR-133記憶體模型,提供了happens-before 原則來輔助保證程式執行的原子性、可見性以及有序性的問題,它是判斷資料是否存在競爭、執行緒是否安全的依據,happens-before 原則內容如下:

  1. 程式順序原則:即在一個執行緒內必須保證語意串行性,也就是說按照代碼順序執行,
  2. 鎖規則:解鎖(unlock)操作必然發生在后續的同一個鎖的加鎖(lock)之前,也就是說,如果對于一個鎖解鎖后,再加鎖,那么加鎖的動作必須在解鎖動作之后(同一個鎖),
  3. volatile規則:volatile變數的寫,先發生于讀,這保證了volatile變數的可見性,簡單的理解就是,volatile變數在每次被執行緒訪問時,都強迫從主記憶體中讀該變數的值,而當該變數發生變化時,又會強迫將最新的值重繪到主記憶體,任何時刻,不同的執行緒總是能夠看到該變數的最新值,
  4. 執行緒啟動規則:執行緒的start()方法先于它的每一個動作,即如果執行緒A在執行執行緒B的start方法之前修改了共享變數的值,那么當執行緒B執行start方法時,執行緒A對共享變數的修改對執行緒B可見
  5. 傳遞性:A先于B ,B先于C 那么A必然先于C
  6. 執行緒終止規則:執行緒的所有操作先于執行緒的終結,Thread.join()方法的作用是等待當前執行的執行緒終止,假設在執行緒B終止之前,修改了共享變數,執行緒A從執行緒B的join方法成功回傳后,執行緒B對共享變數的修改將對執行緒A可見,
  7. 執行緒中斷規則:對執行緒 interrupt()方法的呼叫先行發生于被中斷執行緒的代碼檢測到中斷事件的發生,可以通過Thread.interrupted()方法檢測執行緒是否中斷,
  8. 物件終結規則:物件的建構式執行,結束先于finalize()方法

5. 記憶體屏障

volatile關鍵字另一個作用就是禁止指令重排優化,從而避免多執行緒環境下程式出現亂序執行的現象,關于指令重排優化前面已詳細分析過,這里主要簡單說明一下volatile是如何實作禁止指令重排優化的,先了解一個概念,記憶體屏障(Memory Barrier),

記憶體屏障,又稱記憶體柵欄(Barrier),是一個CPU指令,它的作用有兩個:

  1. 如果在指令間插入一條Memory Barrier則會告訴編譯器和CPU,不管什么指令都不能和這條Memory Barrier指令重排序,也就是說禁止在記憶體屏障前后的指令執行重排序優化,
  2. 強制刷出各種CPU的快取資料,因此任何CPU上的執行緒都能讀取到這些資料的最新版本

6. JMM提供的4種記憶體屏障指令

由于硬體層面的記憶體屏障的實作,不同的硬體架構,對應有不同的機器指令,JMM為了屏蔽了這種底層硬體平臺的差異,提供了四類記憶體屏障指令,來為不同的硬體架構生成相應的記憶體屏障的機器碼,

屏障型別

指令示例

說明

LoadLoad

Load1; LoadLoad; Load2

保證load1的讀取操作在load2及后續讀取操作之前執行

StoreStore

Store1; StoreStore; Store2

在store2及其后的寫操作執行前,保證store1的寫操作已重繪到主記憶體

LoadStore

Load1; LoadStore; Store2

在stroe2及其后的寫操作執行前,保證load1的讀操作已讀取結束

StoreLoad

Store1; StoreLoad; Load2

保證store1的寫操作已重繪到主記憶體之后,load2及其后的讀操作才能執行

3. volatile的記憶體語意及其實作

volatile關鍵字的記憶體語意如下:

  • 【可見性】保證被volatile修飾的共享變數對所有執行緒總數可見的,也就是當一個執行緒修改了一個被volatile修飾共享變數的值,新值總是可以被其他執行緒立即得知,
  • 【有序性】禁止指令重排序優化,

Java編譯器會在生成指令系列時在適當的位置會插入記憶體屏障指令來禁止特定型別的處理器重排序,為了實作volatile記憶體語意,JMM針對編譯器制定的volatile重排序規則表

第一個操作

第二個操作:普通讀寫

第二個操作:volatile讀

第二個操作:volatile寫

普通讀寫

可以重排

可以重排

不可以重排

volatile讀

不可以重排

不可以重排

不可以重排

volatile寫

可以重排

不可以重排

不可以重排

舉例來說,第二行最后一個單元格的意思是:在程式中,當第一個操作為普通變數的讀或寫時,如果第二個操作為volatile寫,則編譯器不能重排序這兩個操作,

從上圖可以看出:

  • 當第二個操作是volatile寫時,不管第一個操作是什么,都不能重排序,這個規則確保volatile寫之前的操作不會被編譯器重排序到volatile寫之后,
  • 當第一個操作是volatile讀時,不管第二個操作是什么,都不能重排序,這個規則確保volatile讀之后的操作不會被編譯器重排序到volatile讀之前,
  • 當第一個操作是volatile寫,第二個操作是volatile讀或寫時,不能重排序,

為了實作volatile的記憶體語意,編譯器在生成位元組碼時,會在指令序列中插入記憶體屏障來禁止特定型別的處理器重排序,對于編譯器來說,發現一個最優布置來最小化插入屏障的總數幾乎不可能,為此,JMM采取保守策略,下面是基于保守策略的JMM記憶體屏障插入策略,

  • 在每個volatile寫操作的前面插入一個StoreStore屏障,禁止上面的普通寫和下面的volatile寫重排序,
  • 在每個volatile寫操作的后面插入一個StoreLoad屏障,防止上面的volatile寫與下面可能有的volatile讀/寫重排序,
  • 在每個volatile讀操作的后面插入一個LoadLoad屏障,禁止上面的volatile讀和下面所有的普通讀操作重排序,
  • 在每個volatile讀操作的后面插入一個LoadStore屏障,禁止上面的volatile讀和下面所有的普通寫操作重排序,

上述記憶體屏障插入策略非常保守,但它可以保證在任意處理器平臺,任意的程式中都能得到正確的volatile記憶體語意,【在理解4種屏障指令的含義,應該也容易理解為什么要這么插入,之后也會有例子來幫助理解】

下面是保守策略下,volatile寫插入記憶體屏障后生成的指令序列示意圖

0

上圖中StoreStore屏障可以保證在volatile寫之前,其前面的所有普通寫操作已經對任意處理器可見了,這是因為StoreStore屏障將保障上面所有的普通寫在volatile寫之前重繪到主記憶體,

這里比較有意思的是,volatile寫后面的StoreLoad屏障,此屏障的作用是避免volatile寫與 后面可能有的volatile讀/寫操作重排序,因為編譯器常常無法準確判斷在一個volatile寫的后面 是否需要插入一個StoreLoad屏障(比如,一個volatile寫之后方法立即return),為了保證能正確 實作volatile的記憶體語意,JMM在采取了保守策略:在每個volatile寫的后面,或者在每個volatile 讀的前面插入一個StoreLoad屏障,從整體執行效率的角度考慮,JMM最終選擇了在每個 volatile寫的后面插入一個StoreLoad屏障,因為volatile寫-讀記憶體語意的常見使用模式是:一個 寫執行緒寫volatile變數,多個讀執行緒讀同一個volatile變數,當讀執行緒的數量大大超過寫執行緒時,選擇在volatile寫之后插入StoreLoad屏障將帶來可觀的執行效率的提升,從這里可以看到JMM 在實作上的一個特點:首先確保正確性,然后再去追求執行效率,

下圖是在保守策略下,volatile讀插入記憶體屏障后生成的指令序列示意圖

0

上圖中LoadLoad屏障用來禁止處理器把上面的volatile讀與下面的普通讀重排序,LoadStore屏障用來禁止處理器把上面的volatile讀與下面的普通寫重排序,

上述volatile寫和volatile讀的記憶體屏障插入策略非常保守,在實際執行時,只要不改變 volatile寫-讀的記憶體語意,編譯器可以根據具體情況省略不必要的屏障,、

下面通過具體的示例代碼進行說明,

class VolatileBarrierExample {
       int a;
       volatile int v1 = 1;
       volatile int v2 = 2;
       void readAndWrite() {
           int i = v1;      // 第一個volatile讀
           int j = v2;       // 第二個volatile讀
           a = i + j;         // 普通寫
           v1 = i + 1;       // 第一個volatile寫
          v2 = j * 2;       // 第二個 volatile寫
       }
}

針對readAndWrite()方法,編譯器在生成位元組碼時可以做如下的優化,

0

注意,最后的StoreLoad屏障不能省略,因為第二個volatile寫之后,方法立即return,此時編 譯器可能無法準確斷定后面是否會有volatile讀或寫,為了安全起見,編譯器通常會在這里插 入一個StoreLoad屏障,

上面的優化針對任意處理器平臺,由于不同的處理器有不同“松緊度”的處理器記憶體模 型,記憶體屏障的插入還可以根據具體的處理器記憶體模型繼續優化,以X86處理器為例,圖3-21 中除最后的StoreLoad屏障外,其他的屏障都會被省略,

前面保守策略下的volatile讀和寫,在X86處理器平臺可以優化成如下圖所示,前文提到過,X86處理器僅會對寫-讀操作做重排序,X86不會對讀-讀、讀-寫和寫-寫操作 做重排序,因此在X86處理器中會省略掉這3種操作型別對應的記憶體屏障,在X86中,JMM僅需 在volatile寫后面插入一個StoreLoad屏障即可正確實作volatile寫-讀的記憶體語意,這意味著在 X86處理器中,volatile寫的開銷比volatile讀的開銷會大很多(因為執行StoreLoad屏障開銷會比

較大),

0

四. 阿里巴巴Java開發手冊對餓漢式單例模式的規范

public class DoubleCheckLock {
    // 阿里巴巴Java開發手冊建議在該變數前加上volatile修飾
    private volatile static DoubleCheckLock instance;

    private DoubleCheckLock(){}

    public static DoubleCheckLock getInstance(){
        //第一次檢測
        if (instance==null){
            //同步
            synchronized (DoubleCheckLock.class){
                if (instance == null){
                    //多執行緒環境下可能會出現問題的地方
                    instance = new  DoubleCheckLock();
                }
            }
        }
        return instance;
    }
}

這種餓漢式單例模式有個很著名的名字 “雙重檢測鎖”,有兩個思考點:

1. 為什么需要兩個if?

2. 為什么阿里巴巴Java開發手冊建議在單例變數前加上volatile修飾?

略......待補...... 大家可以在評論區討論一下,

參考和參考

https://zhuanlan.zhihu.com/p/138819184

https://www.jianshu.com/p/157279e6efdb

《Java并發編程藝術》

圖靈學院課程資料

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/275111.html

標籤:其他

上一篇:Servlet的生命周期

下一篇:ConcurrentHashMap的實作原理(JDK1.7和JDK1.8)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more