Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/shell/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在设计外壳时如何处理Control-C信号?_C_Shell_Operating System_Signals - Fatal编程技术网

在设计外壳时如何处理Control-C信号?

在设计外壳时如何处理Control-C信号?,c,shell,operating-system,signals,C,Shell,Operating System,Signals,我正在尝试实现shell中的一些功能,包括仅当用户输入quit而不是Ctrl+C时才退出。下面是我尝试的代码的简化版本 代码1:不在信号处理程序中调用loop() void loop(){ while(1){ char a[20]; printf("Enter Command : " ); scanf("%s",a); printf("%s\n", a); } } void sigintHandler(int sig_num) {

我正在尝试实现shell中的一些功能,包括仅当用户输入quit而不是Ctrl+C时才退出。下面是我尝试的代码的简化版本

代码1:不在信号处理程序中调用loop()

void loop(){
    while(1){
      char a[20];
      printf("Enter Command : " );
      scanf("%s",a);
      printf("%s\n", a);
    }
}

void sigintHandler(int sig_num)
{
    signal(SIGINT, sigintHandler);
    printf("\n");
}

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

  signal(SIGINT, sigintHandler);
  loop();
  return 0;
}
代码1的输出:

正如在第三个输入上所看到的,我转到一个新行,从我离开循环的地方继续向右。我想重新开始循环。因此,我通过调用信号处理程序本身中的循环进行了以下修改

void loop(){
    while(1){
      char a[20];
      printf("Enter Command : " );
      scanf("%s",a);
      printf("%s\n", a);
    }
}

void sigintHandler(int sig_num)
{
    signal(SIGINT, sigintHandler);
    printf("\n");
    loop();
}

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

  signal(SIGINT, sigintHandler);
  loop();
  return 0;
}
代码2的输出:

可以看出,当我第一次单击Ctrl+C(在输入行3上)时,它工作正常,我可以继续。但是,当我第二次单击Ctrl+C时,我不会转到新行,我必须按enter键才能执行程序


我问了这个问题,但它似乎不适用于我的情况。这是我第一次使用信号和系统调用,所以我的问题可能会变得很愚蠢。但如果有人能帮助我找到一种正确的方式来实现信号,那将是一个很大的帮助。谢谢。

Jonathan Leffler提供了一些有用的提示(尽管还不够,至少在一些流行的操作系统上是这样):

  • 查看
    scanf()
    返回的内容
  • 不要在信号处理程序中使用
    loop()
  • 不要使用
    signal()
    -使用
    sigaction()
    。在您的情况下,这有两个优点:
    • 可以避免在调用信号处理程序时将信号操作恢复为默认状态,因此不必在处理程序中再次更改操作
    • 可以避免重新启动
      read()
      系统调用(在
      scanf()
      内部),以便
      scanf()
      返回,您可以首先对中断做出反应
因此,信号处理器可以是

void sigintHandler(int sig_num)
{
    printf("\n");   // or perhaps better write(1, "\n", 1)
}
信号(SIGINT,sigintHandler)main()
中的code>可以替换为

  sigaction(SIGINT, &(struct sigaction){ .sa_handler = sigintHandler }, NULL);
以及
scanf(“%s”,a)带有

      if (scanf("%s", a) == EOF)
      {
          if (errno == EINTR) continue; // read operation interrupted by signal
          return;
      }

EOF在中断时以及在文件结束时返回,因此通过
errno
区分案例。此外,如果您的程序提供了一种退出它的方法,那么
返回值

不使用
signal()
-使用
sigaction()
(请参阅)。不要在信号处理器中使用
printf()
;它可能导致未定义的行为(请参阅)。不要在信号处理程序中使用
loop()
,这是完全不受支持的。@J.Panek:
signal()
的一些变体重置信号处理程序并要求信号处理程序恢复信号处理。查找
SA_RESETHAND
,其中大部分用于模拟使用
sigaction()
实现的仿古
signal()
。标准C中
signal()
的语义最小;POSIX中
signal()
的语义反映了POSIX标准化之前实际实现的差异。POSIX为许多原因推荐
sigaction()
。当信号处理程序中有
loop()
调用时,您可能会发现中断信号仍然被阻塞,因为您没有从原始信号处理程序返回。另外,在信号处理程序中调用
loop()
,意味着递归调用
loop()
(尽管是间接递归调用)。对
loop()
的原始调用也没有完成。您的
循环()
没有错误检查;我希望这是因为您已经创建了一个MCVE()(或MRE或其他现在使用的名称)。您应该注意
scanf()
的返回值。如果被中断,它可能会返回错误条件-如果被中断,您不应该使用
a
中的值。查看
scanf()
返回的内容。如果显示
1
,则可以使用
a
的内容。如果它显示
0
EOF
,则您有更多的工作要做。此后,您可能需要使用
clearr(stdin)
从终端获取任何有意义的信息。然后您可以返回并再次提示。嗯,我缺少的是检查
errno
。好消息是,在我的原始代码中,它适用于
getc
。我一定会记住你的建议。非常感谢。