乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

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

       WindySky 2016-02-18

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

      1.Sublime Text配置

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

      要注意的是:在Cygwin中安裝Lua解析器后,SublimeClang插件就能識別出可飲用的Lua頭文件了,因為Build System中我們已經(jīng)配置過"-I", "D:\\cygwin64\\usr\\include",而新安裝的Lua頭文件會添加到這里。但是,編譯時卻無法鏈接到頭文件對應(yīng)的動態(tài)鏈接庫。此時,還需要添加一個鏈接選項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)建一個最簡單的helloworld腳本hello.lua:

      print("helloworld!")
      • 1

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

      • 初始化解釋器:lua_open是一個宏定義,等同于luaL_newstate()。創(chuàng)建出的lua_state也暗示了,Lua解釋器不使用C全局變量,而是將所有狀態(tài)都保存到lua_state這個數(shù)據(jù)結(jié)構(gòu)中。
      • 加載類庫:luaL_openLibs()加載常用類庫,如core、table、string、math等等。
      • 加載并編譯代碼/腳本文件:通常由luaL_loadfile()或luaL_loadbuffer()來完成,注意這只會將Lua代碼編譯好,并不會真正執(zhí)行。下面例子中l(wèi)ua_dofile等同于luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0),兩步合并為一步了。
      • 執(zhí)行代碼/腳本文件:由lua_pcall()完成,會根據(jù)當(dāng)前棧上的函數(shù)名、參數(shù)執(zhí)行。當(dāng)錯誤時處理方式與上一步加載雷同,都是打印異常日志,然后從棧上彈出錯誤處理器,最后直接返回或退出。
      • 清理釋放: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í)行代碼

      為了簡化后面的示例代碼,對錯誤處理統(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

      這一次我們不單獨創(chuàng)建一個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ù)

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

      Lua腳本如下:

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

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

      #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)于棧的事實

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

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

      3.2 “討厭”的棧順序

      Lua棧最讓人困惑的就是棧操作函數(shù)中的下標(biāo)參數(shù),有的用正數(shù)有的用負(fù)數(shù)。Lua官方文檔中解釋說:lua_gettop()返回棧中元素個數(shù),也就是棧頂元素的下標(biāo)。負(fù)數(shù)下標(biāo)negative_i = positive_i - (gettop() + 1)。這一點與Redis的List數(shù)據(jù)結(jié)構(gòu)很像,例如當(dāng)查看List中所有元素時,為了方便我們會用lrange lista 0 -1,而不會將-1寫成真的去求一下末尾元素的下標(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ù)指定的是彈出元素個數(shù),想移除指定下標(biāo)的元素要用lua_remove(L, x)。
      • lua_pushvalue(L, x):將指定下標(biāo)x的元素拷貝到棧頂,而不是壓入一個整數(shù)x。
      • lua_replace(L, x):移動棧頂元素到指定下標(biāo)x。

      3.3 棧與table

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

          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()中。我們在棧頂放置了table和一個nil,然后調(diào)用lua_next(),并訪問key和value后移除棧頂?shù)膙alue而保留key,這樣就能依次迭代整個table。注意lua_settable()、lua_setfield()和lua_rawseti()三個函數(shù)的用法。

      4.Redis中的Lua

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

      • 準(zhǔn)備Lua環(huán)境:這一步很簡單,就是創(chuàng)建Lua解釋器和加載類庫。
      • 動態(tài)創(chuàng)建函數(shù):Redis會將腳本中的代碼包裝成一個函數(shù),并生成一個函數(shù)名。
      • 加載編譯函數(shù):這一步與之前完全相同。
      • 準(zhǔn)備表對象:創(chuàng)建redis表對象,并將其與函數(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

      測試main函數(shù)和回調(diào)函數(shù)。main函數(shù)測試一下在Lua代碼中執(zhí)行redis.call(“set”, “foo”, “bar”),而回調(diào)函數(shù)luaRedisCallCommand()則簡單地打印一下入?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

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

      • 執(zhí)行前:Lua環(huán)境里某些東西只初始化一次,準(zhǔn)備KEYS和ARGV兩個全局變量,設(shè)置超時控制hook。
      • 執(zhí)行后:定時Lua GC回收資源,用字典緩存已經(jīng)執(zhí)行過的Lua腳本。

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多