在 C / UNIX 中使用 SIGINT 信号时遇到问题
Having problems working with SIGINT signal in C / UNIX
我有这个 C 程序,它为 UNIX 运行基本的 shell,我想做一些具体的事情。本质上,每当我按下 CTRL+C 时,我都希望终止所有 child 进程,但保持 parent 进程处于活动状态,以便循环返回下一行输入。下面的代码似乎可以像 date、whoami 等基本命令一样正常工作。但是,当我发出以下命令进行测试时,shell 不会循环返回以获取更多输入,之后输入的任何文本都不会执行任何操作。
$sleep 100
$CTRL+C
为了停止 shell 我必须使用 CTRL+Z 强制程序停止。我需要 SIGINT 信号来中断 child 的睡眠命令并强制 parent 到 return 以在终止 child 进程后提示用户,但显然我已经完成了有事吗。这是解释我的意思的输出。
$ ./a3shell2
--------myShell > date
Input command is: date
Sat Nov 9 15:38:08 CST 2019
--------myShell > whoami
Input command is: whoami
klm46
--------myShell > ^C
Caught SIGINT!
killing children
date
Input command is: date
Sat Nov 9 15:38:20 CST 2019
--------myShell > sleep 100
Input command is: sleep 100
^C
Caught SIGINT!
killing children
date
date
whoami
^Z
[2]+ Stopped ./a3shell2
Shell 程序:
/* ----------------------------------------------------------------- */
/* PROGRAM simple shell.c progam */
/* ----------------------------------------------------------------- */
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <assert.h>
#include <sched.h>
pid_t pid = 0;
void sig_int(int signo)
{
printf("\nCaught SIGINT!\n");
printf("killing children\n");
if (pid != 0)
{
kill(pid, SIGKILL);
pid = 0;
}
}
char *getinput(char *buffer, size_t buflen)
{
printf("--------myShell > ");
return fgets(buffer, buflen, stdin);
}
/* ----------------------------------------------------------------- */
/* FUNCTION parse: */
/* ----------------------------------------------------------------- */
void parse(char *line, char **argv)
{
while (*line != '[=12=]') { /* if not the end of line ....... */
while (*line == ' ' || *line == '\t' || *line == '\n')
*line++ = '[=12=]'; /* replace white spaces with 0 */
*argv++ = line; /* save the argument position */
while (*line != '[=12=]' && *line != ' ' &&
*line != '\t' && *line != '\n')
line++; /* skip the argument until ... */
}
*argv = '[=12=]'; /* mark the end of argument list */
}
/* ----------------------------------------------------------------- */
/* FUNCTION execute: */
/* ----------------------------------------------------------------- */
void execute(char **argv)
{
//pid_t pid;
int status;
if ((pid = fork()) < 0) { /* fork a child process */
printf("*** ERROR: forking child process failed\n");
exit(1);
}
else if (pid == 0) { /* for the child process: */
if (execvp(*argv, argv) < 0) { /* execute the command */
printf("*** ERROR: exec failed\n");
exit(1);
}
}
else { /* for the parent: */
while (wait(&status) != pid) /* wait for completion */
;
}
}
/* ----------------------------------------------------------------- */
/* The main program starts here */
/* ----------------------------------------------------------------- */
void main(void)
{
char line[1024]; /* the input line */
char *argv[64]; /* the command line argument */
size_t linelen;
if (signal(SIGINT, sig_int) == SIG_ERR)
{
fprintf(stderr, "signal error: %s\n", strerror(errno));
exit(1);
}
while (1)
{ /* repeat until done .... */
getinput(line, sizeof(line));
line[strlen(line) - 1] = '[=12=]';
printf(" Input command is: %s \n", line);
parse(line, argv); /* parse the line */
if (strcmp(argv[0], "exit") == 0) /* is it an "exit"? */
exit(0); /* exit if it is */
execute(argv); /* otherwise, execute the command */
}
}
当您 CTRL+C.
SIGINT 中断 wait()
,您的信号处理程序将全局 pid
变量设置为零。当控制returns到循环时,它永远不会结束:pid
为零,而wait()
can only return PIDs and -1,永远不会为零。
(您在 date
和 whoami
中看不到这种行为,因为这些 child 进程在您发出 CTRL[=39 时已经终止=]+C。在这种情况下,您可能正在中断 fgets()
,而不是 wait()
,并向已收割的 PID 发送 SIGKILL。这也不好,因为当您发送 SIGKILL 时,PID 可能会是 re-used。)
顺便说一句,目前尚不清楚当 SIGINT 中断您的 wait()
调用时实际发生了什么,因为 signal()
在不同平台上的行为历来各不相同。调用可能会被中断,返回 -1 (EINTR),或者它可能会恢复 wait()
,返回 kill()
ed child 的 PID。在这种情况下没有实际意义——它们都不匹配你在那个时候要找的零——但这是 use sigaction()
and not signal()
.
的一个很好的理由
我有这个 C 程序,它为 UNIX 运行基本的 shell,我想做一些具体的事情。本质上,每当我按下 CTRL+C 时,我都希望终止所有 child 进程,但保持 parent 进程处于活动状态,以便循环返回下一行输入。下面的代码似乎可以像 date、whoami 等基本命令一样正常工作。但是,当我发出以下命令进行测试时,shell 不会循环返回以获取更多输入,之后输入的任何文本都不会执行任何操作。
$sleep 100
$CTRL+C
为了停止 shell 我必须使用 CTRL+Z 强制程序停止。我需要 SIGINT 信号来中断 child 的睡眠命令并强制 parent 到 return 以在终止 child 进程后提示用户,但显然我已经完成了有事吗。这是解释我的意思的输出。
$ ./a3shell2
--------myShell > date
Input command is: date
Sat Nov 9 15:38:08 CST 2019
--------myShell > whoami
Input command is: whoami
klm46
--------myShell > ^C
Caught SIGINT!
killing children
date
Input command is: date
Sat Nov 9 15:38:20 CST 2019
--------myShell > sleep 100
Input command is: sleep 100
^C
Caught SIGINT!
killing children
date
date
whoami
^Z
[2]+ Stopped ./a3shell2
Shell 程序:
/* ----------------------------------------------------------------- */
/* PROGRAM simple shell.c progam */
/* ----------------------------------------------------------------- */
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <assert.h>
#include <sched.h>
pid_t pid = 0;
void sig_int(int signo)
{
printf("\nCaught SIGINT!\n");
printf("killing children\n");
if (pid != 0)
{
kill(pid, SIGKILL);
pid = 0;
}
}
char *getinput(char *buffer, size_t buflen)
{
printf("--------myShell > ");
return fgets(buffer, buflen, stdin);
}
/* ----------------------------------------------------------------- */
/* FUNCTION parse: */
/* ----------------------------------------------------------------- */
void parse(char *line, char **argv)
{
while (*line != '[=12=]') { /* if not the end of line ....... */
while (*line == ' ' || *line == '\t' || *line == '\n')
*line++ = '[=12=]'; /* replace white spaces with 0 */
*argv++ = line; /* save the argument position */
while (*line != '[=12=]' && *line != ' ' &&
*line != '\t' && *line != '\n')
line++; /* skip the argument until ... */
}
*argv = '[=12=]'; /* mark the end of argument list */
}
/* ----------------------------------------------------------------- */
/* FUNCTION execute: */
/* ----------------------------------------------------------------- */
void execute(char **argv)
{
//pid_t pid;
int status;
if ((pid = fork()) < 0) { /* fork a child process */
printf("*** ERROR: forking child process failed\n");
exit(1);
}
else if (pid == 0) { /* for the child process: */
if (execvp(*argv, argv) < 0) { /* execute the command */
printf("*** ERROR: exec failed\n");
exit(1);
}
}
else { /* for the parent: */
while (wait(&status) != pid) /* wait for completion */
;
}
}
/* ----------------------------------------------------------------- */
/* The main program starts here */
/* ----------------------------------------------------------------- */
void main(void)
{
char line[1024]; /* the input line */
char *argv[64]; /* the command line argument */
size_t linelen;
if (signal(SIGINT, sig_int) == SIG_ERR)
{
fprintf(stderr, "signal error: %s\n", strerror(errno));
exit(1);
}
while (1)
{ /* repeat until done .... */
getinput(line, sizeof(line));
line[strlen(line) - 1] = '[=12=]';
printf(" Input command is: %s \n", line);
parse(line, argv); /* parse the line */
if (strcmp(argv[0], "exit") == 0) /* is it an "exit"? */
exit(0); /* exit if it is */
execute(argv); /* otherwise, execute the command */
}
}
当您 CTRL+C.
SIGINT 中断 wait()
,您的信号处理程序将全局 pid
变量设置为零。当控制returns到循环时,它永远不会结束:pid
为零,而wait()
can only return PIDs and -1,永远不会为零。
(您在 date
和 whoami
中看不到这种行为,因为这些 child 进程在您发出 CTRL[=39 时已经终止=]+C。在这种情况下,您可能正在中断 fgets()
,而不是 wait()
,并向已收割的 PID 发送 SIGKILL。这也不好,因为当您发送 SIGKILL 时,PID 可能会是 re-used。)
顺便说一句,目前尚不清楚当 SIGINT 中断您的 wait()
调用时实际发生了什么,因为 signal()
在不同平台上的行为历来各不相同。调用可能会被中断,返回 -1 (EINTR),或者它可能会恢复 wait()
,返回 kill()
ed child 的 PID。在这种情况下没有实际意义——它们都不匹配你在那个时候要找的零——但这是 use sigaction()
and not signal()
.