看一下main
函数:
int main(int argc, char **argv)
{
int ret = EXIT_FAILURE;
int flags = 0, script;
int base;
if (argv[0] && argv[0][0]) progname = argv[0];
lua_State *ls = lua_open();
if (!ls) {
l_message(progname, "cannot create state: not enough memory");
return ret;
}
g_ls = ls;
script = collectargs(argv, &flags);
if (script <= 0) {
print_usage();
return 0;
}
if (flags & FLAGS_VERSION)
print_version();
luaL_openlibs(ls);
lua_getglobal(ls, "package");
lua_getfield(ls, -1, "preload");
lua_remove(ls, -2);
lua_pushcfunction(ls, luaopen_luv);
lua_setfield(ls, -2, "uv");
luaL_openlib(ls, "shark", ll_shark, 0);
lua_getglobal(ls, "shark");
lua_pushboolean(ls, shark_verbose);
lua_setfield(ls, -2, "verbose");
lua_pop(ls, 1);
int narg = getargs(ls, argv, script);
lua_setglobal(ls, "arg");
#include "shark_init.h"
luaL_loadbuffer(ls, luaJIT_BC_shark_init, luaJIT_BC_shark_init_SIZE,
NULL);
if((ret = lua_pcall(ls, 0, 0, 0))) {
ret = lua_report(ls, ret);
goto out;
}
g_event_loop = luv_loop(ls);
if((ret = luaL_loadfile(ls, argv[script]))) {
ret = lua_report(ls, ret);
goto out;
}
base = lua_gettop(ls) - 1;
lua_pushcfunction(ls, lua_traceback);
lua_insert(ls, base);
if (lua_pcall(ls, 0, 0, base)) {
fprintf(stderr, "%s\n", lua_tostring(ls, -1));
exit(EXIT_FAILURE);
}
lua_pop(ls, 1);
uv_run(g_event_loop, UV_RUN_DEFAULT);
ret = 0;
out:
lua_close(ls);
return ret;
}
(1)
if (argv[0] && argv[0][0]) progname = argv[0];
progname
存的是运行程序名字:/path/to/shark
。
(2)
lua_State *ls = lua_open();
if (!ls) {
l_message(progname, "cannot create state: not enough memory");
return ret;
}
g_ls = ls;
创建一个新的Lua
运行环境,为了后续使用。
(3)
script = collectargs(argv, &flags);
if (script <= 0) { /* invalid args? */
print_usage();
return 0;
}
if (flags & FLAGS_VERSION)
print_version();
先看一下print_version
:
static void print_version(void)
{
fputs(SHARK_VERSION " -- " SHARK_COPYRIGHT ". " SHARK_URL "\n", stdout);
exit(0);
}
比较简单,打印出版本就退出了。
再看一下print_usage
:
static void print_usage(void)
{
fprintf(stderr,
"usage: shark [options]... [script [args]...].\n"
"Available options are:\n"
" -v Show version information.\n");
fflush(stderr);
}
可以看到shark
的使用方法:
shark [options]... [script [args]...]
shark
程序后面跟着可选参数,还有执行脚本。
(4)
luaL_openlibs(ls);
加载Luajit
提供的函数库。
(5)
lua_getglobal(ls, "package");
lua_getfield(ls, -1, "preload");
lua_remove(ls, -2);
lua_getglobal(ls, "package");
用来把package
这个table
压入堆栈:
___________________________
|____ package(类型:table)___| (-1)
lua_getfield(ls, -1, "preload");
用来从index
是-1
处取出key
为preload
的值,也就是:package.preload
这个table
,并压入堆栈:
___________________________
|package.preload(类型:table)| (-1)
|____ package(类型:table)___| (-2)
lua_remove(ls, -2);
把package
从堆栈中移除:
___________________________
|package.preload(类型:table)| (-1)
(6)
// Store uv module definition at preload.uv
lua_pushcfunction(ls, luaopen_luv);
lua_setfield(ls, -2, "uv");
lua_pushcfunction(ls, luaopen_luv);
把luaopen_luv
函数压入堆栈:
___________________________
|__luaopen_luv(类型:函数)___| (-1)
|package.preload(类型:table)| (-2)
lua_setfield(ls, -2, "uv");
执行效果是:package.preload.uv = luaopen_luv
,然后把luaopen_luv
弹出堆栈:
____________________________________________
|package.preload(类型:table,uv = luaopen_luv)| (-1)
(7)
luaL_openlib(ls, "shark", ll_shark, 0);
ll_shark
数组定义如下:
static const struct luaL_reg ll_shark[] = {
{"debuginfo_set", &shark_api_debuginfo_set},
{"lua_ref", &shark_api_lua_ref},
{"get_ref", &shark_api_get_ref},
{"stats", &shark_api_stats},
{"set_binary", &shark_api_set_binary},
{"exec", &shark_api_exec},
#ifndef BPF_DISABLE
{"open_raw_sock", &shark_api_open_raw_sock},
{"sock_attach_bpf", &shark_api_sock_attach_bpf},
{"iptos", &shark_api_iptos},
#endif
{NULL, NULL}
};
luaL_openlib(ls, "shark", ll_shark, 0);
函数的作用是创建一个table
,然后将这个table
赋给package.loaded[shark]
和全局变量shark
,并将ll_shark
数组中的函数注册到这个table
,最后把table
压入堆栈:
————————————————————————————————————————————
|___一个注册ll_shark函数数组的table___________| (-1)
|package.preload(类型:table,uv = luaopen_luv)| (-2)
(8)
lua_getglobal(ls, "shark");
lua_pushboolean(ls, shark_verbose);
lua_setfield(ls, -2, "verbose");
lua_pop(ls, 1);
lua_getglobal(ls, "shark");
把shark table
压入堆栈:
____________________________________________
| shark(类型:table) | (-1)
————————————————————————————————————————————
|___一个注册ll_shark函数数组的table___________| (-2)
|package.preload(类型:table,uv = luaopen_luv)| (-3)
lua_pushboolean(ls, shark_verbose);
将shark_verbose
这个布尔值压入堆栈:
_____________________________________________
| ______shark_verbose(类型:bool)_____________| (-1)
| shark(类型:table) | (-2)
————————————————————————————————————————————
|___一个注册ll_shark函数数组的table___________| (-3)
|package.preload(类型:table,uv = luaopen_luv)| (-4)
lua_setfield(ls, -2, "verbose");
执行效果是:shark.verbose = shark_verbose
,然后把shark_verbose
弹出堆栈:
____________________________________________
| shark(类型:table, verbose = shark_verbose) | (-1)
————————————————————————————————————————————
|___一个注册ll_shark函数数组的table___________| (-2)
|package.preload(类型:table,uv = luaopen_luv)| (-3)
lua_pop(ls, 1);
把栈顶元素弹出堆栈:
————————————————————————————————————————————
|___一个注册ll_shark函数数组的table___________| (-1)
|package.preload(类型:table,uv = luaopen_luv)| (-2)
(9)
int narg = getargs(ls, argv, script);
lua_setglobal(ls, "arg");
getargs
函数实现如下:
static int getargs(lua_State *ls, char **argv, int n)
{
int narg;
int i;
int argc = 0;
while (argv[argc])
argc++;
narg = argc - (n + 1);
luaL_checkstack(ls, narg + 3, "too many arguments to script");
for (i = n + 1; i < argc; i++)
lua_pushstring(ls, argv[i]);
lua_createtable(ls, narg, n + 1);
for (i = 0; i < argc; i++) {
lua_pushstring(ls, argv[i]);
lua_rawseti(ls, -2, i - n);
}
return narg;
}
在getargs
函数中,传入参数n
是Lua
脚本在命令行参数的索引,而narg
则是脚本的参数。举个例子:
/root/shark/shark trace.lua 1 2
则argc
是4
,n
是1
,narg
是2
。
luaL_checkstack(ls, narg + 3, "too many arguments to script");
检查堆栈是否有足够的空间。
for (i = n + 1; i < argc; i++)
lua_pushstring(ls, argv[i]);
把参数1
和2
压入堆栈:
___________________________________________
|__________参数: 2__________________________| (-1)
| 参数: 1 | (-2)
|———————————————————————————————————————————
|___一个注册ll_shark函数数组的table___________| (-3)
|package.preload(类型:table,uv = luaopen_luv)| (-4)
lua_createtable(ls, narg, n + 1);
创建一个table
(包含narg
数组元素,n + 1
非数组元素)并压入堆栈:
———————————————————————————————————————————
|______table________________________________| (-1)
|__________参数: 2__________________________| (-2)
| 参数: 1 | (-3)
|———————————————————————————————————————————
|___一个注册ll_shark函数数组的table___________| (-4)
|package.preload(类型:table,uv = luaopen_luv)| (-5)
看最后一个循环:
for (i = 0; i < argc; i++) {
lua_pushstring(ls, argv[i]);
lua_rawseti(ls, -2, i - n);
}
lua_pushstring(ls, argv[i]);
依次把参数压入堆栈,lua_rawseti(ls, -2, i - n);
则是把参数传入table
:
—————————————————————————————————————————————————
|_table(index:value->1:shark;0:trace.lua;1:1;2:2_| (-1)
|__________参数: 2_______________________________| (-2)
| 参数: 1 | (-3)
|————————————————————————————————————————|
|___一个注册ll_shark函数数组的table________________| (-4)
|package.preload(类型:table,uv = luaopen_luv) | (-5)
lua_setglobal(ls, "arg");
作用是把栈顶table
弹出,并赋值给arg
。所以arg
就指向了这个table
(index:value->1:shark;0:trace.lua;1:1;2:2
)。堆栈变为:
____________________________________________
|__________参数: 2__________________________| (-1)
| 参数: 1 | (-2)
|———————————————————————————————————————————
|___一个注册ll_shark函数数组的table___________| (-3)
|package.preload(类型:table,uv = luaopen_luv)| (-4)
(10)
#include "shark_init.h"
luaL_loadbuffer(ls, luaJIT_BC_shark_init, luaJIT_BC_shark_init_SIZE,
NULL);
shark_init.h
是由shark_init.lua
生成的(以后再详细介绍shark_init.lua
),luaJIT_BC_shark_init
和luaJIT_BC_shark_init_SIZE
也定义在shark_init.h
文件中。
luaL_loadbuffer(ls, luaJIT_BC_shark_init, luaJIT_BC_shark_init_SIZE, NULL);
把luaJIT_BC_shark_init
这个chunk
压入堆栈:
____________________________________________
|_luaJIT_BC_shark_init chunk(类型:函数)______| (-1)
|__________参数: 2__________________________| (-2)
| 参数: 1 | (-3)
|———————————————————————————————————————————
|___一个注册ll_shark函数数组的table___________| (-4)
|package.preload(类型:table,uv = luaopen_luv)| (-5)
(11)
if((ret = lua_pcall(ls, 0, 0, 0))) {
ret = lua_report(ls, ret);
goto out;
}
接下来lua_pcall(ls, 0, 0, 0)
会运行luaJIT_BC_shark_init
这个chunk
。运行完后,把chunk
弹出堆栈:
————————————————————————————————————————————
|__________参数: 2__________________________| (-1)
| 参数: 1 | (-2)
|———————————————————————————————————————————
|___一个注册ll_shark函数数组的table___________| (-3)
|package.preload(类型:table,uv = luaopen_luv)| (-4)
lua_report
比较简单,就是如果出错的话,就从栈顶取出错误信息,打印完以后再弹栈:
int lua_report(lua_State *ls, int status)
{
if (status && !lua_isnil(ls, -1)) {
const char *msg = lua_tostring(ls, -1);
if (msg == NULL)
msg = "(error object is not a string)";
l_message(progname, msg);
lua_pop(ls, 1);
}
return status;
}
(12)
g_event_loop = luv_loop(ls);
if((ret = luaL_loadfile(ls, argv[script]))) {
ret = lua_report(ls, ret);
goto out;
}
base = lua_gettop(ls) - 1;
lua_pushcfunction(ls, lua_traceback);
lua_insert(ls, base);
if (lua_pcall(ls, 0, 0, base)) {
fprintf(stderr, "%s\n", lua_tostring(ls, -1));
exit(EXIT_FAILURE);
}
lua_pop(ls, 1);
uv_run(g_event_loop, UV_RUN_DEFAULT);
ret = 0;
out:
lua_close(ls);
return ret;
剩下这段代码就是运行脚本,其中lua_traceback
是脚本出错时的处理函数。个人觉得细节上还有些问题,需要和作者沟通一下,这块代码暂时留个小尾巴。