Programming in Lua(五)- Coroutine, Lua Stack在《 Programming in Lua(三)- Yields in C 》里討論了 Lua 虛擬機(jī)對(duì) yields-in-C 及其 stack 的處理。當(dāng)時(shí)還未讀 Lua 虛擬機(jī)的實(shí)際代碼,只根據(jù)語(yǔ)言的行為來(lái)推測(cè),有些術(shù)語(yǔ)也不符合通常用法。最近從 Lua stack 的實(shí)現(xiàn)入手,發(fā)現(xiàn)了一些以前沒(méi)想過(guò)的問(wèn)題:為什么 resumes-in-C 從來(lái)不是問(wèn)題?為什么有 首先從術(shù)語(yǔ)的標(biāo)準(zhǔn)化說(shuō)起?!?Programming in Lua(三)- Yields in C 》里有多處這樣的描述:
其中「執(zhí)行層次」、「部分」、「段」這樣的字眼應(yīng)該替換為「stack frame」這個(gè)更常用的術(shù)語(yǔ)。線程運(yùn)行時(shí),stack 呈現(xiàn)兩層意義。一是后入先出的簡(jiǎn)單線性結(jié)構(gòu);二是把此線性結(jié)構(gòu)劃分成與函數(shù)調(diào)用層次一一對(duì)應(yīng)的若干段,這樣的一段就被稱(chēng)為一個(gè) stack frame。大多數(shù)語(yǔ)言的 runtime 或虛擬機(jī)中,stack frame 并無(wú)單獨(dú)的數(shù)據(jù)結(jié)構(gòu)表示。在 64-bit x86 的 C runtime (CRT) 中,每個(gè) stack frame 的首項(xiàng)是上一層 stack frame 的最低地址 (base),稱(chēng)為 stored frame pointer (SFP),最頂層 stack frame base 存儲(chǔ)在 就需求本身來(lái)說(shuō),Lua stack 要解決的問(wèn)題比 C 復(fù)雜的多,甚至比同為動(dòng)態(tài)語(yǔ)言的 Python 更復(fù)雜?;谔摂M機(jī)的語(yǔ)言的 call stack 有兩種可能的設(shè)計(jì):一是借用虛擬機(jī)本身的 CRT stack。Byte-code 的函數(shù)調(diào)用指令對(duì)應(yīng)虛擬機(jī)本身 native 代碼的函數(shù)調(diào)用,虛擬機(jī)的 CRT stack 隨 byte-code 函數(shù)調(diào)用的層次增加而增長(zhǎng)。二是由虛擬機(jī)維護(hù)額外的 call stack 數(shù)據(jù)結(jié)構(gòu)。Byte-code 的函數(shù)調(diào)用指令和其它指令一樣,在虛擬機(jī)的同一個(gè)循環(huán)中完成,虛擬機(jī)的 CRT stack 不體現(xiàn) byte-code 函數(shù)的調(diào)用層次。后者通常被稱(chēng)為 stackless 方案,前者暫且對(duì)應(yīng)稱(chēng)為 stackful 方案。 Lua 是 embedded/extension 語(yǔ)言,byte code 的運(yùn)行總會(huì)夾雜 C 函數(shù)。這些 C 函數(shù)的 call stack 在邏輯上是 byte-code 運(yùn)行狀態(tài)的一部分,實(shí)際上則間雜在 Lua 虛擬機(jī)的 CRT stack 中 (在涉及 Lua 的情況下討論 CRT stack 時(shí),要始終說(shuō)明是虛擬機(jī)的 CRT stack 還是 C 函數(shù)的 call stack)。從這個(gè)角度來(lái)說(shuō),embedded/extension 語(yǔ)言更傾向于選擇 stackful 設(shè)計(jì)。但 stackful 設(shè)計(jì)的固有缺陷在于 stack 結(jié)構(gòu)是平臺(tái)相關(guān)的,很難用跨平臺(tái)的方式實(shí)現(xiàn)諸多功能,比如協(xié)作式多任務(wù) (cooperative multi-threading),跟蹤垃圾回收 (tracing-GC),lexical closure。盡管不是全部原因,Python 缺少諸多高級(jí)特性與其 stackful 實(shí)現(xiàn)有很大關(guān)系。 為了遵守 ANSI C 的跨平臺(tái)性和更好的實(shí)現(xiàn)高級(jí)動(dòng)態(tài)功能,Lua 采用了 stackless 實(shí)現(xiàn)。這給處理 C 代碼的 call stack 帶來(lái)了一些挑戰(zhàn)。Lua 的 stack 存儲(chǔ)在
在這個(gè) stack 上缺少一些屬于 call stack 的東西:
這是因?yàn)?Lua 采用了雙 stack 結(jié)構(gòu)。對(duì)應(yīng)的 stack frame 信息存儲(chǔ)在一個(gè)
這里值得多說(shuō)一句,為什么在 C 函數(shù)中執(zhí)行 yield 會(huì)破壞 CRT stack?上文說(shuō)過(guò),Lua 的設(shè)計(jì)主要是 stackless 方式,其具體實(shí)現(xiàn)是通過(guò) 盡管 coroutine 涉及了對(duì) CRT stack 的操作,但是和 error 一樣,僅限于 ANSI C 支持的 longjmp,不會(huì)破壞 Lua 虛擬機(jī)的跨平臺(tái)性。問(wèn)題是,為什么 Lua 要在總體的 stackless 設(shè)計(jì)中制造這個(gè) stackful 例外?首先退一步說(shuō),即使采用 stackless 方式實(shí)現(xiàn) coroutine 切換,僅僅能避免在 yields-in-byte-code 中使用 longjmp,仍然無(wú)法避免在 yields-in-C 中使用 longjmp。這是因?yàn)椋m然不再有必要 longjmp 回到最近一次 resume 之處,但是仍然需要從 yield 之處回到最近的 Lua 虛擬機(jī)代碼。不僅如此,stackless 方式還要給 resumes-in-C 引入類(lèi)似的 longjmp (因?yàn)椴辉倮?CRT stack,所以 resumes-in-C 也必須立即回到 Lua 虛擬機(jī)代碼),破壞調(diào)用 resume 的 C 函數(shù)的 call stack,給 resumes-in-C 加上同現(xiàn)在的 yields-in-C 一樣的局限性。而現(xiàn)在的 stackful 方法則完全沒(méi)有這方面的問(wèn)題。這正是無(wú)需 這篇文章發(fā)布于 2013年05月9日,星期四,11:38,歸類(lèi)于 Lua, 開(kāi)源, 軟件開(kāi)發(fā)。 您可以跟蹤這篇文章的評(píng)論通過(guò) RSS 2.0 feed。 您可以留下評(píng)論,或者從您的站點(diǎn)trackback。 |
|
來(lái)自: quasiceo > 《待分類(lèi)1》