【问题标题】:How do you do non-blocking console I/O on Linux in C?你如何在 C 中的 Linux 上进行非阻塞控制台 I/O?
【发布时间】:2009-04-04 18:37:28
【问题描述】:

如何在 C 语言中在 Linux/OS X 上进行非阻塞控制台 IO?

【问题讨论】:

标签: c linux macos io nonblocking


【解决方案1】:

我想补充一个例子:

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>

int main(int argc, char const *argv[])

{
    char buf[20];
    fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
    sleep(4);
    int numRead = read(0, buf, 4);
    if (numRead > 0) {
        printf("You said: %s", buf);
    }
}

当你运行这个程序时,你有 4 秒的时间向标准输入提供输入。如果没有找到输入,它不会阻塞,只会返回。

2 个示例执行:

Korays-MacBook-Pro:~ koraytugay$ ./a.out
fda 
You said: fda
Korays-MacBook-Pro:~ koraytugay$ ./a.out
Korays-MacBook-Pro:~ koraytugay$ 

【讨论】:

  • 这是正确答案。它涵盖了所要求的两个操作系统(刚刚在 GNU/Linux 上测试过)。请记住,此解决方案仍将缓冲输入。 (例如,您需要按 Enter 键才能将输入传递给程序。
  • 为了更清楚,我会使用 STDIN_FILENO 而不是 0 作为文件描述符
  • 请注意,这可能会破坏终端,因为 stdin 可以与未来可能不期望 stdin 非阻塞的进程共享。运行程序后尝试使用cat。请使用一致的缩进样式。
【解决方案2】:

就像Pete Kirkham,我找到了cc.byexamples.com,它对我有用。去那里寻找对问题的一个很好的解释,以及 ncurses 版本。

我的代码需要从标准输入或文件中获取初始命令,然后在处理初始命令时注意取消命令。我的代码是 C++,但你应该可以使用scanf(),其余的我使用 C++ 输入函数getline()

肉是一个检查是否有可用输入的函数:

#include <unistd.h>
#include <stdio.h>
#include <sys/select.h>

// cc.byexamples.com calls this int kbhit(), to mirror the Windows console
//  function of the same name.  Otherwise, the code is the same.
bool inputAvailable()  
{
  struct timeval tv;
  fd_set fds;
  tv.tv_sec = 0;
  tv.tv_usec = 0;
  FD_ZERO(&fds);
  FD_SET(STDIN_FILENO, &fds);
  select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
  return (FD_ISSET(0, &fds));
}

这必须在任何标准输入函数之前调用当我在使用这个函数之前使用std::cin 时,它再也没有返回true。例如,main() 有一个如下所示的循环:

int main(int argc, char* argv[])
{ 
   std::string initialCommand;
   if (argc > 1) {
      // Code to get the initial command from a file
   } else {
     while (!inputAvailable()) {
       std::cout << "Waiting for input (Ctrl-C to cancel)..." << std::endl;
       sleep(1);
     }
     std::getline(std::cin, initialCommand);
   }

   // Start a thread class instance 'jobThread' to run the command
   // Start a thread class instance 'inputThread' to look for further commands
   return 0;
}

在输入线程中,新命令被添加到队列中,由jobThread 定期处理。 inputThread 看起来有点像这样:

THREAD_RETURN inputThread()
{
  while( !cancelled() ) {
    if (inputAvailable()) {
      std::string nextCommand;
      getline(std::cin, nextCommand);
      commandQueue.lock();
      commandQueue.add(nextCommand);
      commandQueue.unlock();
    } else {
        sleep(1);
    }
  }
  return 0;
}

这个函数可能在 main() 中,但我正在使用现有的代码库,而不是反对它。

对于我的系统,在发送换行符之前没有可用的输入,这正是我想要的。如果您想在键入时读取每个字符,则需要关闭标准输入上的“规范模式”。 cc.byexamples.com 有一些我没有尝试过的建议,但其余的都有效,所以它应该有效。

【讨论】:

  • 您的链接好像倒转了。 cc.byexamples... 链接到 Pete Kirkham 的个人资料,反之亦然。
  • 很好的示例源代码,可以满足我的需要。
【解决方案3】:

你没有,真的。 TTY(控制台)是一个非常有限的设备,你几乎不做非阻塞 I/O。当你看到一些看起来像非阻塞 I/O 的东西时,比如在 curses/ncurses 应用程序中,你所做的称为 raw I/O。在原始 I/O 中,没有字符解释,没有擦除处理等。相反,您需要编写自己的代码,在执行其他操作时检查数据。

在现代 C 程序中,您可以通过将控制台 I/O 放入 线程 或轻量级进程来简化此操作。然后 I/O 可以以通常的阻塞方式继续进行,但数据可以插入到队列中以在另一个线程上处理。

更新

这里有一个curses tutorial,涵盖了更多内容。

【讨论】:

  • 当然你可以在 tty 上做非阻塞 IO。这就是大多数单线程文本 ui 程序的工作方式。
  • 答案实际上是错误的。即使 fd 0 附加到 tty,您也可以 fcntl(0, F_SETFL, fcntl(0, GETFL) | O_NONBLOCK) - 通常不建议这样做,因为它会混淆使用 stdin 的其他人,但它可以工作。
  • 我在评论之前读了三遍,一直读着 s/do/ca/。虽然我不完全同意这个答案,但它是一致的......
  • @eph,发生这种情况时不要讨厌它吗? NP。正如你所说,你可以使它成为非阻塞的。不过,鉴于这个问题,我很确定提问者要求的是原始 I/O。
  • @ephemient 我觉得应该是 F_GETFL
【解决方案4】:

本月早些时候,当我认为我可能需要非阻塞、非缓冲的控制台输入时,我将“Non-blocking user input in loop without ncurses”添加为书签,但我没有,因此无法保证它是否有效。对于我的使用,我并不关心它在用户按回车之前没有得到输入,所以只使用aio 来读取标准输入。

【讨论】:

    【解决方案5】:

    【讨论】:

      【解决方案6】:

      使用 ncurses 或线程的另一个替代方法是使用GNU Readline,特别是它允许您使用register callback functions 的部分。那么模式是:

      1. 在 STDIN 上使用 select()(在任何其他描述符中)
      2. 当 select() 告诉您 STDIN 已准备好读取时,调用 readline 的 rl_callback_read_char()
      3. 如果用户输入了完整的行,rl_callback_read_char 将调用您的回调。否则它将立即返回,您的其他代码可以继续。

      【讨论】:

        【解决方案7】:

        不完全确定您所说的“控制台 IO”是什么意思——您是从 STDIN 读取的,还是从其他来源读取的控制台应用程序?

        如果您从 STDIN 读取数据,则需要跳过 fread() 并使用 read() 和 write() 以及 poll() 或 select() 来防止调用阻塞。您可以使用 setbuf() 禁用输入缓冲,这会导致 fread 返回 EOF,但我从未尝试过。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-10-14
          • 2020-05-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多