简体   繁体   English

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

[英]Porting Windows-Centric Console I/O To Linux

I am developing a Windows application that has a separate thread for processing user (or 3rd party) application input via stdin. 我正在开发一个Windows应用程序,它有一个单独的线程,用于通过stdin处理用户(或第三方)应用程序输入。

This thread is designed such that it waits via WaitForMultipleObjects on two events: 该线程的设计使它在两个事件上通过WaitForMultipleObjects等待:

  1. A death signal . 死亡信号 When this signal is raised, the interface-processing thread shuts down. 当该信号被引发时,接口处理线程关闭。
  2. An interface signal . 接口信号 When this signal is raised, there is input ready to be read. 当该信号被提升时,输入准备好被读取。 The input is read and processed. 读取并处理输入。

Under Windows this thread enters a main loop where it Wait s for these 2 events (where bWaitAll is FALSE ). 在Windows下,这个线程进入一个主循环,在那里Wait这两个事件(其中bWaitAllFALSE )。 Waiting on the stdin handle has the effect of signaling when there is input ready to be read, and the other event is set from elsewhere in the application. 当有输入准备好被读取时,等待stdin句柄具有信令的效果,而另一个事件是从应用程序的其他地方设置的。

This works exactly as I want. 这完全符合我的要求。 It waits for an event to be raised without entering in to a busy-wait, and it waits for both event simutaneously. 它等待一个事件在没有进入繁忙等待的情况下被提升,并且它同时等待这两个事件。

I wish to port this functionality to Linux, but I'm not sure how to achieve the desired result. 我希望将此功能移植到Linux,但我不确定如何实现所需的结果。 Fundamentally, what I really want is this: 从根本上说,我真正想要的是:

Under Linux, how do I design a thread so that it will respond immediately to user-input on stdin, yet it can also respond immediately to a kill-flag being raised from elsewhere in the application? 在Linux下,我如何设计一个线程,以便它立即响应stdin上的用户输入,但它也可以立即响应从应用程序中其他地方引发的kill-flag?

In order to accomplish the latter, it seems to me that I cannot use cin , gets , getch or any other function that blocks until the user has entered text. 为了完成后者,在我看来,在用户输入文本之前,我不能使用cingetsgetch或任何其他阻塞功能。 Yet I do not know how to read user input in a console-based application without blocking. 但我不知道如何在没有阻塞的情况下在基于控制台的应用程序中读取用户输入。

I'm open to any change in architecture (if there's a more Linux-y way to do this) that include having user input processed in a separate thread that can be terminated from elsewhere in the application. 我对架构的任何变化持开放态度(如果有更多的Linux-y方式),包括在一个单独的线程中处理用户输入,该线程可以从应用程序的其他地方终止。 I'm using GCC 4.4, and Boost 1.51. 我正在使用GCC 4.4和Boost 1.51。

The standard way of doing this in Linux is to use the select(2) system call. 在Linux中执行此操作的标准方法是使用select(2)系统调用。 However, select is more limited than WaitForMultipleObjects , in that it can only wait on file descriptors, not other kinds of objects (such as events). 但是, selectWaitForMultipleObjects更受限制,因为它只能等待文件描述符,而不能等待其他类型的对象(例如事件)。 So, the typical way of working around that is to create a pipe and write a dummy value to the pipe as your "signal". 因此,解决这个问题的典型方法是创建一个管道并将一个虚拟值作为“信号”写入管道。

Something like this: 像这样的东西:

// 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)
    }
}

Then to signal to the thread that it's done, just write a single byte to the write end of the pipe: 然后向线程发信号通知它已完成,只需将一个字节写入管道的写入端:

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

libev (and several similar incarnations) offers a convenient abstraction around select including being able to pend on signal s. libev (和几个类似的化身)提供了一个方便的抽象select包括能够挂在signal s上。

If you have the option to alter the origin of " An interface signal " then you could consider changing it to use raise instead. 如果您可以选择更改“ 接口信号 ”的来源,则可以考虑将其更改为使用raise

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM