Lua笔记(24)—— tonumber和tostring

尽管Lua会提供数字和字符串之间的自动转换:

> print("10" + 30)
40
> print(10 .. 30)
1030

为了使程序的可读性更好,可以考虑使用显示转换:

> print(tonumber("10") + 30)
40
> print(tostring(10) .. tostring(30))
1030

tonumber把字符串转换为数字,而tostring则把数字转换为字符串。

另外,把数字和空字符串连接起来,也可以达到tostring的效果:

> print(type(10 .. ""))
string

 

Lua笔记(23)—— pairs和ipairs

在这篇帖子中,提到pairsipairs的区别:

pairs() returns an iterator that will return the key and value of every key in the table, in no particular order. Usually, k and v are used to hold the key and value it returns. This is used to perform actions on each item in a table in turn, like when printing the contents of each value in the table.

ipairs() is very similar to pairs(), except that it will start at table[1] and iterate through all numerically indexed entries until the first nil value. It does this in order, which can be useful when you’re looking for the first item in a list that meets certain criterion.

pairs遍历任意table,返回的keyvalue没有什么顺序。而ipairs只会返回key是数字的元素。ipairs通常用在访问数组上。

参考下面这个程序:

t = {hello = 100, 200, 300}

print("k, v in pairs:")
for k, v in pairs(t) do
    print(k, v)
end

print("k, v in ipairs:")
for k, v in ipairs(t) do
    print(k, v)
end

执行结果如下:

k, v in pairs:
1   200
2   300
hello   100
k, v in ipairs:
1   200
2   300

可以看到,pairs可以遍历所有的key-value对,而ipairs只会访问key是数字的元素。

 

Luajit笔记(2)——FFI库(1)

Luajit提供的FFI库(ffi.*)允许Lua代码调用外部的C函数和使用C数据结构。但是默认情况下,FFI库不会被加载和初始化。因此建议在每个使用FFI库的Lua文件开头加载:

local ffi = require("ffi")

看下面这个例子:

local ffi = require("ffi")

ffi.cdef[[
int printf(const char *format, ...);
]]

ffi.C.printf("Hello world!\n")

执行结果如下:

Hello world!

(1)
ffi.cdef的定义如下:

ffi.cdef(def)

def必须是一个Lua字符串,建议使用“[[...]]”这种格式。ffi.cdef包含的是对C语言类型的定义和外部符号(变量和函数)的声明(仅仅是声明,并没有和实际的内存地址进行绑定,实际的绑定是通过C library namespace)。要注意对C类型的声明不会经过C预处理器,除了#pragma pack以外,包括#define在内的指令都要进行处理替换,比如使用enum等等。

(2)
ffi.C是默认的C library namespace。它同编译器有些类似,但不用显示地声明链接库。在POSIX系统上,ffi.C会在defaultglobalnamespace上绑定符号。包括libclibm,等等。还有Luajit自身提供的API

 

Shark代码分析笔记(4)——bpf.lua(未完待续)

bpf.lua位于bpf文件夹下,其代码如下(省去版权信息):

local ffi = require("ffi")
local C = ffi.C

local bpf = {}

bpf.cargs_extra = ""

---------------------------------------------------------------

bpf.cargs_add = function(s)
  bpf.cargs_extra = s
end

---------------------------------------------------------------

ffi.cdef[[
int load_bpf_file(const char *path);
]]

local function load_bpf_file(path)
  local ret = C.load_bpf_file(path)
  if tonumber(ret) ~= 0 then
    os.exit(-1)
  end
end

local function run_llc_bpf(bc_file, bpfobj_file)
  os.execute("llc-bpf -march=bpf -filetype=obj -o " ..
              bpfobj_file .. " " .. bc_file)
end

---------------------------------------------------------------
local function split(s, delimiter)
  local result = {};
  for match in (s..delimiter):gmatch("(.-)"..delimiter) do
    table.insert(result, match);
  end
  return result;
end

local basic_type_tbl = {
  "char", "u8", "short", "u16", "int", "u32", "long", "long long", "u64"
}

local function get_real_type(typestr)
  for _, v in pairs(basic_type_tbl) do
    if v == typestr then
      return v
    end
  end

  return "cdata"
end

local function process_type(typestr0)
  local typestr, size, size

  typestr, size = string.match(typestr0, "(.*) %[(.*)%]")
  if typestr ~= nil and size ~= nil then
    sizestr = "sizeof("..typestr .. ") * "..size
    typestr = typestr
    if typestr == "char" then
      typestr = "string"
    end
  else
    typestr, size = string.match(typestr0, "(.*)%[(.*)%]")
    if typestr ~= nil and size ~= nil then
      sizestr = "sizeof("..typestr .. ") * "..size
      typestr = typestr
      if typestr == "char" then
        typestr = "string"
      end
    else
      sizestr = "sizeof("..typestr0..")"
      typestr = get_real_type(typestr0)
    end
  end

  return typestr, sizestr
end

local function gen_map_decl(map_type, key_type, val_type, entries, name)
  local key_typestr, key_sizestr = process_type(key_type)
  local val_typestr, val_sizestr = process_type(val_type)

  if not entries then
    entries = 1024;
  end

  local str = string.format("struct bpf_map_def SEC(\"maps\") %s = {\n" ..
                            "\t.name = \"%s\",                 \n" ..
                            "\t.key_type = \"%s\",             \n" ..
                            "\t.val_type = \"%s\",             \n" ..
                            "\t.type = BPF_MAP_TYPE_%s,        \n" ..
                            "\t.key_size = %s,                 \n" ..
                            "\t.value_size = %s,               \n" ..
                            "\t.max_entries = %d,              \n};\n",
        name, name, key_typestr, val_typestr, map_type, key_sizestr,
        val_sizestr, entries)
  io.write(str)
end

local function translate_cdef(s)
  local n = split(s, '\n')
  for _, line in pairs(n) do
    local key_type, val_type, entries, name
    local match = false

    key_type, val_type, name = string.match(line,
             "bpf_map_hash<([^,]*), ([^,]*)> (.*);")
    if key_type and val_type and name then
      gen_map_decl("HASH", key_type, val_type, nil, name)
      match = true
    end

    key_type, val_type, entries, name = string.match(line,
             "bpf_map_hash<([^,]*), ([^,]*), (%d+)> (.*);")
    if key_type and val_type and entries and name then
      gen_map_decl("HASH", key_type, val_type, entries, name)
      match = true
    end

    key_type, val_type, name = string.match(line,
             "bpf_map_array<([^,]*), ([^,]*)> (.*);")
    if key_type and val_type and name then
      gen_map_decl("ARRAY", key_type, val_type, nil, name)
      match = true
    end

    key_type, val_type, entries, name = string.match(line,
             "bpf_map_array<([^,]*), ([^,]*), (%d+)> (.*);")
    if key_type and val_type and entries and name then
      gen_map_decl("ARRAY", key_type, val_type, entries, name)
      match = true
    end

    if not match then
      io.write(line, "\n")
    end
  end

  io.write("\nchar _license[] SEC(\"license\") = \"GPL\";\n")
  io.write("\n#include <linux/version.h>\n")
  io.write("\nu32 _version SEC(\"version\") = LINUX_VERSION_CODE;\n")
end

local function compile(srcfile, objfile)
   local f = io.popen("uname -r", "r")
   local release = f:read()
   local linuxinc = string.format("/lib/modules/%s/build/", release)
   local bpfinc = "bpf/libbpf"

   local clang_cmd = string.format("clang -nostdinc -isystem /usr/lib/gcc/x86_64-linux-gnu/4.8/include -I%s -I%s/arch/x86/include -I%s/arch/x86/include/generated/uapi -I%s/arch/x86/include/generated  -I%s/include -I%s/arch/x86/include/uapi -I%s/arch/x86/include/generated/uapi -I%s/include/uapi -I%s/include/generated/uapi -include %s/include/linux/kconfig.h %s -D__KERNEL__ -Wno-unused-value -Wno-pointer-sign -O2 -emit-llvm -x c -c %s -o %s", bpfinc, linuxinc, linuxinc, linuxinc, linuxinc, linuxinc, linuxinc, linuxinc, linuxinc, linuxinc, bpf.cargs_extra, srcfile, objfile)

  os.execute(clang_cmd)
end

-- builtin cdef function
bpf.cdef = function(s)
  local file

  local srctmp = os.tmpname()
  file = io.open(srctmp, "w")
  io.output(file)
  translate_cdef(s)

  io.close(file)
  io.output(io.stdout)

  -- dump source
  local f = io.open(srctmp, "rb")
  local content = f:read("*all")
  print(content)
  f:close()

  local bctmp = os.tmpname()
  compile(srctmp, bctmp)

  local bpftmp = os.tmpname()
  run_llc_bpf(bctmp, bpftmp)

  -- pass bpf table for bpf object loading, need clear it after load done.
  _G["bpf"] = bpf
  load_bpf_file(bpftmp)

  os.remove(srctmp)
  os.remove(bctmp)
  os.remove(bpftmp)
end

---------------------------------------------------------------

bpf.print_map = function(map)
  local map = map
  for k in pairs(map) do
    print(k, map[k])
  end
end

---------------------------------------------------------------

bpf.copy_map = function(map)
  local new = {}
  local map = map
  for k in pairs(map) do
    new[k] = map[k]
  end
  return new
end

---------------------------------------------------------------

local function fill_line(n, max)
  for i = 1, max do
    if i < n then
      io.write("*")
    else
      io.write(" ")
    end
  end
end

-- print histogram for bpf.var.map
-- Only support number key now.
bpf.print_hist_map = function(t)
  local histo = {}
  local stdSum, max = 0, 0

  for k in pairs(t) do
    if t[k] ~= 0 then
      local k1 = math.pow(2, math.floor(math.log(k, 2)))
      if histo[k1] == nil then histo[k1] = 0 end
      histo[k1] = histo[k1] + t[k]

      stdSum = stdSum + t[k]
      if k1 > max then max = k1 end
    end
  end

  print("        value  ------------- Distribution -------------  count")
  local k = 0
  while k <= max do
    local v = histo[k]
    if v == nil then v = 0 end

    io.write(string.format("%13d |", k))
    fill_line(v * 40 / stdSum, 40)
    io.write(string.format("| %d", v))

    print()

    if k == 0 then k = 1 else k = k * 2 end
  end
end

return bpf

(1)

local ffi = require("ffi")
local C = ffi.C

local bpf = {}

bpf.cargs_extra = ""

以上代码导入ffi模块,并且定义bpf这个tablebpf table有一个cargs_extrakey,所对应的值是一个空字符串。

 

Lua笔记(22)—— error和assert

Lua中的error函数定义:

error (message [, level])

打印出message后,会终止程序运行。关于level的含义,参考下面例子理解会更清楚(test.lua):

function f0()
        error("Error!")
end

function f1()
        f0()
end

function f2()
        f1()
end

f2()

执行如下:

lua: test.lua:2: Error!
stack traceback:
        [C]: in function 'error'
        test.lua:2: in function 'f0'
        test.lua:6: in function 'f1'
        test.lua:10: in function 'f2'
        test.lua:13: in main chunk
        [C]: in ?

默认情况下,level值为1。“lua: test.lua:2: Error!”把错误位置指向了脚本的第2行。把f0函数修改一下:

function f0()
        error("Error!")
end

再次执行,输出如下:

lua: test.lua:6: Error!
stack traceback:
        [C]: in function 'error'
        test.lua:2: in function 'f0'
        test.lua:6: in function 'f1'
        test.lua:10: in function 'f2'
        test.lua:13: in main chunk
        [C]: in ?

这次错误位置指向了脚本的第6行(“lua: test.lua:6: Error!”),也就是f1()函数,可以看到level指定了发生错误时,应该输出函数调用栈的哪一级函数。

assert函数定义如下:

assert (v [, message])

v是假(nilfalse)时,调用error函数,否则返回所有所有参数。其中message默认值是"assertion failed!"。举例如下:

function f0()
        assert(nil, "Assert!")
end
f0()

输出如下:

lua: test.lua:2: Assert!
stack traceback:
        [C]: in function 'assert'
        test.lua:2: in function 'f0'
        test.lua:13: in main chunk
        [C]: in ?

Lua笔记(21)—— “require module”的等价形式

Learn Lua in 15 Minutes中提到,require module的等价形式:

-- Another file can use mod.lua's functionality:
local mod = require('mod')  -- Run the file mod.lua.

-- require is the standard way to include modules.
-- require acts like:     (if not cached; see below)
local mod = (function ()
  <contents of mod.lua>
end)()
-- It's like mod.lua is a function body, so that
-- locals inside mod.lua are invisible outside it.

这个解释很好地说明了require module的原理,让人豁然开朗。

Lua笔记(19)—— 表达式(expression)和语句(statement)

Lua中的表达式(expression)定义:

Expressions denote values. Expressions in Lua include the numeric constants and string literals, variables, unary and binary operations, and function calls. Expressions include also the unconventional function definitions and table constructors.

表达式产生值,包括:数字常量,字符串,变量,单目和双目运算,另外还有函数调用。此外,表达式还包括函数定义和创建table

Lua中的语句(statement)定义:

Lua supports an almost conventional set of statements, similar to those in C or Pascal. The conventional statements include assignment, control structures, and procedure calls. Lua also supports some not so conventional statements, such as multiple assignments and local variable declarations.

语句包括:赋值,控制结构,过程调用(block),多重赋值和local变量定义。

Lua笔记(18)—— 解释器的交互模式

Lua自带解释器(Lua.c编译生成的可执行文件)的交互模式会把每一单独的输入行看做是一个chunk,除非这一行不是完整的命令,例如:

> function test()
>> print("test")
>> end

>>标示这一行和之前的行属于同一行。

因此,如果在一行输入local i = 1Lua就会运行这个chunk。再另起一行输入新命令以后,相当于一个新的chunk,前一行的local变量是不可见的。举例如下:

> local i = 10
> print(i)
nil

解决这个问题可以使用do-end将这个代码包成一个chunk

> do
>> local i = 10
>> print(i)
>> end
10

Lua笔记(17)—— 什么是chunk?

Pil中,chunk的定义:

Each piece of code that Lua executes, such as a file or a single line in interactive mode, is called a chunk. A chunk is simply a sequence of commands (or statements).

Lua执行的一段代码,无论是一个Lua文件,或者仅仅是一条语句,均统称为chunk。说白了,chunk就是一段语句(statement)。

Lua笔记(16)——全局变量和“local”变量

执行以下Lua代码:

i = 32
local i = 0
f = load("i = i + 1; print(i)")
g = function () i = i + 1; print(i) end
f() --> 33
g() --> 1

f()输出33g()输出1。原因是第一个i是全局变量,第二个ilocal变量,而同名的local变量总是覆盖掉全局变量。load产生的函数只能看到全局变量,因此f()输出33。如果想让g()函数访问全局变量i,可以利用全局环境变量_G

g = function () _G.i = _G.i + 1; print(_G.i) end
g() --> 34