如何保持我的C程序从CTRL-C被按下时终止?

AFAIK这与“捕捉SIGINT”有关,但我正在寻找更多的细节。 像许多其他人一样,我通过编写自己的CLI来学习C,可以通过环境variables来启动其他程序。 我的shell可以在前台和后台启动其他进程,但是如果按CTRL-C来终止前台进程,如何保持后台进程运行和我的shell运行? 我的部分代码是:

int main() { /*... builtin commands and i/o ...*/ isBackground = 0; for (b = 0; b<max; b++) { if ('&'==line[b]) { isBackground = 1; } } if (isBackground == 1) { /*If backgroundprocess*/ printf("Background process\n"); take_return=pipe(fd); /*(two new file descriptors)*/ pid_temp = fork(); } else if (isBackground == 0) { /*If foreground process*/ printf("Foreground process\n"); if (1 == isSignal) { /*If using signaldetection*/ printf("Signal foreground\n"); sigemptyset(&my_sig); /*empty and initialising a signal set*/ sigaddset(&my_sig, SIGCHLD); /*Adds signal to a signal set (my_sig)*/ /*http://pubs.opengroup.org/onlinepubs/7908799/xsh/sigprocmask.html*/ sigprocmask(SIG_BLOCK, &my_sig, NULL); } pid_temp = fork(); foreground = pid_temp; /*Set pid for foreground process*/ } else if (0>pid_temp) { /*Error*/ } else { /*Child process*/ if (1 == isBackground) { /*Backgroundprocess*/ dup2(fd[STDIN_FILENO], STDIN_FILENO); close(fd[0]); close(fd[1]); } /*http://www.lehman.cuny.edu/cgi-bin/man-cgi?execvp+2*/ if (execvp(argv2[0],argv2) < 0) { printf("We are sorry to inform you that something went wrong %d \n", errno); } } if (0 == isBackground) { /*Foregroundprocess*/ waitpid(foreground, &status, 0); /*Waiting*/ printf("Foreground process id %d\n", foreground); /*Foregroundprocess terminated*/ /*FIXME*/ gettimeofday(&time_end, NULL); time = (time_end.tv_sec-time_start.tv_sec)*1000000 + time_end.tv_usec-time_start.tv_usec; printf("Execution time %ld ms\n", time); /*TODO Print out the execution time*/ /* int isSignal = 0;*/ /*FIXME*/ if (1 == isSignal) { /*If using signaldetection*/ int a = sigprocmask(SIG_UNBLOCK, &my_sig, NULL); /*http://man7.org/linux/man-pages/man2/sigprocmask.2.html*/ if (0 == a) { /*Sigprocmask was successfull*/ } else { /*Sigprocmask was not successfull, return=-1*/ } Janitor(SIGCHLD); } /*TODO Print how long time was the total execution time*/ } else if (1==isBackground) { close(fd[0]); close(fd[1]); } } /* pid= fork(); if(pid==0) { execvp(progpath,argv); fprintf(stderr, "Child process could not do execvp\n"); } else { wait(NULL); printf("Child exited\n"); }*/ built_in_command = 0; /*Reset*/ memset(line, 0, sizeof line); /*Reset*/ } return (0); } 

您需要添加一个称为“信号处理程序”的函数。 它有一个非常具体的类型。 那么你需要改变你的代码来“安装信号处理程序”。 用sigaction(2)系统调用可能是最好的。 所以,这样的事情:

 #include <signal.h> void sighandler(int, siginfo_t *, void *); ... void sighandler(int signo, siginfo_t *si, void *vp) { write(2, "Received SIGINT\n", 16); } 

main()内部,在程序执行任何耗时之前:

 struct sigaction sa, osa; sa.sa_sigaction = sighandler; sa.sa_flags = SA_SIGINFO; sigaction(SIGINT, &sa, &osa); 

您可能需要检查sigaction()的返回值是否有错误。 一旦你包含信号处理函数,并获得代码来安装它执行控制-C发生之前,恭喜,你有一个信号处理程序安装。

您的代码存在其他后果。 一些系统调用( read()close()命名为两个)会返回一个错误(-1)并将errno为EINTR。 在插座上读取特别容易发生。 所以,为了避免丢失跟踪打开的文件描述符以及丢失套接字数据的问题,您需要有代码来处理EINTR情况。