这是我在大型ruby脚本中遇到的错误。我已经设法把它归结为问题的本质。在讨论这个问题时,脚本是由OS / X上的某个人运行而没有遇到同样的问题,所以而不是一个ruby问题,这似乎是一个linux问题。
#!/usr/bin/env ruby
require "io/console"
def read_char
state=`stty -g`
`stty raw -echo`
input = $stdin.getc.chr
input << $stdin.read_nonblock(3) rescue nil
return input
ensure
`stty #{state}`
# Have also tried `stty -raw echo`
end
system("strace -o strace.log ./trace the start")
puts "press a cursor key"
c=read_char
system("strace -o strace2.log ./trace regular key")
puts "press a regular key"
c=read_char
system("strace -o strace3.log ./trace cursor key")
puts "done press return to exit"
有关非Rubyists的此代码的提及,<<
附加到数组 [1] 。
Backticks的行为与sh
中的相同。目前,我不知道Ruby如何具体实现read_nonblock
。我正在深入研究它。如果问题仍未得到答复,请提及。
稍后我会将C ++源代码发布到“跟踪”函数,但没有源代码就很容易理解。读取命令行并写入预定文件。然后请求输入。从STDIN读取一些输入后,将其保存到同一文件并退出。它本质上是一个调试功能,暂时取代了一些更复杂的功能。
问题
第三个系统调用行为不当,有点像是从/dev/zero
接收它的输入。
STDIN读取的日志条目如下所示:
strace.log:read(0, "1\n", 1024) = 2
strace2.log:read(0, "2\n", 1024) = 2
strace3.log:read(0, 0x7fb388ba0000, 1024) = -1 EAGAIN (Resource temporarily unavailable)
那么为什么第三个跟踪读取内存映射IO?
分配内存的命令条目是:
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb388ba0000
为什么mmap
命令将fd
-1作为参数?的 [2]
附加评论。根据我到目前为止所描述的内容,我几乎必须得出结论,当EAGAIN错误发生时,某些内容无法正常清理,或者stdin状态不知何故被搞砸了起来,现在stdin被堵塞了。然后由系统命令继承。因此标题问题:如何重置stdin,使其行为与最初的行为相同。
[1] 是的,作为一名C ++程序员,我理解这可能会让人讨厌。
[2] 我不熟悉Unix内存映射的IO,我自己从来没有写任何东西。 Windows内存映射的IO我很久以前就用过了。
答案 0 :(得分:1)
mmap()
将-1
作为文件描述符,因为标记为MAP_ANONYMOUS
。
MAP_ANONYMOUS
给出零初始化缓冲区。没有文件被读取。
因此,它在地址0x7fb388ba0000
处给出了一个4096个零初始化字节的r / w区域。
更新。好的,进一步看看它。
在stdin
之后,O_NONBLOCK
很可能会留下$stdin.read_nonblock(3)
。
不确定如何使用传统的 ruby I / O函数取消设置或阻止此操作,但您可以使用fcntl
进行修复。所以,在Ruby中就像这样:
require 'fcntl'
def read_char
...
ensure
flags = $stdin.fcntl(Fcntl::F_GETFL, 0)
$stdin.fcntl(Fcntl::F_SETFL, (~Fcntl::O_NONBLOCK) & flags)
end
在C中,人们可以朝着以下方向做点什么:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
...
char buf[32] = {0};
int fd, flags;
fd = fileno(stdin);
if (fd == -1) {
perror("fileno");
return 1;
}
flags = fcntl(fd, F_GETFL, 0);
if (flags & O_NONBLOCK) {
flags &= (~O_NONBLOCK);
fcntl(fd, F_SETFL, flags);
}
if(!(fgets(buf, 32, stdin)) {
if (ferror (stdin))
perror("fgets");
}
...