好的,由于运行测试代码不需要任何外部Ruby库,我可以在我的机器上编译1.9版本而无需安装它,并运行测试程序。
以下是我的观察:
- Ruby似乎“挂起”(您无法中断它,它也不会自己退出)。
top
显示Ruby以100%的CPU运行。
strace
一旦进入100%CPU模式,就不会输出任何内容。
从这里可以明显看出Ruby陷入了无限循环。查看io.c
中的each_byte
,并在可疑位置添加printf
,可以发现我们卡在了哪里:
static VALUE
rb_io_each_byte(VALUE io)
{
rb_io_t *fptr;
char *p, *e;
RETURN_ENUMERATOR(io, 0, 0);
GetOpenFile(io, fptr);
for (;;) {
p = fptr->rbuf+fptr->rbuf_off;
e = p + fptr->rbuf_len;
printf("UH OH: %d < %d\n", p, e);
while (p < e) {
fptr->rbuf_off++;
fptr->rbuf_len--;
rb_yield(INT2FIX(*p & 0xff));
p++;
errno = 0;
}
rb_io_check_byte_readable(fptr);
READ_CHECK(fptr);
if (io_fillbuf(fptr) < 0) {
break;
}
}
return io;
}
在我的计算机上,它打印出这个:
UH OH: 0 < 0
UH OH: 137343104 < 137351296
UH OH: 137343119 < 137343104
UH OH: 137343119 < 137343104
UH OH: 137343119 < 137343104
...ad infinitum...
137343119并不小于137343104,这意味着我们停止进入while
循环(这将产生该块)。
当您运行代码以避免挂起时,会得到以下结果:
UH OH: 0 < 0
UH OH: 137341560 < 137349752
UH OH: 137341560 < 137349752
UH OH: 137341560 < 137349752
UH OH: 137341560 < 137349752
....
137341560小于137349752。
无论如何,目前我只能提供这些信息。仍然不知道为什么会发生这种情况。但是现在我们至少知道了发生了什么。编写该代码的人可能可以立即解释为什么会发生这种情况。
总之,我仍然认为lseek
调用会以某种方式破坏Ruby的内部文件指针,并且上面的循环因此而出现问题。
编辑
以下是修复方法:
将io.c
中的flush_before_seek
更改为以下内容:
static rb_io_t *
flush_before_seek(rb_io_t *fptr)
{
int wbuf_len = fptr->wbuf_len;
if (io_fflush(fptr) < 0)
rb_sys_fail(0);
if (wbuf_len != 0)
io_unread(fptr);
errno = 0;
return fptr;
}
我添加的是对
wbuf_len != 0
的检查,这样我们就不会不必要地执行
io_unread
。在
each_byte
循环中调用
io_unread
是导致问题混乱的原因。跳过未读取使事情正常工作,并且所有针对
make test
的测试仍然通过。
无论如何,这不是一个合适的修复方法,因为
f.pos
存在一些根本性的思考错误。它只是一个解决方法......但它仍然解决了上述问题。 :-/
f.each_byte { if((i += 1) % 16 == 1) then puts f.pos - 1; else puts f.pos - 1; end }
完成得非常好。 - mu is too shortpos
会执行各种有趣的操作,包括fflush
和lseek
。不确定它如何与同时读取字节的循环配合。 - Casperf.each_byte {puts "%08X\n" % (f.pos - 1) if f.pos % 16 == 1}
(完全不使用i
)也不会挂起。有很多重构循环的方法,使得pos
仍然在其中被调用,并且脚本能够正确终止。 - Maxstrace
!这将很快告诉你它是否进入了某个无限循环,或者是否存在类似解释器死锁之类的问题。有人愿意尝试一下,让我们看看结果如何? - Casperstrace
运行了几个不同版本的脚本。它们都从read
文件开始,然后_llseek
回到开头再次开始_llseek
。我唯一能看到的明显区别是挂起的版本通过16字节增量寻找,而我在最后一条评论中发布的版本逐字节寻找。我觉得这很奇怪,因为从逻辑上讲,两个版本都应该以单字节增量寻找。也许Ruby试图简化循环,从而引入了错误。 - Max