【Lua源碼分析系列】2.分析思路

很抱歉,這周實在是太忙了。。。不廢話,直接進入主題。css

引言

所謂的分析思路,無非就是找到程序的入口點,按執行流程一步一步地閱讀分析,或者從本身感興趣的部分入手。這個系列暫時還不是對Lua的全面剖析,只是選取其中一部分來分析。所以咱們要找到本身感興趣的部分,有針對性地進行分析。html

因爲Lua的官方文檔比較齊全,瀏覽一下官方提供的資料集會比較便於咱們着手分析。算法

所謂的源碼分析,就是從源代碼構建可執行程序,調試調試,看看執行流程,看看主要的數據結構和算法、程序的運行狀態之類的,最後再品味一下設計(問題背景、緣由、優缺點),僅此而已。編程

那咱們先來看看源代碼的佈局。api

代碼佈局

能夠從Makefile文件入手,不過這裏用的是Windows + Visual Studio,能夠省去這個步驟。
看看從官方下載的源碼包,提供瞭如下文件:數據結構

./lua-5.3.0/
    Makefile    README
    /doc/
        contents.html      logo.gif      lua.1
        lua.css            luac.1        manual.css
        manual.html        osi-certified-72x60.png
        readme.html
    /src/
        lapi.c       lapi.h       lauxlib.c    lauxlib.h
        lbaselib.c   lbitlib.c    lcode.c      lcode.h
        lcorolib.c   lctype.c     lctype.h     ldblib.c
        ldebug.c     ldebug.h     ldo.c        ldo.h
        ldump.c      lfunc.c      lfunc.h      lgc.c
        lgc.h        linit.c      liolib.c     llex.c
        llex.h       llimits.h    lmathlib.c   lmem.c
        lmem.h       loadlib.c    lobject.c    lobject.h
        lopcodes.c   lopcodes.h   loslib.c     lparser.c
        lparser.h    lprefix.h    lstate.c     lstate.h
        lstring.c    lstring.h    lstrlib.c    ltable.c
        ltable.h     ltablib.c    ltm.c        ltm.h
        lua.c        lua.h        lua.hpp      luac.c
        luaconf.h    lualib.h     lundump.c    lundump.h
        lutf8lib.c   lvm.c        lvm.h        lzio.c
        lzio.h       Makefile

第一件事固然是看README啦還用說,固然專業的作法能夠看Makefile,這裏爲了不引入其餘無關知識,仍是選用簡單的方法。閉包

按照說明查看doc/readme.html文件其中的【Building Lua on other systems】節,咱們發現它介紹了lua可執行程序大體的組成和依賴關係以下:數據結構和算法

library:
        lapi.c          lcode.c         lctype.c        ldebug.c
        ldo.c           ldump.c         lfunc.c         lgc.c
        llex.c          lmem.c          lobject.c       lopcodes.c
        lparser.c       lstate.c        lstring.c       ltable.c
        ltm.c           lundump.c       lvm.c           lzio.c
        lauxlib.c       lbaselib.c      lbitlib.c       lcorolib.c
        ldblib.c        liolib.c        lmathlib.c      loslib.c
        lstrlib.c       ltablib.c       lutf8lib.c      loadlib.c
        linit.c
interpreter:
        library,        lua.c
compiler:
        library,        luac.c

根據這個簡單的依賴關係,我用VS2015創建了相應的工程,便於調試。其實官方資料集裏也提供了現成的VS工程的下載連接:編輯器

TODO:此處應有下載連接^_^函數

由於咱們分析的重點是編譯原理和虛擬機的部分,而不是相關的庫的實現部分。所以應該從 lua.c 或 luac.c 開始入手,其實咱們從文件列表中也能夠看出,裏面有幾個比較重要的文件:

llex.c    lopcodes.c    lparser.c    lvm.c

到底從哪裏入手比較好,就見仁見智了。這裏我仍是採用了官方提供的資料來幫助選擇。
官方的資料集的wiki中提供了一個頁面。該頁面介紹了這些文件的用途、編程約定、模塊結構等等。
爲了不這個頁面失效,下面仍是可恥地複製粘貼略帶翻譯地提供給你們,網上也有一些翻譯,只是他們翻譯時省略掉了一些我以爲有用的信息。個別簡單的,我就不翻譯了。

Lua源碼的模塊結構

實用功能模塊

  • ldebug.c - 調試接口。包括如下功能:

    1. 訪問調試鉤子 (lua_sethook, lua_gethook, lua_gethookcount),

    2. 訪問運行時棧信息 (lua_getstack / lua_getlocal / lua_setlocal),

    3. 檢查字節碼 (luaG_checkopenop / luaG_checkcode),

    4. 引起錯誤 (luaG_typeerror / luaG_concaterror / luaG_aritherror /luaG_ordererror / luaG_errormsg / luaG_runerror)

  • lzio.c - 一種緩衝輸入流接口。

  • lmem.c - 內存管理接口。它實現了這些內存分配函數:luaM_realloc / luaM_growaux_

  • lgc.c - 增量式GC (內存管理)

基本數據類型的實現模塊

  • lstate.c - 全局狀態。包括:

    • 用於打開和關閉Lua states的函數(lua_newstate/lua_close)

    • 線程相關函數 (luaE_newthread / luaE_freethread)

  • lobject.c - 一些針對Lua對象的通用函數。包括:

    • 數據類型與其字符串形式的互相轉換

    • 原始數據相等性測試(luaO_rawequalObj)

    • 日誌基礎設施2(luaO_log2)

  • lstring.c - string table (持有由Lua處理的全部字符串)

  • lfunc.c - 用來操縱原型和閉包的輔助函數。

  • ltable.c - Lua tables (hash)

語法分析與代碼生成相關模塊

  • lcode.c - Lua的代碼生成器. 由 lparser.c 來使用

  • llex.c - 詞法分析器. 由 lparser.c 來使用

  • lparser.c - Lua 解析器.

  • lundump.c - 加載通過預編譯的Lua代碼塊:

    • 實現了用於加載預編譯後的代碼塊的luaU_undump 函數。

    • 還提供了用來解析函數頭的 luaU_header 函數(在luaU_undump()內部被調用)。

  • ldump.c - 用於保存通過預編譯的Lua代碼塊:

    • 實現了用於轉儲函數對象的luaU_dump()函數。這種函數對象是從文件或字符串預編譯而來的Lua代碼塊。

處理執行Lua字節碼的模塊

  • lopcodes.c - 由Lua虛擬機使用的操做碼。

    • 經過 luaP_opnames 和 luaP_opmodes這兩個映射表,定義了全部操做碼的名稱和相關信息。

  • lvm.c - Lua虛擬機:

    • 用於執行字節碼 (luaV_execute).

    • 還暴露了少許函數供 lapi.c 使用(如:luaV_concat).

  • ldo.c - Lua函數調用和棧管理。處理函數調用 (luaD_call / luaD_pcall), 棧生長, 協程處理等

  • ltm.c - 標籤方法(tag methods)。 實現了查詢對象中的元方法的功能。

標準庫的實現模塊

lbaselib.c - (base functions)
lstrlib.c  - string
ltablib.c  - table
lmathlib.c - math
loslib.c   - os
liolib.c   - io
loadlib.c  - package
ldblib.c   - debug

如下模塊定義了 C API

  • lapi.c - Lua API. Lua C API的主要實現部分(lua_* functions).

  • lauxlib.c - 定義了 luaL_* 函數

  • linit.c - 實現了 luaL_openlibs 用於從C語言環境中加載上述模塊。

lua 和 luac 程序的實現模塊

  • lua.c - 獨立的Lua 解析器

  • print.c - 定義了 "PrintFunction?" 函數,這些函數用於打印字節碼 (經過luac.c "-l" 選項來使用)

  • luac.c - Lua 編譯器 (保存字節碼到文件/列出字節碼)

感興趣的模塊

搞清楚Lua的模塊結構後,咱們須要着重分析的模塊就出來了:

  • lua 和 luac 程序的實現模塊

  • 處理執行Lua字節碼的模塊

  • 語法分析與代碼生成相關模塊

  • 基本數據類型的實現模塊

固然,到了後期,若是有時間,我還會再分析一下Lua的垃圾收集器,但最近實在是太忙太忙了。

從Makefile來看模塊間的依賴關係

如下是對 lua-5.3.0/src/Makefile 文件重要部分的節選

LUA_A=    liblua.a
CORE_O=    lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o \
    lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o \
    ltm.o lundump.o lvm.o lzio.o
LIB_O=    lauxlib.o lbaselib.o lbitlib.o lcorolib.o ldblib.o liolib.o  \
    lmathlib.o loslib.o lstrlib.o ltablib.o lutf8lib.o loadlib.o linit.o
BASE_O= $(CORE_O) $(LIB_O) $(MYOBJS)

LUA_T=    lua
LUA_O=    lua.o

LUAC_T=    luac
LUAC_O=    luac.o

根據咱們感興趣的模塊,大概是如下目標文件:

  1. lua.o

  2. luac.o

  3. llex.o

  4. ldo.o

  5. lstate.o

  6. lparser.o

  7. lopcodes.o

  8. lvm.o

  9. lcode.o

  10. ldump.o

  11. lundump.o

  12. ltm.o

  13. lobject.o

  14. lstring.o

  15. ltable.o

  16. lfunc.o

(不得不吐槽這Markdown編輯器的Bug,莫名其妙給其餘其餘條目自動加粗了。。。我本來都不是這麼寫的!!!!)
其實仍是挺多的,不過比較重要的都用粗體標出來了。大體就是按這個順序去分析。這些其實也能夠直接看源文件中引用的頭文件,但那樣太麻煩了,而且,Lua源碼有時候並非在文件的頂部寫include指令,而是在中間的某個地方,比較蛋疼。看Makefile是比較方便並且專業的作法。咱們繼續看一下Makefile 文件中上述目標文件依賴關係:

lua.o: lua.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h

luac.o: luac.c lprefix.h lua.h luaconf.h lauxlib.h lobject.h llimits.h \
  lstate.h ltm.h lzio.h lmem.h lundump.h ldebug.h lopcodes.h

llex.o: llex.c lprefix.h lua.h luaconf.h lctype.h llimits.h ldo.h \
  lobject.h lstate.h ltm.h lzio.h lmem.h lgc.h llex.h lparser.h lstring.h \
  ltable.h

ldo.o: ldo.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
  lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lopcodes.h \
  lparser.h lstring.h ltable.h lundump.h lvm.h

lstate.o: lstate.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
  lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h llex.h \
  lstring.h ltable.h

lparser.o: lparser.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \
  llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \
  ldo.h lfunc.h lstring.h lgc.h ltable.h

lopcodes.o: lopcodes.c lprefix.h lopcodes.h llimits.h lua.h luaconf.h

lvm.o: lvm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
  llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h \
  ltable.h lvm.h

lcode.o: lcode.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \
  llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \
  ldo.h lgc.h lstring.h ltable.h lvm.h

這裏簡要地說一下Makefile語法:冒號左邊的.o是編譯時生成的目標文件,冒號右邊是生成這個目標文件須要的源文件,\ 表示還沒寫完,下一行繼續寫。上一段Makefile代碼中,等號左邊是一個變量,等號右邊是這個變量的值。

關於Makefile:Makefile是GNU/Linux系統中用於自動化構建的DSL,供gnu make使用,和Android開發中的.gradle文件做用相似。不過VS和Qt也都使用了各自的Makefile格式和工具。VS有nmake,Qt有qmake,跨平臺的有cmake,這裏就不展開說了,本身去看文檔吧。

如今,咱們能夠比較有針對性地去看源文件了。

關於下一期

下一期將介紹一下Lua源碼中的編程約定(其實仍是作個搬運工+翻譯工,這裏的翻譯就湊合着看吧,翻譯質量應該沒有太大問題)。前面這幾期都是比較無聊又不可或缺的,只能忍忍啦。預報一下,後天將更新第三彈。

參考資料

  1. 模塊結構的簡介 http://lua-users.org/wiki/LuaSource

相關文章
相關標籤/搜索