io.popen 如何使用lua变量?

763 views
Skip to first unread message

朱茂海

unread,
Oct 15, 2012, 11:29:15 PM10/15/12
to open...@googlegroups.com
js="aaaa"
local handle = io.popen("echo "..js)
local result = handle:read("*a")
这样会出错:
stdin:1: attempt to index global 'handle' (a nil value)
stack traceback:
        stdin:1: in main chunk
        [C]: ?

如何解决,谢谢!

smallfish

unread,
Oct 15, 2012, 11:33:54 PM10/15/12
to open...@googlegroups.com
"echo" .. js 连接符号中间有空格?
--



朱茂海

unread,
Oct 15, 2012, 11:36:12 PM10/15/12
to open...@googlegroups.com
有空格的,没有空格会是这样:
> local handle = io.popen("echo"..js)
sh: echoaaaaaaaaaaa: command not found

Lance

unread,
Oct 15, 2012, 11:41:57 PM10/15/12
to open...@googlegroups.com
我这里跑你的代码是正常的,你最好能多提供些上下文版本什么的

Lance


2012/10/16 朱茂海 <z3292...@gmail.com>

朱茂海

unread,
Oct 15, 2012, 11:44:36 PM10/15/12
to open...@googlegroups.com
LuaJIT 2.0.0-beta10

Lance

unread,
Oct 15, 2012, 11:49:23 PM10/15/12
to open...@googlegroups.com

    server
    {
        listen 81;
        access_log /data/logs/81.access.log combined;
        error_log /data/logs/81.error.log warn;

        location / {

          js="aaaa"
          local handle = io.popen("echo "..js)
          local result = handle:read("*a")
          ngx.say("xxx" .. result)
        }
    }

[root@s conf]# curl localhost:81  
xxxaaaa

[root@s conf]# /usr/local/openresty/nginx/sbin/nginx -v
nginx version: ngx_openresty/1.2.3.5


Lance


2012/10/16 朱茂海 <z3292...@gmail.com>

朱茂海

unread,
Oct 15, 2012, 11:56:19 PM10/15/12
to open...@googlegroups.com
说下我的环境:
nginx-1.2.3  lua-nginx-module-v0.7.1rc1    luajit-2.0.0-beta10

smallfish

unread,
Oct 16, 2012, 1:11:10 AM10/16/12
to open...@googlegroups.com
哦,刚才我也测试了一下,环境是:ngx_openresty-1.2.3.8(Mac OSX 10.8)。

代码片段如下:

        location /echo {
            default_type text/html;
            content_by_lua '
              local js="aaaa"
              local handle = io.popen("echo " .. js)
              local result = handle:read("*a")
              ngx.say("xxx" .. result)
          ';
        }

测试结果比较诡异,就是一会可以出现“xxxaaaa”一会是HTTP500错误。错误率大概80-90%以上。

to @agentzh,这个是不是哪儿有一个bug(或者是shell调用的问题?),错误日志如下:

2012/10/16 13:08:50 [error] 31048#0: *23 lua handler aborted: runtime error: [string "content_by_lua"]:5: attempt to concatenate local 'result' (a nil value)
stack traceback:
coroutine 0:
        [string "content_by_lua"]: in function <[string "content_by_lua"]:1>, client: 127.0.0.1, server: localhost, request: "GET /echo HTTP/1.1", host: "0.0.0.0:8080"

朱茂海

unread,
Oct 16, 2012, 1:27:17 AM10/16/12
to open...@googlegroups.com
你现在测试用的openresty用到--with-luajit吗

lhmwzy

unread,
Oct 16, 2012, 1:53:22 AM10/16/12
to open...@googlegroups.com
在最新的1.2.4.1上没有错误信息输出
输出结果是:xxxaaaa

nginx/sbin/nginx -V
nginx version: ngx_openresty/1.2.4.1
built by gcc 4.2.1 20070719 [FreeBSD]
TLS SNI support enabled
configure arguments: --prefix=/usr/local/openresty/nginx
--add-module=../ngx_devel_kit-0.2.17
--add-module=../echo-nginx-module-0.41
--add-module=../xss-nginx-module-0.03rc9
--add-module=../ngx_coolkit-0.2rc1
--add-module=../set-misc-nginx-module-0.22rc8
--add-module=../form-input-nginx-module-0.07rc5
--add-module=../encrypted-session-nginx-module-0.02
--add-module=../drizzle-nginx-module-0.1.4
--add-module=../srcache-nginx-module-0.16
--add-module=../ngx_lua-0.7.1
--add-module=../headers-more-nginx-module-0.18
--add-module=../array-var-nginx-module-0.03rc1
--add-module=../memc-nginx-module-0.13rc3
--add-module=../redis2-nginx-module-0.09
--add-module=../redis-nginx-module-0.3.6
--add-module=../auth-request-nginx-module-0.2
--add-module=../rds-json-nginx-module-0.12rc10
--add-module=../rds-csv-nginx-module-0.05rc2
--with-ld-opt=-Wl,-rpath,/usr/local/libdrizzle/lib:/usr/local/openresty/luajit/lib
--with-http_ssl_module

Lance

unread,
Oct 16, 2012, 2:19:27 AM10/16/12
to open...@googlegroups.com
我的所有配置都是基于luajit的,否则性能差太多了

Lance


2012/10/16 朱茂海 <z3292...@gmail.com>

pengqi

unread,
Oct 16, 2012, 3:47:48 AM10/16/12
to open...@googlegroups.com
hi,

2012/10/16 朱茂海 <z3292...@gmail.com>
从你的错误信息来看,是访问了未定义的全局变量,你确认你实际测试的代码里面 local handle 和 handle:read()这2个语句里面的handle是一致的(没有笔误)? 因为handle被定义为局部变量,handle为nil(当io.popen()返回nil时),正常的错误提示应该类似于 。。。index local 'handle' (a nil value)


--
Jinglong
Software Engineer
Server Platforms Team at Taobao

朱茂海

unread,
Oct 16, 2012, 5:33:53 AM10/16/12
to open...@googlegroups.com
问题很奇怪,上面的代码我是在luajit命令行里执行的,会出错,但在nginx配置文件里写就没问题。

Lance

unread,
Oct 16, 2012, 5:37:39 AM10/16/12
to open...@googlegroups.com
[root@s ~]# cat 1.lua
js="aaaa"
local handle = io.popen("echo "..js)
local result = handle:read("*a")
print(result)
[root@s ~]# /usr/local/openresty/luajit/bin/luajit-2.0.0-beta10 1.lua
aaaa


Lance


2012/10/16 朱茂海 <z3292...@gmail.com>
问题很奇怪,上面的代码我是在luajit命令行里执行的,会出错,但在nginx配置文件里写就没问题。
--

pengqi

unread,
Oct 16, 2012, 5:47:24 AM10/16/12
to open...@googlegroups.com
hi,

恩,必须将这整段代码放进一个文件里面执行,或者将这段代码写作一行,一次执行掉,如果首先执行
local handle = io.popen("echo "..js)
再执行local result = handle:read("*a"),第1次执行时定义的handle局部变量对第2次执行是不可见的。

agentzh

unread,
Oct 16, 2012, 3:31:13 PM10/16/12
to open...@googlegroups.com
Hello!

首先,我们应该对 io.popen 和 io.read 这些操作进行恰当地错误处理:

location /echo {
default_type text/html;
content_by_lua '
local js = "aaaa"

local handle, err = io.popen("echo " .. js)
if not handle then
ngx.log(ngx.ERR, "failed to open: ", err)
return
end
local result, err = handle:read("*a")
if not result then
ngx.log(ngx.ERR, "failed to read: ", err)
return
end
ngx.print("xxx" .. result)
';
}

然后我们以 ab -c10 对这个 /echo 接口施加压力,便可以在 nginx 的错误日志文件中看到类似这样的错误:

[error] 27462#0: *6937 [lua] [string "content_by_lua"]:10: failed
to read: Interrupted system call, ...

显然 io.read() 操作失败了,而且是系统调用被信号这样的东西给中断了。接下来,使用下面的 systemtap 追踪发送给当前
nginx worker 进程的所有信号:

# signal.stp
probe begin {
println("Start tracing...\nHit Ctrl-C to end.")
}

probe signal.send {
if (sig_pid == target()) {
printf("%s was sent to %s (pid:%d) by %s uid:%d\n",
sig_name, pid_name, sig_pid, execname(),
uid())
}
}

执行之(同时请求 /echo 接口):

$ stap -x 27462 signal.stp
Start tracing...
Hit Ctrl-C to end.
SIGCHLD was sent to nginx (pid:27462) by sh uid:1000
SIGCHLD was sent to nginx (pid:27462) by sh uid:1000
...

假设这里只有一个 nginx worker 进程,并且其 pid 是 27462.

从这个 systemtap 脚本的输出可以清楚地看到,io.popen 确实创建了新的 sh 子进程,而当 sh
子进程退出时,便会自动向它的父进程,也就是调用 io.popen 的 nginx worker 进程发送 SIGCHLD 信号。而如果此时
nginx 正阻塞在 io.read() 发起的 read 系统调用上时,则该系统调用就会被中断,并返回错误“Interrupted
system call”. 一个直接的解决办法是,总是显式地调用 io.close() 方法关闭当前的 pipe 文件句柄:

location /echo {
default_type text/html;
content_by_lua '
local js = "aaaa"

local handle, err = io.popen("echo " .. js)
if not handle then
ngx.log(ngx.ERR, "failed to open: ", err)
return
end
local result, err = handle:read("*a")
handle:close()
if not result then
ngx.log(ngx.ERR, "failed to read: ", err)
return
end
ngx.print("xxx" .. result)
';
}

这样可以降低 Nginx 已经在处理下一个请求时才收到当前 shell 子进程的 SIGCHLD
信号。不过,正如上面的分析所暗示的,io.popen 和 io.read/io.write 这些都会发起系统调用都是阻塞
I/O,也就是说,使用它们的 ngx_lua 接口都只有最多 1 并发每 nginx worker 进程。所以是应当慎用的。

我有计划自己在 ngx_lua 中以非阻塞方式重新实现 io.popen 以及对应的 io.read/io.write
方法。虽然系统管道天然不可能是 C10K 的,但至少读写不要阻塞 nginx worker 进程 :)

另外,值得一提的是,在上面这个例子中,即使显式调用了 handle:close(),我们仍然会在 nginx 错误日志中得到类似下面这样的错误信息:

[alert] 17237#0: waitpid() failed (10: No child processes)

这是 Nginx worker 进程注册的 SIGCHLD 信号处理程序打印的无害的错误。更多细节可以参见静龙同学早前发的这一篇贴子:

http://groups.google.com/group/openresty/msg/6f353536526441c3

Best regards,
-agentzh

Reply all
Reply to author
Forward
0 new messages