自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

你還不知道CIL?一個讓你開發(fā)項目更有性價比的中間語言

開發(fā) 后端
項目緊趕慢趕總算在年前有了一些成績,所以沉寂了幾周之后,小匹夫也終于有時間寫點東西了。以前匹夫寫過一篇文章,對CIL做了一個簡單地介紹,不過不知道各位看官看的是否過癮,至少小匹夫覺得很不過癮。

前言:

項目緊趕慢趕總算在年前有了一些成績,所以沉寂了幾周之后,小匹夫也終于有時間寫點東西了。以前匹夫寫過一篇文章,對CIL做了一個簡單地介紹,不過不知道各位看官看的是否過癮,至少小匹夫覺得很不過癮。所以決定寫幾篇關于CIL的文章,即和各位看官一起進行個交流,同時也是匹夫自己總結和鞏固一下這些知識點。俗話說的好,“萬事開頭,Hello World”,那么作為匹夫總結CIL的第一篇文章,就從Hello World開始吧。當然,正式開始寫CIL代碼之前,我們還有點閑話要說,那就是運行時的選擇為何是它?為何是CIL?而CIL為何又是基于堆棧的?內存或者寄存器難道不是更理想的選擇嗎?

為何是CIL?

開始正文內容之前,匹夫帶領大家先回顧一下《Mono為何能跨平臺?聊聊CIL(MSIL)》的簡要內容:首先,用C#寫的代碼被C#的編譯器編譯成CIL(當然除了C#還有很多其他的語言,比如VB等等),之后再有JIT編譯器在程序運行時即時編譯或者AOT(或者NGEN)進行提前編譯將CIL代碼編譯成對應平臺的機器碼,最后運行在平臺上的便是機器碼。小匹夫在那篇文章中提過,首先將各種不同的語言都統(tǒng)一編譯成CIL,再由CIL編譯成各個平臺的機器碼是跨平臺的基礎。那么仔細想想,一定有人會提出這樣的疑問,直接從C#編譯到機器碼,省略掉“多余”的中間語言,是不是也可行呢?這個問題的確值得討論,同時也為了小匹夫接下來的文章師出有名,所以首先聊聊CIL的“合法性”(用必要性這個詞也許更好)問題就成了匹夫寫這篇文章的頭等大事。

論據一:考慮下“性價比”

首先提出我們的論據一,那就是使用CIL這套體系對實現跨平臺的開銷要小的多的多。

引入一個“多余的”中間語言和兩個編譯器(C#----->CIL------>機器碼)聽上去總是要比只使用一種編譯器(C#-------->機器碼)的實現代價高的多,因為我們的目的是C#代碼能編譯成機器能運行的機器碼,顯然一步到位是最直接有效的方式。相反,引入中間語言之后,我們就需要實現兩種語言的分析和編譯,看上起的確多此一舉。但如果我們考慮到跨平臺這個前提,就會發(fā)現中間語言是多么的重要。

假設你可以選擇的語言有N種(比如C#, VB, F#, JScript .NET,Boo...),而我們的目標平臺有M種(win,mac,linux,ios,android...)。那么如果我們采用最直接的編譯方式,即從源代碼直接編譯成機器碼,那么到底需要多少個編譯器呢?

答案很直接咯:需要N*M種編譯器。因為你需要為每一種語言針對每一個平臺寫一個編譯器。

如果我們采用了中間語言呢?

我們只需要為N種語言寫N種編譯器,將它編譯成CIL代碼。再為M種平臺寫M種編譯器,將上一步生成的CIL代碼編譯成M種平臺的機器碼。那么這次我們到底需要多少編譯器呢?

答案也很明顯:需要M+N種編譯器。

所以,采用中間語言要比直接編譯代碼的開銷小的多得多。

論據二:實現的難度

假設,匹夫對硬件語言一竅不通(當然事實上是這樣的。。。),但卻具備一種分析源代碼語義的特殊天賦(瞎掰的)。那么要實現從C#到各個平臺機器碼一步到位的編譯,匹夫就要去啃各種目標芯片的說明,將C#代碼轉化成對應芯片的機器碼。這聽上去就像是一條不歸路,因為你并不擅長這個領域而且工作量巨大,同時由于不擅長帶來的隱患難以估量。

換言之,這個難度太大了。

但是如果我們通過對C#進行語義分析,能十分容易的就生成一份和芯片無關的CIL代碼,那么實現的難度相比直接從C#到機器碼那可是大大的降低了。因為CIL語言本身就十分簡單(至少匹夫這種粗人都能看懂),所以從源代碼到CIL的編譯器實現就十分容易。同時,也是因為CIL語言本身十分簡單,所以從CIL到機器碼的編譯器也十分簡單。

而且即便有新的平臺出現,你也不需要為每種語言都寫一個針對新平臺的編譯器,而只需要實現一個從CIL到新平臺機器碼的編譯器就可以了。

所以可以看到,CIL中間語言的出現,大大降低了跨平臺的實現難度。

Mono為何能跨平臺?聊聊CIL(MSIL)》這篇文章中,小匹夫也給各位列舉了一些CIL的代碼,同時做了一些解釋,文中在介紹CIL不依托cpu的寄存器時寫了這樣一句話:

不錯,CIL是基于堆棧的,也就是說CIL的VM(mono運行時)是一個棧式機。

 那么不知道各位看官是否也有這樣的疑問呢?那就是~~~~~~~

為什么是棧式機?直接放在內存中不好嗎?

終于要聊聊小匹夫也覺得挺有趣的一個話題了。對啊,為什么CIL基于堆棧呢?那么我們首先就來聊聊什么是“棧式機”。

假如讓你來...

假如讓你來設計一種機器語言,同時實現一個簡單地加法功能,簡單到什么程度呢?比如a+b等于c這樣好了。那么思路是什么呢?

方案一:使用內存

add [a的地址], [b的地址], [結果的地址也就是c的地址]

當機器遇到add操作符時,它就會去尋找a的地址和b的地址這兩個地址中存放的值,然后用balabala的方式將它們求和,并將結果存放在c的地址。

方案二:使用寄存器

當然匹夫也是一個學過匯編的漢子,也了解一點點單片機的知識,知道有一個叫做累加器的東西。累加器就屬于寄存器了,它主要用來儲存計算所產生的中間結果,最后將其轉存到其它寄存器或內存中。所以使用累加器的思路也很簡單,一開始將累加器設定為0,每個數字依序地被加到累加器中,當所有的數字都被加入后,結果才寫回到主內存中。

方案三:使用堆棧

等等,這個部分介紹的不是棧式機嗎?怎么感覺有點跑題呢?好吧,拉回思緒,讓我們再來考慮下使用堆棧如何實現這個簡單地加法功能呢?

push a
push b
add
pop c

add操作符首先將a,b彈出堆棧,然后將二者相加,再將結果壓棧。那么,使用了這種方案的虛擬機,就被稱為“棧式機”。

所以如果要回答為何CIL的選擇是使用堆棧,那么就繞不過堆棧和另外兩種方案的比較。

首先看一下我們做這種簡單加法時,硬件需要為我們提供一些什么呢?對,就是存放這些值的臨時空間。所謂的臨時空間,就是說存儲這個值的空間只有在需要這個值的時候才有用,其余的時候你并不需要關心這個空間或者說它的地址到底是什么。假設我們已經定義了一些操作符,比如Allocate用來分配內存,Call用來調用函數,Add用來求和,Store則是用來存儲數據。

首先我們直接使用內存來運行CIL,那么遇到這樣的表達式:

x = A() + B() + C() + 100

機器首先要為A()在內存上分配空間用來保存它的返回值,然后調用A()并將A()的返回值保存在之前分配給它的地址中,我們就管它叫做ret1好了。之后為B()在內存上分配空間來保存B()的返回值,接著調用B(),同樣將B()的返回值保存在剛才分配給它的內存中,我們暫時稱呼它ret2。這時,我們遇到了第一個“+”號,所以此時會為ret1和ret2相加的結果在內存上分配一個空間,并且將ret1和ret2相加,并將結果保存在剛剛分配的內存中(我們稱為sum1),之后的過程以此類推。

 
 
  1. Allocate ret1         //為A()的返回值分配臨時空間ret1  
  2. Call A(),ret1         //調用A()并將結果保存在ret1  
  3. Allocate ret2        //為B()的返回值分配臨時空間ret2  
  4. Call B(),ret2         //調用B()并將結果保存在ret2  
  5. Allocate sum1        //為第一次相加的結果分配臨時空間sum1  
  6. Add ret1,ret2,sum1      //使用Add操作符將ret1和ret2中的內容相加,并將結果保存在sum1中。  
  7. ... 

可以看到這樣的CIL代碼在每一步真正的邏輯執(zhí)行之前,都會先在內存上分配一塊臨時空間,用來存儲我們此時需要的數據。如果使用堆棧,這個步驟是不需要,因為你將你需要的數據存儲在了堆棧之中,而非在內存上臨時去分配空間。所以,使用堆棧時,CIL代碼看上去也許像是這樣的:

 
 
  1. push x的地址 // 將x的地址壓棧  
  2. call A()              // 現在堆棧中包含x的地址和A()的返回值ret1  
  3. call B()              // 現在堆棧中包換x的地址,ret1,B()的返回值ret2  
  4. add                   // 現在堆棧中包含x的地址,ret1 + ret2的結果sum1  
  5. call C()              // 現在堆棧中包含x的地址,sum1和C()的返回值ret3  
  6. add                   // 現在堆棧中包含x的地址, ret1+ret2+ret3的返回值sum2  
  7. push 100              // 現在堆棧中包含x的地址,sum2,以及100  
  8. add                   // 現在堆棧中包含x的地址, ret1+ret2+ret3+100的和sum3  
  9. store                 //將sum3存在x的地址中。 

同時,我們還可以看到如果CIL直接使用內存的話,由于在內存上的空間是臨時分配的,所以CIL代碼在運行時需要帶上它的操作數地址以及返回地址,比如上例中的Add ret1,ret2,sum1,因為如果不告訴它這些地址,它就不知道該從何處得到數據,并將返回的數據放在何處。

所以直接使用內存來運行CIL代碼,會使得CIL代碼變得十分的臃腫不堪,而且要做很多多余的工作。所以不直接使用內存,而是使用堆棧的原因就是因為:如果我們僅僅只是為了臨時存儲一些值,而在使用完這些值之后我們就不再關心這塊空間如何如何,顯然使用堆棧要比直接使用內存方便的多,簡潔的多。

至于為何不使用寄存器,小匹夫在上文提到的文章中已經解釋過了。簡單的講就是因為簡單。

好啦,到此為CIL正名的過程就結束啦。那么下面就開始首尾呼應,結尾點題,從Hello World開始踏上我們的CIL語言的征程吧~~

#p#

Hello Wolrd 你好,沃爾德

本文開篇就提到了那句名言:“萬事開頭,Hello World”。那么我們第一個CIL語言的程序,就從Hello World開始吧。因為匹夫使用的是mac機器,所以編譯.il文件所使用的工具是mono的ilasm。

那么匹夫就先新建一個.il文件,起名就叫做chen.il好了。

與C#不同,CIL并不要求方法必須要屬于一個類。所以,我們無需定義一個類,只需要聲明一個主函數(按照C#的說法main)即可。其實在CIL中我們應該管這種函數叫做“entrypoint”,也就是入口函數。只要定義了“entrypoint”,函數叫不叫main都無關緊要,為了演示這一點,我們的函數名就叫做Fanyou好了。

那么小匹夫就這樣寫一下咯:

上面就是小匹夫的Fanyou方法的定義了。和一般的語言一樣,包括方法簽名和方法體。但是在CIL語言中,方法的定義有以下需要注意的地方:

  1. 方法的定義以.method作為標識,可以在類中聲明,也可以在類外聲明。
  2. 和C#一樣,CIL程序的入口也必須是靜態(tài)的,也就是意味著調用這個入口函數并不需要某個類的實例。當然,使用static關鍵字來標識。
  3. 入口的標識.entrypoint,這個標志表明了該方法是CIL程序的入口。所以咯,只有一個函數能擁有.entrypoint標識。
  4. .maxstack這個標識表明了預計使用的堆棧槽,這里是1,因為我們只是把“Hello World”這個字符串壓棧。舉個例子,如果像上文那樣做2個數相加的加法,則需要2個堆棧槽,首先需要將2個數壓棧,之后add操作符將2個數彈出并求和,最后將結果壓棧。所以最多需要2個棧槽。
  5. ldstr操作符將“Hello World”壓棧,供之后的WriteLine方法使用。
  6. call調用了mscorlib程序集中System.Console類的WriteLine方法。這里call指明了WriteLine完整的簽名(void [mscorlib]System.Console::WriteLine(string))所以運行時可以選擇WriteLine的正確地重載。
  7. ret操作符則將結果返回給調用者。在這里,作為入口函數的返回,也意味著應用運行的結束。
  8. 有一些同學可能也看過很多CIL語言的代碼,是不是發(fā)現它們每一條語句之前往往有一個“IL_0000:”這樣的東東?但是匹夫你寫的代碼里沒有啊!是不是你寫錯了?NO,NO,那個IL_XXXX其實僅僅是行號,是不會影響程序的運行的。

好啦,一個簡單地Hello World的確能帶來一些最基本的知識點,但是這個.il文件編譯之后能運行嗎?答案是NO。因為上面的第6點也說了,調用了mscorlib程序集。但是我們貌似沒有引入什么程序集啊?所以我們還要加入一些程序集的信息才可以哦。那么完整的代碼如下了:

然后,讓我們編譯并且運行一下,看看我們寫的實現了Fanyou方法,輸出Hello World的CIL代碼到底是否可以運行吧!

運行結果:

首先

ilasm chen.il

對chen.il這個CIL文件進行編譯,生成的結果是chen.exe

之后再運行chen.exe

mono chen.exe

可以看到屏幕上輸出了“Hello World”。

OK,大功告成!

原文出自:http://www.cnblogs.com/murongxiaopifu/p/4257264.html

責任編輯:林師授 來源: 嘉棟的博客
相關推薦

2016-07-22 17:55:07

云計算

2020-12-14 07:51:16

JS 技巧虛值

2021-03-18 14:02:56

iOS蘋果細節(jié)

2022-07-17 06:53:24

微服務架構

2024-03-07 07:58:26

Web開發(fā)響應媒體查詢工具

2025-02-27 08:33:13

2018-09-02 15:43:56

Python代碼編程語言

2022-05-05 12:02:45

SCSS函數開發(fā)

2020-06-12 09:20:33

前端Blob字符串

2020-07-28 08:26:34

WebSocket瀏覽器

2011-04-01 09:49:54

Python

2023-04-09 23:37:31

JavaScript開發(fā)

2014-01-22 16:19:06

游戲圈游戲創(chuàng)業(yè)移動游戲

2021-10-22 09:41:26

橋接模式設計

2019-12-24 09:49:02

微軟英語瀏覽器

2020-10-15 18:01:04

項目經理項目管理首席技術官

2019-11-29 16:49:42

HTML語言開發(fā)

2023-12-21 14:40:09

Python編程語言

2024-05-20 09:27:00

Web 開發(fā)CSS

2011-09-15 17:10:41

點贊
收藏

51CTO技術棧公眾號