在设计外壳时如何处理Control-C信号?
我正在尝试实现shell中的一些功能,包括仅当用户输入quit而不是Ctrl+C时才退出。下面是我尝试的代码的简化版本 代码1:不在信号处理程序中调用loop()在设计外壳时如何处理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) {
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
。我一定会记住你的建议。非常感谢。