Какой сигнал я должен обрабатывать и как?

Где в моем коде я должен «ждать, пока дети закончат»? У меня есть программа C, похожая на пользовательскую оболочку. Теперь у меня есть встроенная функция checkEnv, которая может печатать отсортированные переменные среды. Итак, я могу запустить свою оболочку и перечислить переменные среды:

$ ./a.out 
miniShell>> checkEnv
"'><;|&(:
_=./a.out
CLUTTER_IM_MODULE=xim
COMPIZ_CONFIG_PROFILE=ubuntu
COMP_WORDBREAKS=        
DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-mh5oMhyCI6
DEFAULTS_PATH=/usr/share/gconf/ubuntu.default.path
DESKTOP_SESSION=ubuntu

Код, который делает это, следующий:

 if(StartsWith(line, "checkEnv")) {
     built_in_command=1;
     pagerValue = getenv ("PAGER");
     if (! pagerValue) {
       if (ret == 0) {
         pager_cmd[0]="less";
       } else {
         pager_cmd[0]="more";
       }
     }
    else {
     pager_cmd[0]=pagerValue;
    }


    if(i==1) {
       cmd[0].argv= printenv;
       cmd[1].argv= sort;
       cmd[2].argv= pager_cmd;
       fork_pipes(3, cmd);


    }
    else {

     for (k = 1; k < i; k++)
    {
         len += strlen(argv2[k]) + 2;
    }
    tmp = (char *) malloc(len);
    tmp[0] = '\0';
    for (k = 1; k < i; k++)
    {
       pos += sprintf(tmp + pos, "%s%s", (k == 1 ? "" : "|"), argv2[k]);
    }
    grep[0]="grep";
    grep[1]="-E";
    grep[2]= tmp;
    grep[3]= NULL;
    cmd2[0].argv= printenv;
    cmd2[1].argv= grep;
    cmd2[2].argv= sort;
    cmd2[3].argv= pager_cmd;
    fork_pipes(4, cmd2);
    free(tmp);

            }

Теперь я хочу поймать выходной сигнал завершения при перечислении переменных среды с помощью пейджера, чтобы программа возвращалась в пользовательскую оболочку, а не завершала всю программу. Итак, я полагаю, что мне следует использовать некоторую обработку сигналов, но как и какой сигнал?

Фактическая вилка из этой функции.

/* Helper function that forks pipes */
void fork_pipes(int n, struct command *cmd) {
    int i;
    int in = 0;
    int fd[2];
    /** loop and fork() */
    for (i = 0; i < n - 1; ++i) {

        if (pipe(fd) == -1) {
            err_syserr("Failed creating pipe");
        }

        spawn_proc(in, fd[1], cmd + i);
        close(fd[1]);
        in = fd[0];
    }
    if (dup2(in, 0) < 0) {
        err_syserr("dup2() failed on stdin for %s: ", cmd[i].argv[0]);
    }
    fprintf(stderr, "%d: executing %s\n", (int) getpid(), cmd[i].argv[0]);
    execvp(cmd[i].argv[0], cmd[i].argv);
    err_syserr("failed to execute %s: ", cmd[i].argv[0]);
}

Куда должна идти обработка сигнала? Где минимальный рабочий пример того, что я пытаюсь сделать? Я не вижу примера, который делает это, и я думаю, что документация ужасна, только фрагменты и нет полного примера.

Моя вспомогательная функция

/* Helper function that spawns processes */
int spawn_proc(int in, int out, struct command *cmd) {
    pid_t pid;
    pid = fork();
    if (pid == 0) {
        if (in != 0) {
            if (dup2(in, 0) < 0)
                err_syserr("dup2() failed on stdin for %s: ", cmd->argv[0]);
            close(in);
        }
        if (out != 1) {
            if (dup2(out, 1) < 0)
                err_syserr("dup2() failed on stdout for %s: ", cmd->argv[0]);
            close(out);
        }
        printf("** we are executing parent ***");
        fprintf(stderr, "%d: executing %s\n", (int) getpid(), cmd->argv[0]);
        execvp(cmd->argv[0], cmd->argv);
        err_syserr("failed to execute %s: ", cmd->argv[0]);
    }
    else if (pid < 0) {
        err_syserr("fork failed: ");
    } else {
        /* */
         printf("** we are the parent ***");
    }
    return pid;
}

Мой main() теперь выглядит так:

int main(int argc, char *argv[]) {

    sourceCount = 0;
    const char *commandFile;

    commandFile = NULL;
    char *pathValue;
/*    struct sigaction sa, osa;
    struct sigaction sa2;*/
    int errflag;
    int cOption;
    struct sigaction action;
    /* use getopt_long()  */
    char *argv1[] = {"version", "par2", 0};
/*    char *argv2[] = {"help", "-m", "arg1", 0};*/

    /* use sigaction */

    sigemptyset(&action.sa_mask);
    action.sa_handler = handle_sigchld;
    action.sa_flags = 0;

    sigaction(SIGPIPE, &action, NULL);   //Not work with kill -13 process_id
    //works well
    sigaction(SIGINT, &action, NULL);    //work with kill -2 process_id

    errflag = 0;

    /* use getopt_long()  */
    while ((cOption = getopt(2, argv1, "m:t:n:fs?")) != -1) {

        switch (cOption) {
            case 'a':
                printf("apples\n");
                break;
            case 'b':
                printf("bananas\n");
                break;
            case 't':
                printf("tree = %s\n", optarg);
                break;
            case '?':
                ++errflag;
                break;
        }
    }
/*
    while (( cOption = getopt (3, argv2, "m:t:n:fs?")) != -1) {
        switch (cOption) {
            case 'm':
                printf("\n Help msg : %s \n", optarg);
                exit(0);
            case '?':
                printf("\n -? Arg : %s \n", optarg);
                break;
            case 'n':
                printf("\n -n Arg : %s \n", optarg);
                break;
        }
    }
*/


    /* get the PATH environment to find if less is installed */
    pathValue = getenv("PATH");
    if (!pathValue || getenv("PATH") == NULL) {
        printf("'%s' is not set.\n", "PATH");

        /* Default our path if it is not set. */

        putenv("PATH=/bin:/usr/bin:/sbin:/usr/sbin:/etc");
    }
    else {
        printf("'%s' is set to %s.\n", "PATH", pathValue);
    }
    exec_program(commandFile);
    return (0);
}

Если я запускаю свою оболочку в gdb, я получаю нормальный выход.

(gdb) run
Starting program: /home/dac/ClionProjects/shell2/openshell/shell 
'PATH' is set to /home/dac/proj/google-cloud-sdk/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin.
dac:/home/dac/ClionProjects/shell2/openshell $ checkenv
7429: executing printenv
7430: executing grep
7417: executing less
7431: executing sort
process 7417 is executing new program: /bin/less
[Inferior 1 (process 7417) exited normally]
(gdb) 

person Niklas R.    schedule 13.04.2016    source источник
comment
Какой сигнал вызывает завершение вашей программы? СИГПАЙП? Ваша настоящая оболочка должна сказать вам об этом.   -  person Mark Plotnick    schedule 13.04.2016


Ответы (2)


В родительском элементе вам не нужно получать какой-либо сигнал, просто вызывайте wait(), пока он не вернет, что ваш ребенок умер и теперь пожинает плоды. Теперь ваша оболочка готова к выполнению новой задачи.

ваш код в исходном запросе, если он очень неполный, поэтому вот один пример:

#include <stdio.h>
#include <unistd.h>
#include <wait.h>

void foobar()
{
  int pipe_fd[2];
  pid_t pid1, pid2;

  pipe(pipe_fd);

  pid1=fork();
  if (pid1 == 0)
  {
    /* child1 - let us pretend that we wanted to replace stdin and this child */
    close (0);
    dup(pipe_fd[0]);
    close(pipe_fd[0]);
    close(pipe_fd[1]);
    execlp("wc", "wc", NULL);
    perror ("execlp(wc)");
    _exit(0);
  }

  pid2=fork();
  if (pid2 == 0)
  {
    /* child - let us pretent that we wanted to replace stdout */
    close (1);
    dup(pipe_fd[1]);
    close(pipe_fd[0]);
    close(pipe_fd[1]);
    execlp("ls", "ls", "-l", NULL);
    perror ("execlp(ls)");
    _exit(0);
  }

  close(pipe_fd[0]);
  close(pipe_fd[1]);

  /* wait until children are finished */
  while ((pid1 >= 0) || (pid2 >= 0))
  {
    pid_t pid;
    int status;
    pid = wait(&status);
    if (pid < 0)
    {
      continue;
    }
    if (pid == pid1)
    {
      pid1 = -1;
    }
    if (pid == pid2)
    {
      pid2 = -1;
    }
  }
}

int main(int argc, char *argv[])
{
        foobar();
        return 0;
}
person Stian Skjelstad    schedule 22.04.2016
comment
Спасибо. Мне просто нужно было fork и ждать теперь, когда я знаю, что делаю. - person Niklas R.; 22.04.2016

Вы хотите поймать SIGCHLD и получить дочерний статус с помощью одной из функций wait().

Следующая информация взята из онлайн-учебника.

void handle_sigchld(int sig) {
    int saved_errno = errno;
    while (waitpid((pid_t)(-1), 0, WNOHANG) > 0) {}
    errno = saved_errno;
}

struct sigaction sa;
sa.sa_handler = &handle_sigchld;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
if (sigaction(SIGCHLD, &sa, 0) == -1) {
    perror(0);
    exit(1);
}

Кроме того, вы, вероятно, захотите игнорировать SIGPIPE.

struct sigaction sa;
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGPIPE, &sa, 0) == -1) {
  perror(0);
  exit(1);
}
person jxh    schedule 13.04.2016
comment
Спасибо за ответ. Пожалуйста, можно немного поподробнее? Я не делал этого раньше, и я не эксперт C. - person Niklas R.; 13.04.2016
comment
@ Programmer400 Есть ссылка на учебник. Я не думаю, что есть смысл копировать что-то еще здесь... - person Eugene Sh.; 13.04.2016
comment
@ЕвгенийШ. Я не понимаю, где в коде я должен wait(), поэтому я награждаю за детали. - person Niklas R.; 22.04.2016
comment
Обработчик сигнала handle_sigchld вызывает waitpid, одну из функций wait. - person jxh; 22.04.2016
comment
Куда должен идти код обработки сигнала? У меня есть обработчик сигналов, который работает, чтобы программа работала на ctrl+c, но невозможно поймать программу less от выхода из всей оболочки. Документация действительно ужасна - я не вижу примера того, что я пытаюсь сделать. - person Niklas R.; 22.04.2016
comment
Обработчики сигналов обычно устанавливаются в верхней части main. Возможно, вам придется обрабатывать более одного сигнала. Ctrl-C генерирует SIGINT. Сигналы могут прервать ваш системный вызов, а errno установлено на EINTR. Если это произойдет, повторите вызов еще раз. Это вся информация, которую можно найти в учебнике по программированию UNIX, который, возможно, вам следует найти и просмотреть для получения дополнительной информации. - person jxh; 22.04.2016
comment
Должны ли две переменные с именем sa быть одинаковыми или разными для двух разных обработчиков сигналов? Я разместил свой код из main, который работает для sigint, но не для поддержания работы оболочки после выполнения встроенной команды. - person Niklas R.; 22.04.2016
comment
@jxh Хорошо, поэтому я работаю над main, пытаясь включить SIGPIPE, потому что я помню, что это работало до того, как я провел большой рефакторинг. Я запускаю встроенную команду, которая запускает конвейер команд из /bin, и я не могу поддерживать работу оболочки, когда завершается последняя программа ("less") в конвейере. - person Niklas R.; 22.04.2016
comment
Вы, кажется, неправильно поняли мой ответ. Я предоставил технику для пожинания порожденных детей. Я дал совет по работе с записью на закрытое соединение. Вы не задали вопрос почему моя программа не работает. Если вы намеревались, рассмотрите возможность публикации нового вопроса. - person jxh; 22.04.2016
comment
@jxh Все, что я хотел, это поддерживать работу оболочки. Я снова расскажу, как этого добиться в новом вопросе. Спасибо вам за помощь. - person Niklas R.; 22.04.2016