2015-05-19 2 views
2

AFAIK это как-то связано с «поймать SIGINT», но я ищу более подробную информацию. Как и многие другие, я изучаю C, создавая свой собственный CLI, который может запускать другую программу, расположенную переменными окружения. Моя оболочка может запускать другие процессы на переднем плане и в фоновом режиме, но как сохранить фоновый процесс, и моя оболочка работает, когда CTRL-C нажата для завершения процесса переднего плана? Часть моего кода:Как сохранить мою программу на C при нажатии CTRL-C?

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); 

} 
+0

Вы уже можете обрабатывать сигналы (вы ловите и действуете на 'SIGCHLD'), поэтому вам просто нужно добавить обработчик для' SIGINT' таким же образом и сделать все, что вы хотите в этом обработчике. – user43791

ответ

5

Вам необходимо добавить функцию, называемую «обработчиком сигнала». Он имеет очень специфический тип. Затем вам нужно изменить свой код на «установить обработчик сигнала». Вероятно, лучше всего это сделать с системным вызовом 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.

+2

Не забудьте * внимательно прочитать [signal (7)] (http://man7.org/linux/man-pages/man7/signal.7.html). Часто обработчик сигнала должен устанавливать только некоторый флаг 'volatile sigatomic_t', который должен быть протестирован в другом месте. –

Смежные вопросы