国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
Redis源碼學(xué)習(xí):Lua腳本

Redis源碼學(xué)習(xí):Lua腳本

1.Sublime Text配置

我是在Win7下,用Sublime Text + Cygwin開(kāi)發(fā)的,配置方法請(qǐng)參考《Sublime Text 3下C/C++開(kāi)發(fā)環(huán)境搭建》

要注意的是:在Cygwin中安裝Lua解析器后,SublimeClang插件就能識(shí)別出可飲用的Lua頭文件了,因?yàn)锽uild System中我們已經(jīng)配置過(guò)"-I", "D:\\cygwin64\\usr\\include",而新安裝的Lua頭文件會(huì)添加到這里。但是,編譯時(shí)卻無(wú)法鏈接到頭文件對(duì)應(yīng)的動(dòng)態(tài)鏈接庫(kù)。此時(shí),還需要添加一個(gè)鏈接選項(xiàng)lua5.1,修改后的完整Build System配置文件如下:

{    "path": "D:\\cygwin64\\bin",    "cmd": ["gcc", "-I", "D:\\cygwin64\\usr\\include", "${file}", "-o", "${file_path}/${file_base_name}", "-lm", "-llua5.1", "-Wall", "&", "start", "${file_path}/${file_base_name}.exe"],    "file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",    "working_dir": "${file_path}",    "selector": "source.c, source.c++",    "shell": true,    "variants":    [       {            "name": "Run::Cygwin",            "cmd": [ "start", "${file_path}/${file_base_name}.exe"]       }    ]}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

2.Lua基礎(chǔ)

2.1 執(zhí)行腳本

首先創(chuàng)建一個(gè)最簡(jiǎn)單的helloworld腳本hello.lua:

print("helloworld!")
  • 1

下面詳細(xì)解釋一下從C代碼中如何執(zhí)行Lua腳本文件。不管是如何執(zhí)行,Lua腳本的執(zhí)行過(guò)程都分為以下五步。以下面一段代碼框架適用于后面所有示例程序:

  • 初始化解釋器:lua_open是一個(gè)宏定義,等同于luaL_newstate()。創(chuàng)建出的lua_state也暗示了,Lua解釋器不使用C全局變量,而是將所有狀態(tài)都保存到lua_state這個(gè)數(shù)據(jù)結(jié)構(gòu)中。
  • 加載類庫(kù):luaL_openLibs()加載常用類庫(kù),如core、table、string、math等等。
  • 加載并編譯代碼/腳本文件:通常由luaL_loadfile()或luaL_loadbuffer()來(lái)完成,注意這只會(huì)將Lua代碼編譯好,并不會(huì)真正執(zhí)行。下面例子中l(wèi)ua_dofile等同于luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0),兩步合并為一步了。
  • 執(zhí)行代碼/腳本文件:由lua_pcall()完成,會(huì)根據(jù)當(dāng)前棧上的函數(shù)名、參數(shù)執(zhí)行。當(dāng)錯(cuò)誤時(shí)處理方式與上一步加載雷同,都是打印異常日志,然后從棧上彈出錯(cuò)誤處理器,最后直接返回或退出。
  • 清理釋放:lua_close()清理釋放解釋器占用的資源。
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <lua.h>#include <lualib.h>#include <lauxlib.h>void execute_from_script(char *filename);int main(int argc, char const *argv[]){    execute_from_script("hello.lua");    return 0;}/** * Execute from Lua script. * @param filename  script file name */void execute_from_script(char *filename) {    /* Lua interpreter */    lua_State *lua = lua_open();    /* Open Lua standard lib: io, string... */    luaL_openlibs(lua);    /* Execute code in script */    if (luaL_dofile(lua, filename)) {        fprintf(stderr, "Error when executing script: %s, %s\n",                     filename, lua_tostring(lua, -1));        /* Remove error handler */        lua_pop(lua, 1);        return;    }    /* Release all resource used */    lua_close(lua);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

2.2 執(zhí)行代碼

為了簡(jiǎn)化后面的示例代碼,對(duì)錯(cuò)誤處理統(tǒng)一封裝成bail()函數(shù):

void bail(lua_State *lua, char *msg, char *arg) {    fprintf(stderr, "%s %s: %s\n", msg, arg, lua_tostring(lua, -1));    exit(-1);}
  • 1
  • 2
  • 3
  • 4
  • 5

這一次我們不單獨(dú)創(chuàng)建一個(gè)Lua腳本文件,而是將Lua代碼嵌入到C代碼中直接執(zhí)行!

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <lua.h>#include <lualib.h>#include <lauxlib.h>void execute_from_code(char *code);int main(int argc, char const *argv[]){    execute_from_code("print(\"hello world!!!\")");    return 0;}/** * Execute Lua command directly. * @param code  Lua command */void execute_from_code(char *code){    lua_State *lua = lua_open();    luaL_openlibs(lua);    // Load & compile command and execute immediately    if (luaL_loadbuffer(lua, code, strlen(code), "line")             || lua_pcall(lua, 0, 0, 0))        bail(lua, "Error when executing code", code);    lua_close(lua);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

2.3 執(zhí)行函數(shù)

在這個(gè)例子中,我們執(zhí)行腳本文件中的函數(shù),而不是直接一段Lua代碼。在C代碼中調(diào)用Lua函數(shù)時(shí),如何傳入?yún)?shù)值和獲取返回值是學(xué)習(xí)的重點(diǎn):

Lua腳本如下:

function say_hello(name)    return "Hello, " .. name .. "!"end
  • 1
  • 2
  • 3

C示例代碼如下。注意加載并編譯函數(shù)后,lua_getglobal(lua, funcname)是關(guān)鍵,這一句會(huì)在全局中查找函數(shù),并將函數(shù)的指針壓到棧上。這樣后面調(diào)用lua_pcall()時(shí)才不會(huì)報(bào)錯(cuò):

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <lua.h>#include <lualib.h>#include <lauxlib.h>void execute_function_from_script(char *filename, char *funcname, char *arg);void execute_function_from_code(char *code);int main(int argc, char const *argv[]){    execute_function_from_script("hellofunc.lua", "say_hello", "cdai008");    return 0;}/** * Execute Lua function from script * @param filename  script file name * @param funcname  function name * @param arg       arguments */void execute_function_from_script(char *filename, char *funcname, char *arg){    lua_State *lua = lua_open();    luaL_openlibs(lua);    /* 1.Load and compile function code */    if (luaL_loadfile(lua, filename) || lua_pcall(lua, 0, 0, 0))        bail(lua, "Error when loading/compiling function", filename);    /* 2.Prepare function and arguments */    lua_getglobal(lua, funcname);    lua_pushstring(lua, arg);    /* 3.Do the call (1 arg, 1 result) */    if (lua_pcall(lua, 1, 1, 0) != 0)        bail(lua, "Error when calling function", funcname);    /* 4.Retrieve result */    char *ret = lua_tostring(lua, -1);    printf("Result: %s\n", ret);    lua_pop(lua, 1);    lua_close(lua);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

3.深入理解Lua棧

3.1 關(guān)于棧的事實(shí)

首先看幾條關(guān)于Lua棧的事實(shí):

  • Lua腳本與其他編程語(yǔ)言交換數(shù)據(jù)的唯一方式
  • lua_state創(chuàng)建后就存在,獨(dú)立于任何腳本或函數(shù)
  • 棧中元素不能修改,只能被替換或移除
  • 棧中元素可以是各種數(shù)據(jù)類型的

3.2 “討厭”的棧順序

Lua棧最讓人困惑的就是棧操作函數(shù)中的下標(biāo)參數(shù),有的用正數(shù)有的用負(fù)數(shù)。Lua官方文檔中解釋說(shuō):lua_gettop()返回棧中元素個(gè)數(shù),也就是棧頂元素的下標(biāo)。負(fù)數(shù)下標(biāo)negative_i = positive_i - (gettop() + 1)。這一點(diǎn)與Redis的List數(shù)據(jù)結(jié)構(gòu)很像,例如當(dāng)查看List中所有元素時(shí),為了方便我們會(huì)用lrange lista 0 -1,而不會(huì)將-1寫(xiě)成真的去求一下末尾元素的下標(biāo)。

下面看一段示例代碼,加深一下理解:

static void stackDump(lua_State *L){    int i;    int top = lua_gettop(L);    printf("---- Begin Stack %i ----\n", top);    for (i = 1; i <= top; i++) {        int t = lua_type(L, i);        int ni = i - (top + 1);        switch (t) {          case LUA_TSTRING:     /* strings */            printf("%i -- (%i) ---- '%s'", i, ni, lua_tostring(L, i));            break;          case LUA_TBOOLEAN:    /* booleans */            printf("%i -- (%i) ---- %s", i, ni, lua_toboolean(L, i) ? "true" : "false");            break;          case LUA_TNUMBER:     /* numbers */            printf("%i -- (%i) ---- %g", i, ni, lua_tonumber(L, i));            break;          default:              /* other values */            printf("%i -- (%i) ---- '%s'", i, ni, lua_typename(L, t));            break;        }        printf("\n");    }    printf("---- End Stack ----\n\n");}void test_lua_stack_order(){    lua_State *L = lua_open();    lua_pushstring(L, "hi there");    lua_pushnumber(L, 17);    lua_pushboolean(L, 1);    lua_pushstring(L, "foobar");    stackDump(L);    /*        ---- Begin Stack 4 ----        1 -- (-4) ---- 'hi there'        2 -- (-3) ---- 17        3 -- (-2) ---- true        4 -- (-1) ---- 'foobar'        ---- End Stack ----    */    lua_pushvalue(L, -4);     stackDump(L);    /*        ---- Begin Stack 5 ----        1 -- (-5) ---- 'hi there'        2 -- (-4) ---- 17        3 -- (-3) ---- true        4 -- (-2) ---- 'foobar'        5 -- (-1) ---- 'hi there'        ---- End Stack ----     */    lua_replace(L, 3);     stackDump(L);    /*        ---- Begin Stack 4 ----        1 -- (-4) ---- 'hi there'        2 -- (-3) ---- 17        3 -- (-2) ---- 'hi there'        4 -- (-1) ---- 'foobar'        ---- End Stack ----     */    lua_settop(L, 6);     stackDump(L);    /*        ---- Begin Stack 6 ----        1 -- (-6) ---- 'hi there'        2 -- (-5) ---- 17        3 -- (-4) ---- 'hi there'        4 -- (-3) ---- 'foobar'        5 -- (-2) ---- 'nil'        6 -- (-1) ---- 'nil'        ---- End Stack ----     */    lua_remove(L, -3);     stackDump(L);    /*        ---- Begin Stack 5 ----        1 -- (-5) ---- 'hi there'        2 -- (-4) ---- 17        3 -- (-3) ---- 'hi there'        4 -- (-2) ---- 'nil'        5 -- (-1) ---- 'nil'        ---- End Stack ----     */    lua_settop(L, -5);     stackDump(L);    /*        ---- Begin Stack 1 ----        1 -- (-1) ---- 'hi there'        ---- End Stack ----     */    lua_close(L);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103

注意棧操作函數(shù)中參數(shù)的意義:

  • lua_pop(L, n):參數(shù)指定的是彈出元素個(gè)數(shù),想移除指定下標(biāo)的元素要用lua_remove(L, x)。
  • lua_pushvalue(L, x):將指定下標(biāo)x的元素拷貝到棧頂,而不是壓入一個(gè)整數(shù)x。
  • lua_replace(L, x):移動(dòng)棧頂元素到指定下標(biāo)x。

3.3 棧與table

table在棧上的創(chuàng)建方式有些tricky。首先lua_newtable()會(huì)壓入“table”到棧頂,然后依次壓入key-value鍵值對(duì),然后調(diào)用lua_settable()會(huì)使鍵值對(duì)被彈出,形成真正的table。此時(shí),棧上又只剩字符串“table”了。數(shù)據(jù)跑哪里去了?此時(shí)要使用lua_next()函數(shù)對(duì)table進(jìn)行遍歷:

    lua_newtable(L);    lua_pushnumber(L, 1);    lua_pushstring(L, "allen");    stackDump(L);    lua_settable(L, -3);    stackDump(L);    /*        ---- Begin Stack 4 ----        1 -- (-4) ---- 'hi there'        2 -- (-3) ---- 'table'        3 -- (-2) ---- 1        4 -- (-1) ---- 'allen'        ---- End Stack ----        ---- Begin Stack 2 ----        1 -- (-2) ---- 'hi there'        2 -- (-1) ---- 'table'        ---- End Stack ----     */    lua_pushstring(L, "hank");    /* set table at index -2, table["2"]="hank" */    lua_setfield(L, -2, "2");    lua_pushstring(L, "carter");    /* set table at index -2, table[3]="carter" */    lua_rawseti(L, -2, 3);    /* Push nil as first key */    lua_pushnil(L);    /* Pops a key from the stack, and pushes a key–value pair from the table         at the given index (the "next" pair after the given key) */    while(lua_next(L, -2) != 0) {        /* uses 'key' (at index -2) and 'value' (at index -1) */        int t = lua_type(L, -2);        switch (t) {          case LUA_TSTRING:            printf("table['%s']='%s'\n", lua_tostring(L, -2), lua_tostring(L, -1));            break;          case LUA_TNUMBER:            printf("table[%g]='%s'\n", lua_tonumber(L, -2), lua_tostring(L, -1));            break;        }        /* removes 'value'; keeps 'key' for next iteration */        lua_pop(L, 1);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

答案就在lua_next()中。我們?cè)跅m敺胖昧藅able和一個(gè)nil,然后調(diào)用lua_next(),并訪問(wèn)key和value后移除棧頂?shù)膙alue而保留key,這樣就能依次迭代整個(gè)table。注意lua_settable()、lua_setfield()和lua_rawseti()三個(gè)函數(shù)的用法。

4.Redis中的Lua

終于到了本文的重點(diǎn),模擬一下Redis是如何執(zhí)行Lua腳本的,分為以下幾步:

  • 準(zhǔn)備Lua環(huán)境:這一步很簡(jiǎn)單,就是創(chuàng)建Lua解釋器和加載類庫(kù)。
  • 動(dòng)態(tài)創(chuàng)建函數(shù):Redis會(huì)將腳本中的代碼包裝成一個(gè)函數(shù),并生成一個(gè)函數(shù)名。
  • 加載編譯函數(shù):這一步與之前完全相同。
  • 準(zhǔn)備表對(duì)象:創(chuàng)建redis表對(duì)象,并將其與函數(shù)指針一起壓到棧上。
  • 執(zhí)行函數(shù):這一步與之前完全相同。
  • 清理釋放:這一步與之前完全相同。

核心部分的C示例代碼:

/** * Showcase of how to deal with return values * @param cmd   Lua command */void execute_function_from_code(char *cmd){    // 1.Prepare Lua execution enviornment    lua_State *lua = lua_open();    luaL_openlibs(lua);    // 2.Create function dynamically    char funcdef[100], *funcname = "fun1";    memset(funcdef, 0, sizeof(funcdef));    strcat(funcdef, "function ");    strcat(funcdef, funcname);    strcat(funcdef, "() ");    strcat(funcdef, cmd);    strcat(funcdef, " end");    printf("Code: %s\n", funcdef);    // 3.Compile code in buffer and push onto stack    if(luaL_loadbuffer(lua, funcdef, strlen(funcdef), "@user_script")            || lua_pcall(lua, 0, 0, 0))        bail(lua, "Error when loading/compiling function", funcname);    // 4.Prepare function and global table 'redis'    lua_getglobal(lua, funcname);    lua_newtable(lua);    lua_pushstring(lua,"call");    lua_pushcfunction(lua, luaRedisCallCommand);    lua_settable(lua, -3);    lua_setglobal(lua, "redis");    // 5.Execute Lua function    if (lua_pcall(lua, 0, 0, -2))        bail(lua, "Error when calling function", funcname);    // 6.Cleanup    lua_close(lua);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

測(cè)試main函數(shù)和回調(diào)函數(shù)。main函數(shù)測(cè)試一下在Lua代碼中執(zhí)行redis.call(“set”, “foo”, “bar”),而回調(diào)函數(shù)luaRedisCallCommand()則簡(jiǎn)單地打印一下入?yún)ⅲ?/p>

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <lua.h>#include <lualib.h>#include <lauxlib.h>void execute_function_from_code(char *code);int main(int argc, char const *argv[]){    execute_function_from_code("redis.call(\"set\", \"foo\", \"bar\")");    return 0;}int luaRedisCallCommand(lua_State *lua) {    int i, argc = lua_gettop(lua);    for (i = 0; i < argc; i++) {        char *obj_s;        size_t obj_len;        obj_s = (char *)lua_tolstring(lua, i + 1, &obj_len);        printf("Argument[%d]=%s\n", i, obj_s);    }    return 1;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

這里只是一個(gè)演示的小例子,詳細(xì)介紹請(qǐng)參考《Redis設(shè)計(jì)與實(shí)現(xiàn)》。但Lua腳本這一章是免費(fèi)Web版里沒(méi)有的,得看實(shí)體書(shū)。真正的Redis代碼流程要復(fù)雜得多,包括:

  • 執(zhí)行前:Lua環(huán)境里某些東西只初始化一次,準(zhǔn)備KEYS和ARGV兩個(gè)全局變量,設(shè)置超時(shí)控制hook。
  • 執(zhí)行后:定時(shí)Lua GC回收資源,用字典緩存已經(jīng)執(zhí)行過(guò)的Lua腳本。
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)
打開(kāi)APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Redis源代碼筆記 – Lua腳本支持| Just Carry On
輕量級(jí)語(yǔ)言Lua入門(mén)
c++調(diào)用lua腳本1(平臺(tái)windows)
luajit FFI LUA腳本中怎樣調(diào)用C自己定義的函數(shù)
lua和c/c 互相調(diào)用實(shí)例分析 - lxyfirst - C 博客
Step By Step(Lua-C API簡(jiǎn)介)(轉(zhuǎn))
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服