将以Windows为中心的控制台I / O移植到Linux

时间:2012-11-06 21:45:53

标签: c++ linux c++11

我正在开发一个Windows应用程序,它有一个单独的线程,用于通过stdin处理用户(或第三方)应用程序输入。

此线程的设计使其在两个事件上等待WaitForMultipleObjects

  1. 死亡信号。当引发此信号时,接口处理线程将关闭。
  2. 接口信号。当该信号被提升时,输入准备好被读取。读取并处理输入。
  3. 在Windows下,此线程进入一个主循环,其中Wait为这两个事件(bWaitAllFALSE)。当有输入准备好被读取时,等待stdin句柄会产生信号的影响,而另一个事件是从应用程序的其他地方设置的。

    这完全符合我的要求。它等待一个事件在没有进入繁忙等待的情况下被提升,并且它同时等待这两个事件。

    我希望将此功能移植到Linux,但我不确定如何实现所需的结果。从根本上说,我真正想要的是:

      

    在Linux下,如何设计线程以便它响应   立即用户输入stdin,但它也可以响应   立刻从一个其他地方筹集的杀戮旗帜   应用

    为了完成后者,在我看来,我不能使用cingetsgetch或任何其他阻止用户输入文本的功能。但我不知道如何在没有阻塞的情况下在基于控制台的应用程序中读取用户输入。

    我对架构中的任何变化持开放态度(如果有更多的Linux-y方式),包括在单独的线程中处理用户输入,该线程可以从应用程序的其他地方终止。我正在使用GCC 4.4和Boost 1.51。

2 个答案:

答案 0 :(得分:3)

在Linux中执行此操作的标准方法是使用select(2)系统调用。但是,selectWaitForMultipleObjects更受限制,因为它只能等待文件描述符,而不能等待其他类型的对象(例如事件)。因此,解决这个问题的典型方法是创建一个管道并将一个虚拟值作为“信号”写入管道。

这样的事情:

// Error checking omitted for expository purposes
int pipefd[2];
pipe(pipefd);  // Create the pipe

while(1)
{
    // Create file descriptor set of stdin and the read end of the pipe
    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(STDIN_FILENO, &fds);
    FD_SET(pipefd[0], &fds);
    int maxfd = MAX(STDIN_FILENO, pipefd[0]);

    // Wait until input becomes available on either stdin or the pipe
    int num_available = select(&fds, NULL, NULL, NULL);

    // Read & process stdin if possible (will not block)
    if (FD_ISSET(STDIN_FILENO, &fds))
    {
        int n = read(STDIN_FILENO, buffer, size);
        ...
    }

    // Read & process pipe if possible (will not block)
    if (FD_ISSET(pipefd[0], &fds))
    {
        char dummy;
        read(pipefd[0], &dummy, 1);
        // Handle signal (e.g. break out of loop)
    }
}

然后向线程发出信号表明它已完成,只需将一个字节写入管道的写入端:

char dummy = 42;
write(pipefd[1], &dummy, 1);

答案 1 :(得分:1)

libev(以及几个类似的化身)提供了select周围的方便抽象,包括能够在signal上下载。

如果您可以选择更改“接口信号”的来源,则可以考虑将其更改为使用raise