精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

如何用C語(yǔ)言實(shí)現(xiàn)一個(gè)虛擬機(jī)?

云計(jì)算 虛擬化
由于我喜歡在較低級(jí)別(Low-level)的應(yīng)用中(編譯器,解釋器,解析器,虛擬機(jī)等等)工作,所以我覺得寫一篇關(guān)于用C編程語(yǔ)言構(gòu)建虛擬機(jī)的文章,是非常有必要的。我認(rèn)為這篇文章除了能夠讓你了解到虛擬機(jī)的工作原理外,還可以讓你了解到較低級(jí)別的編程過(guò)程。

由于我喜歡在較低級(jí)別(Low-level)的應(yīng)用中(編譯器,解釋器,解析器,虛擬機(jī)等等)工作,所以我覺得寫一篇關(guān)于用C編程語(yǔ)言構(gòu)建虛擬機(jī)的文章,是非常有必要的。我認(rèn)為這篇文章除了能夠讓你了解到虛擬機(jī)的工作原理外,還可以讓你了解到較低級(jí)別的編程過(guò)程。

準(zhǔn)備內(nèi)容

  • 使用的編譯器類型:我正在使用的是clang,它是輕量級(jí)編譯器,但你可以使用任何現(xiàn)代編譯器;
  • 文本編輯器:我建議當(dāng)編寫C語(yǔ)言時(shí),通過(guò)IDE編輯文本編輯器,我將使用Emacs;
  • 基本的編程知識(shí):比如什么是變量,流量控制,函數(shù),結(jié)構(gòu)等;
  • GNU Make:GNU Make主要用于自動(dòng)化構(gòu)建可執(zhí)行程序(庫(kù)文件),這樣我們就不需要在終端中一遍又一遍地編寫相同的命令來(lái)編譯代碼。Make的功能包括:自動(dòng)化構(gòu)建和安裝;增量編譯及自動(dòng)更新;適用于多語(yǔ)言,比如c/c++、java、php等;支持自定義功能擴(kuò)展(只要有意義,都是可以放到makefile中)。

[[233734]]

為什么你應(yīng)該寫一個(gè)虛擬機(jī)?

以下是你應(yīng)該編寫虛擬機(jī)的一些原因:

1.你需要更深入地了解計(jì)算機(jī)的工作方式,本文將幫助你了解你的計(jì)算機(jī)在較低級(jí)別的環(huán)境中是如何運(yùn)行工作的?而虛擬機(jī)則提供了一個(gè)非常簡(jiǎn)單的抽象層;

2.順便了解一些虛擬機(jī)的知識(shí);

3.深入了解一下編程語(yǔ)言的工作原理,現(xiàn)在的各種語(yǔ)言都針對(duì)虛擬機(jī),比如JVM,Lua VM,F(xiàn)aceBook 的 Hip—Hop VM(PHP/Hack)等。

指令集

指令集會(huì)相對(duì)簡(jiǎn)單,我將簡(jiǎn)要介紹一下,例如如何從寄存器中移動(dòng)值或跳轉(zhuǎn)到其他指令。

假設(shè)我們的虛擬機(jī)有一組寄存器:A,B,C,D,E和F,且這些都是通用寄存器,這意味著它們可以用于存儲(chǔ)任何東西。這與專用寄存器不同,例如在x86上,ip, flag, ds, …程序是只讀指令集。如果虛擬機(jī)是一個(gè)基于棧的虛擬機(jī),這意味著它有一個(gè)我們可以壓棧和彈出值的棧,另外,該虛擬機(jī)還有一些我們也可以使用的寄存器。基于棧的虛擬機(jī)比基于寄存器的虛擬機(jī)實(shí)現(xiàn)起來(lái)要簡(jiǎn)單得多。

下面是我將要實(shí)施的一個(gè)指令集的示例:

  1. PSH 5       ; pushes 5 to the stack 
  2. PSH 10      ; pushes 10 to the stack 
  3. ADD         ; pops two values on top of the stack, adds them pushes to stack 
  4. POP         ; pops the value on the stack, will also print it for debugging 
  5. SET A 0     ; sets register A to 0 
  6. HLT         ; stop the program 

以上就是我的指令集,請(qǐng)注意,POP指令將打印我們彈出的指令,其中很多是調(diào)試用的。ADD會(huì)將結(jié)果壓棧到棧,所以我們可以從棧中的彈出值來(lái)驗(yàn)證它是否存在。我還在其中包含了一條SET指令,這樣你就可以了解如何訪問和寫入寄存器了。你也可以嘗試執(zhí)行像MOV A,B(將值A(chǔ)移至B)的指令, HLT是顯示我已經(jīng)完成程序執(zhí)行的指令。

虛擬機(jī)如何工作?

其實(shí)虛擬機(jī)比你想象的要簡(jiǎn)單,它的工作模式遵循一個(gè)簡(jiǎn)單的規(guī)律,即“指令周期(instruction cycle)”,整個(gè)過(guò)程包括讀取、解碼、執(zhí)行三大塊。首先,你要讀取指令集,然后才能解碼指令并執(zhí)行解碼后的指令。

項(xiàng)目結(jié)構(gòu)

在我開始編程之前,需要做一些準(zhǔn)備工作。我需要一個(gè)文件夾來(lái)放置項(xiàng)目,我喜歡將項(xiàng)目放置于~/Dev下。另外,我需要一個(gè)C編譯器(我使用的是 clang 3.4)。以下是我在終端中設(shè)置我的項(xiàng)目的過(guò)程,假設(shè)你已經(jīng)擁有一個(gè)?/ dev /目錄,不過(guò)你可以把它放到任何你想要的位置。

  1. $ cd ~/dev/ 
  2. $ mkdir mac 
  3. $ cd mac 
  4. $ mkdir src 

上面就是我把cd放到我的~/dev目錄的過(guò)程,首先,我會(huì)創(chuàng)建一個(gè)目錄(我稱之為VM“mac”)。然后,我進(jìn)入該目錄并創(chuàng)建我的src目錄,這個(gè)目錄被用于存放代碼。

Makefile文件

我的makefile相對(duì)比較簡(jiǎn)單,由于該文件不需要將任何東西分隔成多個(gè)文件,所以其中也不會(huì)包含任何東西,我只需要用一些標(biāo)志來(lái)編譯文件即可。

  1. SRC_FILES = main.c 
  2. CC_FLAGS = -Wall -Wextra -g -std=c11 
  3. CC = clang 
  4.  
  5. all
  6.     ${CC} ${SRC_FILES} ${CC_FLAGS} -o mac 

現(xiàn)在這應(yīng)該足夠了,你以后可以隨時(shí)改進(jìn)它,但只要它能完成這項(xiàng)工作,我們應(yīng)該沒問題。

指令編程

現(xiàn)在就可以開始編寫虛擬機(jī)的代碼了。首先,為了解釋指令編程,我必須用到一個(gè)枚舉,因?yàn)槲覀兊闹噶罨旧鲜菑?到X的數(shù)字。事實(shí)上,匯編程序?qū)@取你的匯編文件,并將所有操作轉(zhuǎn)換為對(duì)應(yīng)的數(shù)字。例如,如果你為mac編寫一個(gè)匯編程序,它將把所有MOV操作轉(zhuǎn)換為數(shù)字0。

  1. typedef enum { 
  2.     PSH, 
  3.     ADD
  4.     POP, 
  5.     SET
  6.     HLT 
  7. } InstructionSet; 

現(xiàn)在我可以將測(cè)試程序存儲(chǔ)為一個(gè)數(shù)組,然后寫一個(gè)簡(jiǎn)單的程序用于測(cè)試,比如將5和6相加,然后將它們用POP指令打印出來(lái)。如果你愿意,你可以定義一個(gè)指令將棧頂?shù)闹荡蛴〕鰜?lái)。

指令應(yīng)該存儲(chǔ)成一個(gè)數(shù)組,我將在文檔的頂部定義它。但你可以把它放在一個(gè)頭文件中,以下是我的測(cè)試程序。

  1. const int program[] = { 
  2.     PSH, 5, 
  3.     PSH, 6, 
  4.     ADD
  5.     POP, 
  6.     HLT 
  7. }; 

上面的程序會(huì)將5和6壓棧入棧,執(zhí)行add指令,該指令將彈出棧中的兩個(gè)值,將它們加在一起并將結(jié)果壓棧回棧。然后會(huì)彈出結(jié)果,出于調(diào)試目的,我的彈出指令將打印這兩個(gè)值。

***,HLT指令意味著終止程序。如果我們要控制流程,可以隨時(shí)終止程序。不過(guò),如果我們沒有任何指示,我們的虛擬機(jī)***也將自然終止。

現(xiàn)在我實(shí)現(xiàn)了虛擬機(jī)的讀取、解碼、執(zhí)行的過(guò)程。但是要記住,我沒有解碼任何東西,因?yàn)槲医o出的是原始指令。

獲取當(dāng)前指令

因?yàn)槲乙褜⒊绦虼鎯?chǔ)為一個(gè)數(shù)組,所以獲取當(dāng)前指令很簡(jiǎn)單。虛擬機(jī)有一個(gè)計(jì)數(shù)器,通常稱為程序計(jì)數(shù)器,不過(guò)有時(shí)也叫做指令指針等,通常它們分別縮寫為PC或IP。

現(xiàn)在,我只需在代碼頂部創(chuàng)建一個(gè)名為ip的變量,并將其設(shè)置為0。

  1. int ip = 0; 

這個(gè)ip代表指令指針,由于我已經(jīng)將程序本身存儲(chǔ)為一個(gè)整數(shù)數(shù)組,固ip變量作為數(shù)組中的索引,表示當(dāng)前正在執(zhí)行的指令。

  1. int ip = 0; 
  2.  
  3. int main() { 
  4.     int instr = program[ip]; 
  5.     return 0; 

如果打印變量instr,則PSH將顯示為0,因?yàn)樽兞縤nstr是我們枚舉里的***個(gè)值。不過(guò),我們也可以寫一個(gè)取回函數(shù),如下所示:

  1. int fetch() { 
  2.     return program[ip]; 

該函數(shù)在被調(diào)用時(shí)將返回當(dāng)前指令。那么,如果我們想要下一條指令呢?我們只要增加指令指針即可。

  1. int main() { 
  2.     int x = fetch(); // PSH 
  3.     ip++; // increment instruction pointer 
  4.     int y = fetch(); // 5 

那么我們?cè)撊绾螌?shí)現(xiàn)自動(dòng)化呢?我們知道程序只有執(zhí)行通過(guò)HLT指令時(shí),才會(huì)停止,所以該程序本身就是一個(gè)***循環(huán)。

  1. #include <stdbool.h>  
  2.  
  3. bool running = true
  4.  
  5. int main() { 
  6.     while (running) { 
  7.        int x = fetch(); 
  8.        if (x == HLT) running = false
  9.        ip++; 
  10.     } 

我目前要做的是循環(huán)遍歷每條指令,檢查指令的值是否為HLT,如果是,則停止循環(huán),否則就不斷重復(fù)。

一條指令的評(píng)估

這是虛擬機(jī)的運(yùn)行要點(diǎn),其實(shí)虛擬機(jī)非常簡(jiǎn)單,你可以編寫一個(gè)巨大的switch語(yǔ)句。而這么做就是為了加快運(yùn)行速度,與之相對(duì)的是,對(duì)于所有指令和使用execute方法的某個(gè)抽象類或接口,都要使用HashMap。

switch語(yǔ)句中的每個(gè)case都是我們?cè)诿杜e中定義的指令,這個(gè)eval函數(shù)將使用一個(gè)簡(jiǎn)單的指令參數(shù)來(lái)評(píng)估指令。除非你正在使用操作數(shù),否則不要在此函數(shù)中執(zhí)行任何指令指針增量。

  1. void eval(int instr) { 
  2.     switch (instr) { 
  3.     case HLT: 
  4.         running = false
  5.         break; 
  6.     } 

我會(huì)將此函數(shù)添加回虛擬機(jī)的主循環(huán)中:

  1. bool running = true
  2. int ip = 0; 
  3.  
  4. // instruction enum 
  5. // eval function 
  6. // fetch function 
  7.  
  8. int main() { 
  9.     while (running) { 
  10.         eval(fetch()); 
  11.         ip++; // increment the ip every iteration 
  12.     } 

不過(guò)在添加其他指令之前,我們需要一個(gè)棧。棧是一個(gè)非常簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu)。我們將使用這個(gè)數(shù)組而不是一個(gè)鏈表。因?yàn)槲业臈J枪潭ù笮〉模圆槐負(fù)?dān)心大小的調(diào)整。使用數(shù)組而不是鏈接列表,在緩存效率方面會(huì)更占優(yōu)勢(shì)。

與我們?nèi)绾螕碛幸粋€(gè)用于索引程序數(shù)組的ip類似,現(xiàn)在我們需要一個(gè)棧指針(sp)來(lái)顯示我們?cè)跅?shù)組中的位置。

下面是我的一個(gè)棧的數(shù)據(jù)結(jié)構(gòu)的詳細(xì)列表:

  1. [] // empty 
  2.  
  3. PSH 5 // put 5 on **top** of the stack 
  4. [5] 
  5.  
  6. PSH 6 // 6 on top of the stack 
  7. [5, 6] 
  8.  
  9. POP // pop the 6 off the top 
  10. [5] 
  11.  
  12. POP // pop the 5 
  13. [] // empty 
  14.  
  15. PSH 6 // push a 6... 
  16. [6] 
  17.  
  18. PSH 5 // etc.. 
  19. [6, 5] 

讓我們按照棧來(lái)分解我們的程序::

  1. PSH, 5, 
  2. PSH, 6, 
  3. ADD
  4. POP, 
  5. HLT 

首先我把5壓棧到棧上:

  1. [5] 

然后把6壓棧到棧上:

  1. [5, 6] 

然后add指令將彈出這些值并將它們加在一起,***將結(jié)果壓棧到棧上。

  1. [5, 6] 
  2.  
  3. // pop the top value, store it in a variable called a 
  4. a = pop; // a contains 6 
  5. [5] // stack contents 
  6.  
  7. // pop the top value, store it in a variable called b 
  8. b = pop; // b contains 5 
  9. [] // stack contents 
  10.  
  11. // now we add b and a. Note we do it backwards, in addition 
  12. // this doesn't matter, but in other potential instructions 
  13. // for instance divide 5 / 6 is not the same as 6 / 5 
  14. result = b + a; 
  15. push result // push the result to the stack 
  16. [11] // stack contents 

你看出來(lái)了嗎,我的棧指針在哪里起了作用?棧指針被設(shè)置為-1,這意味著它是空的。數(shù)組在C中為零索引,所以如果sp為0,它將被設(shè)置為C編譯器在其中引發(fā)的隨機(jī)數(shù),因?yàn)閿?shù)組的內(nèi)存未被清零。

如果現(xiàn)在我壓棧3個(gè)值,則sp將為2.因此,這里是一個(gè)包含3個(gè)值的數(shù)組:

  1.       -> sp -1 
  2.    psh -> sp 0 
  3.    psh -> sp 1 
  4.    psh -> sp 3 
  5.  
  6.  sp points here (sp = 2) 
  7.       | 
  8.       V 
  9. 1, 5, 9] 
  10. 0  1  2 <- array indices or "addresses" 

現(xiàn)在我從棧上出棧一次,就需要減小棧頂指針。比如我接下來(lái)要把9出棧,那么棧頂將變?yōu)?。

  1. sp points here (sp = 1) 
  2.         | 
  3.         V 
  4.     [1, 5] 
  5.      0  1 <- these are the array indices 

所以,當(dāng)我想知道棧頂內(nèi)容的時(shí)候,只需要查看sp的當(dāng)前值,希望你現(xiàn)在應(yīng)該知道棧是如何工作的。

現(xiàn)在我們用C語(yǔ)言實(shí)現(xiàn)它,用C語(yǔ)言實(shí)現(xiàn)一個(gè)棧是很簡(jiǎn)單的,和ip一樣,我們也應(yīng)該定義sp變量和數(shù)組,這個(gè)數(shù)組就是棧。

  1. int ip = 0; 
  2. int sp = -1; 
  3. int stack[256]; 
  4.  
  5. ... 

現(xiàn)在,如果我們想將壓棧一個(gè)值,就要增加棧指針,然后在當(dāng)前sp中設(shè)置該值。

這個(gè)命令的順序是非常重要的,如果你先設(shè)置值,再增加sp,那你會(huì)得到一些不好的行為,因?yàn)槲覀冊(cè)谒饕?1處寫入了內(nèi)存。

  1. // sp = -1 
  2. sp++; // sp = 0 
  3. stack[sp] = 5; // set value at stack[0] -> 5 
  4. // top of stack is now [5] 

在我們的eval函數(shù)中,可以像以下這樣進(jìn)行壓棧。

  1. void eval(int instr) { 
  2.     switch (instr) { 
  3.         case HLT: { 
  4.             running = false
  5.             break; 
  6.         } 
  7.         case PSH: { 
  8.             sp++; 
  9.             stack[sp] = program[++ip]; 
  10.             break; 
  11.         } 
  12.     } 

可以很明顯看到,這與以前的eval函數(shù)有一些區(qū)別。首先,我們把每個(gè)case語(yǔ)句塊放到大括號(hào)里。你可能不理解這樣做的用意,它可以讓你在每條case的作用域里定義變量。雖然現(xiàn)在不需要定義變量,但將來(lái)會(huì)用到,并且這樣做可以很容易得讓所有的case語(yǔ)句塊保持一致的風(fēng)格。

其次,是program[++ip] 表達(dá)式,該表達(dá)式是負(fù)責(zé)PSH指令所需的操作數(shù)。因?yàn)槲覀兊某绦虼鎯?chǔ)在一個(gè)數(shù)組里,PSH指令需要獲得一個(gè)操作數(shù)。操作數(shù)本質(zhì)是一個(gè)參數(shù),就像當(dāng)你調(diào)用一個(gè)函數(shù)時(shí),你可以給它傳遞一個(gè)參數(shù)。這種情況我們稱作壓棧數(shù)值5(PSH, 5)。我們可以通過(guò)增加指令指針ip來(lái)獲取操作數(shù)。當(dāng)ip為0時(shí),這意味著執(zhí)行到了PSH指令,接下來(lái)我們希望取得壓棧的數(shù)值。這可以通過(guò)ip自增的方法實(shí)現(xiàn),實(shí)現(xiàn)后,就需要跳到下一條指令否則會(huì)引發(fā)奇怪的錯(cuò)誤。當(dāng)然我們也可以把sp++簡(jiǎn)化到stack[++sp]里。

  1. program = [ PSH, 5, PSH, 6, ] 
  2.             0    1  2    3 
  3.  
  4. when pushing: 
  5. ip starts at 0 (PSH) 
  6. ip++, so ip is now 1 (5) 
  7. sp++, allocate some space on the stack 
  8. stack[sp] = program[ip], put the value 5 on the stack 

POP指令非常簡(jiǎn)單,只需對(duì)堆棧指針進(jìn)行遞減操作即可。但是,如果你想讓pop指令打印剛剛彈出的值即出棧值,還要做很多工作。

  1. case POP: { 
  2.     // store the value at the stack in val_popped THEN decrement the stack ptr 
  3.     int val_popped = stack[sp--]; 
  4.  
  5.     // print it out
  6.     printf("popped %d\n", val_popped); 
  7.     break; 

***是添加ADD指令,ADD指令,是一種計(jì)算機(jī)指令,含義為兩數(shù)相加(不帶進(jìn)位)。這可能會(huì)讓你覺得有點(diǎn)棘手,而這正是case語(yǔ)句塊放到大括號(hào)里的技巧所在,因?yàn)槲覀儸F(xiàn)在要引入了一些變量了。

  1. case ADD: { 
  2.     // first we pop the stack and store it as 'a' 
  3.     int a = stack[sp--]; 
  4.  
  5.     // then we pop the top of the stack and store it as 'b' 
  6.     int b = stack[sp--]; 
  7.  
  8.     // we then add the result and push it to the stack 
  9.     int result = b + a; 
  10.     sp++; // increment stack pointer **before** 
  11.     stack[sp] = result; // set the value to the top of the stack 
  12.  
  13.     // all done! 
  14.     break; 

在具體操作之前,請(qǐng)注意,這里的某些操作的順序很重要! 

  1. 5 / 4 != 4 / 5 

棧是LIFO,全稱First in, First out,先進(jìn)先出。也就是說(shuō),如果先進(jìn)棧5再進(jìn)棧4,就會(huì)先出棧4,然后出棧5。如果我們確實(shí)執(zhí)行了pop() / pop(),那么這將會(huì)給我們錯(cuò)誤的表達(dá)式,因此,確保順序正確是至關(guān)重要的。

寄存器

寄存器是虛擬機(jī)中的選配件,很容易實(shí)現(xiàn)。之前提到過(guò)我們可能需要六個(gè)寄存器:A,B,C,D,E和F。和實(shí)現(xiàn)指令集一樣,我們也用一個(gè)枚舉來(lái)實(shí)現(xiàn)它們。

  1. typedef enum { 
  2.    A, B, C, D, E, F, 
  3.    NUM_OF_REGISTERS 
  4. } Registers; 

不過(guò)這里有一個(gè)小技巧,枚舉的***會(huì)出現(xiàn)NUM_OF_REGISTERS。通過(guò)這個(gè)函數(shù)可以獲取寄存器的大小,即便你又添加了其它的寄存器,也可以獲得他們的大小。

我會(huì)將我的寄存器存儲(chǔ)在一個(gè)數(shù)組中,這是因?yàn)槲乙褂妹杜e,A = 0,B = 1,C = 2等。所以當(dāng)我想設(shè)置寄存器A時(shí),就像說(shuō)register [A] = some_value一樣簡(jiǎn)單。

  1. int registers[NUM_OF_REGISTERS]; 

打印寄存器A中的值:

  1. printf("%d\n", registers[A]); // prints the value at the register A 

指令指針

記住有一條分支指令的指針是要指向當(dāng)前指令的,由于現(xiàn)在是虛擬機(jī)源代碼,所以***的辦法是將指令指針作為一個(gè)寄存器,這樣你就可以從虛擬機(jī)程序中進(jìn)行讀取和各種操作。

  1. typedef enum { 
  2.     A, B, C, D, E, F, PC, SP, 
  3.     NUM_OF_REGISTERS 
  4. } Registers; 

現(xiàn)在我需要移植代碼來(lái)實(shí)際使用這些指令和堆棧指針,最快的方法是刪除棧頂?shù)膕p和ip變量,并用以下的定義替換它們。

  1. #define sp (registers[SP]) 
  2. #define ip (registers[IP]) 

這樣你就不必重寫很多代碼了,就能***地運(yùn)行了。不過(guò)缺點(diǎn)是,不能很好地?cái)U(kuò)展,并且它會(huì)混淆一些代碼,所以我建議不要使用這種方法,但對(duì)于一個(gè)簡(jiǎn)單的虛擬機(jī)來(lái)說(shuō),用一下也無(wú)妨。

當(dāng)涉及到分支代碼時(shí),我會(huì)給你一個(gè)提示。有了新的IP寄存器后,我們可以通過(guò)向這個(gè)IP寫入不同的值來(lái)進(jìn)行分支。試試下面這個(gè)例子,看看它能做什么。

  1. PSH 10 
  2. SET IP 0 

這類似于很多人都熟悉的基本程序:

  1. 10 PRINT "Hello, World" 
  2. 20 GOTO 10 

但是,由于我們不斷地進(jìn)行進(jìn)棧,所以一旦進(jìn)棧的量超過(guò)空間量,就將發(fā)生棧溢出。

請(qǐng)注意,每個(gè) ‘word’是一個(gè)指令,所以程序以下所示。

  1.               ;  these are the instructions 
  2. PSH 10        ;  0 1 
  3. PSH 20        ;  2 3 
  4. SET IP 0      ;  4 5 6 

如果我們想跳到第二組指令,我們將IP寄存器設(shè)置為2而不是0。

總結(jié)

閱讀完本文后,如果你在項(xiàng)目根目錄中運(yùn)行make,則可以執(zhí)行虛擬機(jī):./mac。

你可以在這里查看github上的源代碼,如果你想用MOV和SET指令來(lái)看虛擬機(jī)的更新版本,那么檢查一下mac-improved目錄,我們?cè)诒疚闹袑?shí)現(xiàn)的虛擬機(jī)的源代碼位于mac.c中

責(zé)任編輯:武曉燕 來(lái)源: 4hou
相關(guān)推薦

2022-01-26 16:30:47

代碼虛擬機(jī)Linux

2023-12-07 12:59:46

C語(yǔ)言循環(huán)隊(duì)列代碼

2024-08-26 14:32:43

2020-03-05 15:34:16

線程池C語(yǔ)言局域網(wǎng)

2023-02-26 01:37:57

goORM代碼

2021-03-10 07:52:58

虛擬機(jī)程序VMware

2021-07-31 12:58:53

PodmanLinux虛擬機(jī)

2016-09-06 19:45:18

javascriptVue前端

2009-07-27 08:46:22

2023-10-26 11:03:50

C語(yǔ)言宏定義

2017-03-20 17:59:19

JavaScript模板引擎

2010-12-23 14:05:12

虛擬機(jī)

2012-04-10 10:29:29

2017-03-15 08:43:29

JavaScript模板引擎

2022-09-05 08:07:25

goreplay監(jiān)控工具

2013-02-21 17:02:00

C語(yǔ)言

2009-01-05 19:07:03

服務(wù)器虛擬化虛擬機(jī)

2013-12-09 15:35:44

Docker虛擬機(jī)

2020-01-17 10:52:37

無(wú)服務(wù)器容器技術(shù)

2012-07-03 13:15:00

vSphere虛擬機(jī)存儲(chǔ)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

大陆极品少妇内射aaaaa| 91免费国产网站| 精品人妻互换一区二区三区| 欧美7777| 国产精品久久久久久久久快鸭 | 欧美日韩天堂| 亚洲国产欧美一区二区三区同亚洲| 精品无码国产一区二区三区av| 国产 欧美 自拍| 久久久久久色| 精品久久国产精品| 男男做爰猛烈叫床爽爽小说| jizz亚洲女人高潮大叫| 亚洲综合一区在线| 日本不卡二区高清三区| 国产成人精品一区二三区四区五区| 欧美日韩福利| 中文字幕欧美日韩| 欧产日产国产精品98| 成人在线高清| 欧美日韩性生活视频| 中文字幕av日韩精品| 无码精品视频一区二区三区| 狠狠色综合日日| 97超级碰碰碰久久久| 亚洲天堂av中文字幕| 美女主播精品视频一二三四| 欧美久久久影院| 五十路熟女丰满大屁股| 免费不卡视频| 91免费视频网址| 99re国产| 国产精品久久久久久久久久久久久久久久久久 | 国产一区红桃视频| 日韩在线播放中文字幕| 国产一区欧美| 久久久成人av| 超碰人人干人人| 亚洲制服欧美另类| 精品国产免费人成在线观看| 亚洲欧美国产中文| 少妇献身老头系列| 中文在线资源| 无码av免费一区二区三区试看| 亚洲日本精品一区| 国产精品99999| 97久久精品人人做人人爽50路| 亚洲综合小说区| 亚洲中文一区二区三区| 日本欧美一区二区| 国产精品成人久久久久| 亚洲精品中文字幕乱码三区91| 亚洲精品国产日韩| 欧美日韩国产成人在线| 波多野结衣爱爱视频| 日韩欧美电影| 精品国内亚洲在观看18黄| 成年人视频软件| 国产精品7m凸凹视频分类| 中文字幕无线精品亚洲乱码一区 | 中文字幕制服丝袜在线| 日本福利在线| 亚洲欧洲精品一区二区三区不卡 | 成人欧美亚洲| 欧美国产一区视频在线观看| 欧美一区二区高清在线观看| 国产色a在线| 中文字幕欧美日韩一区| 一本色道久久99精品综合| 亚洲视频tv| 亚洲视频免费看| 一级黄色录像免费看| 黄网址在线观看| 亚洲精品成人天堂一二三| 色一情一乱一乱一区91| 激情图片在线观看高清国产| 亚洲成人自拍偷拍| 无码日本精品xxxxxxxxx| а√天堂资源官网在线资源| 欧美色视频日本高清在线观看| 日本成人在线免费视频| 欧美风情在线视频| 欧美大片日本大片免费观看| 国产一线在线观看| 你微笑时很美电视剧整集高清不卡 | 日韩一级免费在线观看| 久久精品资源| 精品久久久久久久久久久久包黑料 | 欧美日韩精品免费看 | 2021中文字幕一区亚洲| 日韩久久久久久久| 18视频在线观看网站| 五月天激情小说综合| 亚洲欧美另类动漫| 香蕉大人久久国产成人av| 日韩成人中文电影| 99热99这里只有精品| 黄色另类av| 国产精品十八以下禁看| 亚洲av无码乱码国产麻豆| 91免费小视频| 日韩精品免费一区| av高清一区| 欧美精品一区二区三区在线| 五月婷六月丁香| 欧美日韩免费观看一区=区三区| 国产91精品青草社区| 国产精品无码久久久久成人app| 不卡区在线中文字幕| 日韩在线观看电影完整版高清免费| 成人video亚洲精品| 欧美日韩在线观看视频| 伊人精品视频在线观看| 最新亚洲精品| 欧美激情在线播放| 亚洲自拍第二页| 26uuu久久综合| 老司机激情视频| 成人看片毛片免费播放器| 欧美一级欧美一级在线播放| 精品无码人妻一区二区免费蜜桃 | 僵尸世界大战2 在线播放| 日韩欧乱色一区二区三区在线| 日韩成人免费视频| 国产亚洲精品久久777777| 亚洲精品孕妇| 国产不卡在线观看| 四虎免费在线观看| 一区二区三区在线视频观看58| 国产视频手机在线播放| 婷婷成人综合| 久久人91精品久久久久久不卡| 91欧美日韩麻豆精品| 久久久久青草大香线综合精品| 蜜臀精品一区二区| 久久久久久亚洲精品美女| 色阁综合伊人av| 久久久久久在线观看| 99久久99久久精品免费看蜜桃| 大片在线观看网站免费收看| 日韩电影精品| 最近的2019中文字幕免费一页| 亚洲欧美一二三区| wwwwxxxxx欧美| 国产成人黄色片| 日韩超碰人人爽人人做人人添| 久久久噜噜噜久久久| 国产成人三级一区二区在线观看一 | 伊人成人在线视频| 99免费在线观看视频| 尤物yw193can在线观看| 日韩亚洲欧美在线观看| 亚洲精品卡一卡二| 国产综合久久久久久鬼色| 中文字幕成人一区| 高清在线一区二区| 久久人人爽人人爽爽久久 | 日韩精品亚洲专区| 日本一区二区三不卡| 国产韩日精品| 上原亚衣av一区二区三区| 一级特黄色大片| 亚洲人精品一区| 手机看片国产精品| 黄色工厂这里只有精品| 精品视频第一区| 成人影院av| 在线中文字幕日韩| 97超碰国产在线| 一区二区三区在线视频观看 | 国产亚洲精品久久久久久牛牛| www.国产com| 国产午夜亚洲精品午夜鲁丝片| 色哟哟精品视频| 在线观看日韩| 国产精品一 二 三| 亚洲电影观看| 日日骚av一区| 亚洲国产精品国自产拍久久| 姬川优奈aav一区二区| 久久国产柳州莫菁门| 狠狠色狠狠色综合日日91app| 中文精品无码中文字幕无码专区| 欧美激情极品| 国产欧美精品va在线观看| 黄色网址在线免费| 亚洲国产精品久久久久秋霞蜜臀| 日韩黄色片网站| 亚洲老妇xxxxxx| 日本一区二区三区网站| 麻豆成人av在线| 99在线免费视频观看| 视频一区欧美| 亚洲在线免费看| 英国三级经典在线观看| 色噜噜久久综合伊人一本| 动漫av一区二区三区| 91官网在线观看| 清纯粉嫩极品夜夜嗨av| 久久久久久夜精品精品免费| 中文字幕55页| 久久这里有精品15一区二区三区| 一区二区三区四区欧美日韩| 欧美1区二区| 亚洲free嫩bbb| 怡红院成人在线| 欧美激情免费观看| 色网站免费在线观看| 亚洲精品美女视频| 国产成人av免费看| 在线观看www91| 精品91久久久| 亚洲青青青在线视频| 午夜在线观看一区| 成人18视频在线播放| 国产又黄又猛的视频| 久久精品1区| 成人免费播放器| 亚洲女同一区| 亚洲精品影院| 九色精品国产蝌蚪| 精品视频第一区| 操欧美女人视频| 成人在线国产精品| jizz欧美| 国产精品久久久久9999| jizz内谢中国亚洲jizz| 久久久久亚洲精品成人网小说| 精品欧美色视频网站在线观看| 亚洲性线免费观看视频成熟| 午夜视频免费在线| 精品免费国产二区三区| 国产剧情久久久| 欧美日韩精品一区二区| www.国产毛片| 色综合久久久久久久久| 国产极品在线播放| 一区二区三区在线视频观看| 日本黄色小说视频| 国产精品国产三级国产a| 精品国产aaa| 国产欧美精品区一区二区三区| 亚洲一区二区三区蜜桃| 91丨九色丨国产丨porny| av电影在线播放| 成人一级片网址| 韩国三级视频在线观看| 国产不卡视频在线播放| 男插女视频网站| 国产成人综合在线观看| 国产不卡的av| 国产成人精品影视| 日本一级大毛片a一| 成人午夜在线免费| 成人在线视频免费播放| av动漫一区二区| 中文字幕日韩三级片| 93久久精品日日躁夜夜躁欧美| 蜜臀av粉嫩av懂色av| 成人黄色国产精品网站大全在线免费观看| 久久久男人的天堂| 成人性生交大合| 玖玖爱在线精品视频| 91日韩精品一区| 日本理论中文字幕| 中文字幕日韩精品一区| 少妇影院在线观看| 亚洲高清免费一级二级三级| wwwwww国产| 欧美视频一区二区三区| 国产精品伊人久久 | 91香蕉视频免费看| 成人夜色视频网站在线观看| 国产网站欧美日韩免费精品在线观看| 亚洲第一在线播放| 欧美性高清videossexo| 一级黄色a视频| 欧美大片一区二区| 精品无吗乱吗av国产爱色| 日韩在线国产精品| 污视频免费在线观看| 91成人在线视频| 97欧美成人| 99久久精品无码一区二区毛片 | 肉肉视频在线观看| 欧美亚洲一区在线| 在线视频成人| 黑人巨大精品欧美一区二区小视频 | 国产精品videosex极品| 欧美在线观看成人| 久久av中文字幕片| 完美搭档在线观看| 欧美激情一区二区在线| 国精品无码一区二区三区| 无吗不卡中文字幕| 国产精品无码天天爽视频| 亚洲精品美女在线观看| 欧美 日韩 亚洲 一区| 欧美激情第二页| 青青在线视频免费| 国产福利一区二区三区| 精品人妻一区二区三区蜜桃视频| 亚洲欧洲综合另类| 人人爽人人爽人人片av| 这里只有精品99re| 日韩毛片在线一区二区毛片| 久久久成人的性感天堂| 免费福利视频一区二区三区| 99久久精品久久久久久ai换脸| 九九热线有精品视频99| 日韩在线视频在线| 麻豆传媒一区二区三区| 扒开jk护士狂揉免费| 亚洲一区二区在线免费观看视频 | 在线免费黄色| 91成人精品网站| 午夜电影一区| 中文字幕中文字幕99| 日韩在线卡一卡二| 国产精品伦子伦| 亚洲黄色av一区| 中文字幕日产av| 亚洲人成网站在线播| 国产又色又爽又黄刺激在线视频| 国产日韩换脸av一区在线观看| 亚洲免费成人av在线| 国产毛片久久久久久国产毛片 | 亚洲少妇一区二区| 1区2区3区欧美| 中文字幕无码乱码人妻日韩精品| 精品亚洲夜色av98在线观看| 黄网站在线观| 99久久久精品免费观看国产| 欧美一区二区| 国产永久免费网站| 国产精品看片你懂得| 伊人久久久久久久久久久久| 亚洲欧美国产制服动漫| www.51av欧美视频| 国产成人免费电影| 欧美/亚洲一区| 精品国产乱码久久久久久1区二区| 国产精品全国免费观看高清| 最好看的日本字幕mv视频大全| 亚洲欧美中文字幕在线一区| 午夜av不卡| 老牛影视免费一区二区| 国产日韩一区二区三区在线| 加勒比精品视频| 岛国av一区二区三区| 天堂av资源网| 91高清视频在线免费观看| 伦理一区二区三区| 99色精品视频| 久久精品日产第一区二区三区高清版| 亚洲欧美偷拍一区| 这里只有精品在线观看| 青青青国产精品| 国产av不卡一区二区| 国产成人自拍高清视频在线免费播放| 亚洲熟女www一区二区三区| 欧美v日韩v国产v| rebdb初裸写真在线观看| 久久久久久国产精品免费免费| av不卡在线看| 亚洲精品国产91| 欧美日韩色综合| 日本伦理一区二区| 久久精品人人做人人爽电影| 石原莉奈一区二区三区在线观看| 1024在线看片| 欧美一区二区三区人| wwww在线观看免费视频| 久久久亚洲综合网站| 免费看日韩精品| 欧美成人免费看| 日韩av综合中文字幕| 日本h片久久| 国产 国语对白 露脸| 97久久超碰精品国产| 这里只有精品免费视频| 伦理中文字幕亚洲| 欧美挤奶吃奶水xxxxx| 天天干天天玩天天操| 亚洲电影在线播放| 国产黄色片在线播放| 亚洲一区制服诱惑| 国产日韩欧美三级| 欧美丰满熟妇bbbbbb| 国产婷婷色综合av蜜臀av| 欧美视频第一| 久久久亚洲精品无码| 国产精品久久久久7777按摩| 亚洲精品免费在线观看视频| 日韩美女主播视频| 午夜精品久久| 日韩一区二区a片免费观看| 日韩欧美不卡一区| 中文另类视频| 日韩伦理在线免费观看|