Lua 的标准输出与缓存

866 词
			<p>最近我遇到了个奇怪的问题,我的一个 Lua 脚本需要通过 shell 的重定向将输出追加到一个日志文档中。但是那个 Lua 脚本的输出在日志文档里看来却不是实时的,输出的文本直到脚本结束时才能看到。</p>

在 shell 下运行这个进程,是可以看到实时输出的:

-- buffer_test.lua
local socket = require "socket"

local const = 100

for i=1, const, 1 do
print(i)
socket.select(nil, nil, 1)
end

但是当通过重定向时,只有脚本结束后才能看到文档:

# lua buffer_test.lua >>log.txt 2>&1 &
# tail -f log.txt

看来当 Lua 的标准输出 stdout 连接的是终端时,采用了行缓存模式,而重定向到文档时则变成了完全缓存。翻遍了 Lua 的官方文档也没有找到这样的说明。但是在查看 stdio 的手册页时发现了下面的一段话:

原来 stdio 都是由 libc 提供,而我在 ldd lua 时发现 Lua 确实也依赖于 libc。这下就可以解释了:Lua 在连接是终端的时候采用的是行缓存,而连接的是非活跃的设备时是采用的是完全缓存。

但是假如我们中途终止脚本,查看日志:

# nl log.txt
 1	lua: io.lua:9: interrupted!
 2	stack traceback:
 3		[C]: in function 'select'
 4		io.lua:9: in main chunk
 5		[C]: ?
 6	1
 7	2
 8	3

可以发现异常的日志难道不应该在最后面吗?其实上面的引用已经帮我们回答了:stderr 并不是完全缓存,当发生异常时,stderr 首先被写入日志,接着缓存区的 stdout 才会被刷入文档。

回顾这个问题,给我们留下的经验是 Lua 的很多库的实现原理其实在 libc 与系统调用那儿,不要只把目光局限在 Lua 的文档上。其实在其他的语言中,我估计也差不多。我们在查找问题的时候,一定要跳出自己的固有思维,有时候自己非常有把握的知识恰恰是不准确的。