【问题标题】:How to reset stdin after an EAGAIN error?如何在 EAGAIN 错误后重置标准输入?
【发布时间】:2013-05-11 21:42:12
【问题描述】:

这是我在大型 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"

关于非 Ruby 爱好者的代码,&lt;&lt; 附加到数组[1]

反引号的行为与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)

那么为什么第三条trace读取内存映射IO呢?

分配内存的命令条目是:

mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb388ba0000

为什么mmap 命令将-1 的fd 作为参数? [2]

附加评论。根据我到目前为止所描述的,我几乎必须得出结论,当 EAGAIN 错误发生时,某些东西没有正确清理,或者 stdin 状态以某种方式混乱起来,现在标准输入被阻塞了。然后这会被系统命令继承。因此标题问题:您如何重置标准输入,以便它的行为与最初的行为一样。

[1] 是的,作为一名 C++ 程序员,我明白这有多烦人。

[2] 我对 Unix 内存映射 IO 不太熟悉,我自己从未使用它写过任何东西。我很久以前使用过的 Windows 内存映射 IO。

【问题讨论】:

  • 这更适用于stackoverflow。此外,我认为标题不太适合这个问题。
  • 其实我觉得这个标题很合适。我正在添加一条解释它的评论。我只是忘了把它们绑在一起。
  • 至于对 stackoverflow 的更多了解,评论将解决这个问题,但它似乎更像是一个 linux 系统调用问题而不是一个编程问题,请注意这个问题在 OS 上没有看到/X 但在 Linux 上。
  • 请参阅此参考:meta.unix.stackexchange.com/questions/314/…。看着接近投票,我希望你的问题很快就会被转移到 SO 上。

标签: mmap stty


【解决方案1】:

mmap()-1 作为文件描述符,因为给出了标志MAP_ANONYMOUS

MAP_ANONYMOUS 给出一个零初始化缓冲区。没有文件被读取。

因此,它在地址 0x7fb388ba0000 处提供了 4096 个零初始化字节的 r/w 区域。


更新。好的,再看一遍。

stdin 很可能在$stdin.read_nonblock(3) 之后留下O_NONBLOCK

不确定如何使用 常规 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");
}

...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-21
    • 1970-01-01
    • 2012-10-19
    • 1970-01-01
    • 1970-01-01
    • 2013-01-11
    • 2013-05-11
    • 2014-03-22
    相关资源
    最近更新 更多