在重定向输入期间使用键盘停止unix程序,在C中没有SIGINT

时间:2016-05-24 19:06:11

标签: c unix

到目前为止,我还没有找到解决类似情况的帖子:
在用于某些浮点数据集超声处理的命令行unix程序中,我有一个main(),它可以读取来自名称为program argumant或stdin重定向( < )的文件或来自其他程序( | )的数据,具体取决于argc。 只要argv[1]是文件名,我就可以通过点击&#39;输入&#39;来轻轻地停止执行。 key,以编程方式设置淡出信号的条件,清理并定期退出音频子例程并终止程序。我通过调用非阻塞kbhit()的函数来实现它。定义:

int kbhit(void)
{    
    struct timeval tv;
    fd_set read_fd;

    tv.tv_sec = 0;
    tv.tv_usec= 0;

    FD_ZERO(&read_fd);    
    FD_SET(0, &read_fd);

    if(select(1, &read_fd, NULL, NULL, &tv) == -1)
        return 0;

    if(FD_ISSET(0, &read_fd))
        return 1;

    return 0;    
}

但是,如果正在重定向或管道输入,则kbhit()会阻止执行,因为它会在启动时停止播放,因为它会返回1。我不知道如何处理它,所以我使用的是SIGINT,我觉得它不那么优雅。以下是代码段:

//...   ... 
bool stop = false;        //global variable
//...   ...  
if(argc == 1) stdr = true;//flag: reading from redirected or piped stdin (simplified)

//code for reading from file or stdin, using fscanf() ...
//code for initializing and starting audio process ...
//loop, during audio callback on a separate thread ...
while((player.totalFrames < player.totalSize) && !stop){
    if(!stdr){      //( = reading from file)
      if (kbhit()){
        stop = true;
        break;
      }
   }else{           //( = reading from redirected stdin)
      if (signal(SIGINT, sig_handler) == SIG_ERR)
        printf("\ncan't catch SIGINT\n");
   }        
}

if(stop){ … }//code for gently fading the signal and stopping the audio process
//cleanup and termination...
//...   ...

这是标准信号处理程序,它将stop条件设置为true

void sig_handler(int s)
{
    if (s == SIGINT){
      if(debug)printf("\ncaught SIGINT\n");
      stop = true;
    }
}

是否有更优雅的方式:

  1. 发送&amp;在输入重定向或管道输入时从键盘捕获消息,以允许程序自行清除并退出(除了如上所述使用SIGINT)?
  2. 如何从代理键盘(在重定向或管道中)允许这样的消息的代码如何? kbhit()内的论据是否可能以某种方式重新排列,或者必须朝着完全不同的方向发展。我不知道哪个?

    如果可以的话,我会很感激的建议。提前致谢!

2 个答案:

答案 0 :(得分:1)

如果您希望交互式TTY用户能够使用输入字符(不是导致TTY驱动程序发送信号的信令字符)来停止程序,并且该程序必须重定向,那么您唯一的选择就是显式打开文件描述符到TTY设备(通过/dev/tty)并监视 输入。

您的kbhit函数可能应该使用int fd参数,使用该参数为其提供应该轮​​询的打开TTY文件描述符。

如果您希望TTY的单个字符输入立即可用于程序(即使该字符不是换行符),则必须将TTY置于“原始模式”,或者至少部分:至少,您必须通过否定ICANON结构中c_iflag数字字段中的struct termios标记来禁用“规范输入处理”,并确保c_cc[VTIME]c_cc[VMIN]分别设置为0和1。查看tcgetattrtcsetattr。您可能希望禁用信令字符,并禁用回显和其他内容。某些平台具有cfmakeraw函数,可以以正确的方式调整struct termios对象,从而实现非常原始的模式,而不必使用任何termios标记。

在规范输入处理模式下,TTY不会使进程可用输入(并且在输入的select下不会选择正数),直到收到完整的输入行为止。

答案 1 :(得分:1)

您的问题是,从重定向读取数据时,您尝试同时使用文件和键盘。 stdin不再是你的键盘,它是文件,因此你无法从键盘读取数据(如按下“enter”键)。

如果我没弄错的话,你只有两个选项是信号,并为键盘输入(TTY)打开一个新的文件描述符。