如何在设计 shell 时处理 Control-C 信号?
How to handle Control-C signal while designing a shell?
我正在尝试实现 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 键才能执行程序。
我回答了 this 个问题,但它似乎不适用于我的情况。这是我第一次使用信号和系统调用,所以我的问题可能很愚蠢。但是,如果有人可以帮助我找到正确的信号实现方式,那将是一个很大的帮助。谢谢。
Jonathan Leffler 提供了有用的提示(尽管还不够,至少在一些流行的操作系统上):
- 看看什么
scanf()
returns.
- 不要在信号处理程序中使用
loop()
- 不要使用
signal()
— 使用 sigaction()
。在您的情况下,这有两个优点:
- 可以避免在调用信号处理程序时将信号操作恢复为默认状态,因此您不必在处理程序中再次更改操作。
- 可以避免重新启动
read()
系统调用(在scanf()
内),这样scanf()
returns和你可以第一时间响应中断。
所以,信号处理程序可以只是
void sigintHandler(int sig_num)
{
printf("\n"); // or perhaps better write(1, "\n", 1)
}
main()
中的signal(SIGINT, sigintHandler);
可以替换为
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
区分情况。此外,如果您的程序提供退出它的方法,那就太好了,因此 return
.
我正在尝试实现 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 键才能执行程序。
我回答了 this 个问题,但它似乎不适用于我的情况。这是我第一次使用信号和系统调用,所以我的问题可能很愚蠢。但是,如果有人可以帮助我找到正确的信号实现方式,那将是一个很大的帮助。谢谢。
Jonathan Leffler 提供了有用的提示(尽管还不够,至少在一些流行的操作系统上):
- 看看什么
scanf()
returns. - 不要在信号处理程序中使用
loop()
- 不要使用
signal()
— 使用sigaction()
。在您的情况下,这有两个优点:- 可以避免在调用信号处理程序时将信号操作恢复为默认状态,因此您不必在处理程序中再次更改操作。
- 可以避免重新启动
read()
系统调用(在scanf()
内),这样scanf()
returns和你可以第一时间响应中断。
所以,信号处理程序可以只是
void sigintHandler(int sig_num)
{
printf("\n"); // or perhaps better write(1, "\n", 1)
}
main()
中的signal(SIGINT, sigintHandler);
可以替换为
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
区分情况。此外,如果您的程序提供退出它的方法,那就太好了,因此 return
.