1. 程式人生 > >一個很簡單的基於棧式過程虛擬機器的實現,它執行目標平臺【x86】的原生程式碼。

一個很簡單的基於棧式過程虛擬機器的實現,它執行目標平臺【x86】的原生程式碼。

    本文提供的 “棧式過程虛擬機器” 的實現,掛在本人的 github 上面,對想要深入瞭解 “棧式過程虛擬機器” 的人,它或許可以起到一個不錯的作用,但是本人建議一般性瞭解就可以了,另外順帶一提:如果你想要依靠它維持生活,在咋們國家是不可能不現實的,到時候你就只有 “多冷的隆冬 恨啊啊啊 在東北玩泥巴” 西北風吹啊吹;差不多這樣子的悽慘。

    如果你真想要搞 “過程虛擬機器” 這方面的東西,或許可以給你一箇中肯建議,如果可以進 “Oracle、Google、Microsoft” 裡面搞這個東西,那麼,不要猶豫的去吧,不然就自己娛樂的玩玩就可以了,不要對任何人提起這個東西否則你會被一幫逗比們說成裝逼的,突破國外技術封鎖、自主研發是一種美麗的幻想,要是國內有人忽悠喊回國XX公司一定不要回來,國內無論是技術界還是這些個公司的風氣各方面就不對,回來只有領藥丸被各種政治鬥爭的關係戶給幹了。(當然要是一幫真正搞技術的人,在一起弄家公司搞的話,這個可以有!)

    準確的說有時我並不明白,你瞭解某些東西的時候,為什麼別人總會把你視作裝逼,或許這就是這些人為什麼到現在都只是我眼中的很低階 “不入流” 的傢伙的緣故把,掛著 “高階工程師”、“架構師” 的名頭;幹著 “不入流” 的事情,講著不 “負責任” 的話,裝著各種神乎其技的逼,拿著老外的東西當個寶,別人開源就自主就懂了~!可笑吧。

    當然 “過程虛擬機器” 其實實現並不是大多數人們想象中的那般困難,好比弄弄作業系統核心拿著 “C/C++” 可勁的 “肝” 幾個月的時間也能 “肝” 出一個可執行的 “超級簡單的玩具核心” 出來,當然還有一點是成熟度、穩定性、效率方面的問題。

    我這個人非常憎惡裝逼俠,所以本文的內容不建議各位裝逼 “大佬” 觀看,畢竟我只是個 “小菜鳥” 不是嗎?您們都是大佬,當然在這塊領域讓我真正的崇拜的是搞 “模擬晶片” 真正的大佬,例如 PPU、GPU、CPU、電路晶片模擬,而不是搞 “過程虛擬機器” 的大佬,他們之間的技術層次差距應該還是會比較大的,當然 “搞過程虛擬機器” 裡面還是有真正的大佬;來自一枚小菜鳥的 “盲目的崇拜”!崇拜大佬這是很正常的事情,因為知道它們厲害,看不到之間的距離,所以知道自己有多菜。

    棧式過程虛擬機器,有很多的缺點也有很多的優點,好的一點就是說編碼與實現容易而且容易理解,但壞的一點就是說效率方面會有一些問題,大量的R/D讀寫棧記憶體,記憶體I/O的效能,影響了整體的執行效能,而且它不與 “C/C++”、.NET CLR/JIT[Win32K]、V8-Engine 那般會利用 CPU 的快取記憶體與暫存器條目之間進行優化,R/D 讀寫記憶體的效率與讀寫暫存器的效率差距還是有點明顯【記憶體的效率低於R/D讀寫CPU暫存器條目或快取的效能】,當然這是利用CPU的振盪態頻率高精度測量下的情況【100ns為一個基本單位】,Win32k PDH 中提供了可用於測量CPU振盪態頻率的C函式,不過縱然如此,它還是遠遠比 “過程直譯器” 快的多的多,這點不用質疑;

    所以 “過程虛擬機器” 又分了另外一種類別叫做 “基於暫存器的過程虛擬機器” 而,“.NET CLR/JIT[Win32K]、V8-Engine” 都屬於這一型別的過程虛擬機器,它們可以提供很不錯的目標平臺原生程式碼的效能;另外實現一個相對完整一點,super simple 的棧式過程虛擬機器,幾千行程式碼的樣子應該差不多。

    

    一般做一套 “棧式過程虛擬機器” 你首先就需要定義一套 “棧式指令集” 規範,你可以沿用現成或比較熟悉順手的指令集規範,比如我就比較習慣寫 .NET MSIL 中間語言指令集,當然這不等於 “.NET CLR/JIT[Win32K]” 就是 “棧式過程虛擬機器”,MSIL指令集是棧式的,但“.NET CLR/JIT[Win32K]” 即時編譯後的 native-code 卻不是,評價一個 “過程虛擬機器” 是不是棧式的,不是看它或者別人怎麼說,而是要拿它最終編譯並在目標平臺執行的 native-code 來說話,否則都是扯淡的,當然 SimpleClr 它只是一個很簡單的過程虛擬機器,說白了就是拿來搞這玩的東西,所以它只有幾十個被實現可用的 MSIL 中間語言指令,當然搞這個東西我大概就花了一天不到的樣子,兩個中午加一個下午的時間;

   不要把它們想的太複雜,沒有所謂複雜到不能理解的技術,它們只是有一定的門檻而已,還談不上去太超前太火星,另外沒有學習資料或文獻這不是藉口;可勁的去 “肝” 不停地去 “悟”,花時間早晚都能整懂,你可以從這裡獲取到這個虛擬機器與即時編譯並執行一段IL指令的 demo 的原始碼 https://github.com/liulilittle/SimpleClr 

    另外本人在 github 上掛的程式碼是可以直接執行的,這型別的東西說再多其實很抽象,當然你是一名搞技術的天才那另當別論,當然既然是天才靠自己 “悟” 就可以了,它們也不是很需要參考這些程式碼了,最多也就是錦上添點花而已。

namespace SimpleClr
{
    using System;
    using System.Diagnostics;
    using System.Reflection.Emit;
    using System.Runtime.InteropServices;

    public static class program
    {
        public delegate int Bootloader();

        public unsafe static void Main(string[] args)
        {
            byte[] il =
            {
                clr.Nop,
                clr.Ldc_0, // int i = 0;
                clr.Stloc_0,
                clr.Ldloc_0, // IL_003
                clr.Inc,
                clr.Dup,
                clr.Stloc_0,
                clr.Ldc,
                0, 0, 0, 0, // constant
                clr.Clt,
                clr.Brtrue_s, // i < 1000 then goto IL_003
                3, 0, 0, 0, // label
                clr.Ldloc_0,
                clr.Ret,
            };
            *(int*)Marshal.UnsafeAddrOfPinnedArrayElement(il, 8) = 100000000; // 迴圈一億次
            byte[] instructions = clr.Build(il);
            // 固定託管記憶體請求不要被移動設為非託管記憶體
            IntPtr address = GCHandle.Alloc(instructions, GCHandleType.Pinned).AddrOfPinnedObject();
            Bootloader bootloader = (Bootloader)Marshal.GetDelegateForFunctionPointer(address, typeof(Bootloader));
            // 把即時編譯的函式轉換成 bootloader
            int eax = 0;
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            {
                eax = bootloader(); // 引導即時編譯的函式執行
            }
            stopwatch.Stop();
            Console.WriteLine("ticks={0}, eax={1}", stopwatch.ElapsedMilliseconds, eax);
            Console.ReadKey(false);
        }
    }
}