Параллельная обработка не работает при использовании execv

Итак, я пытаюсь выполнить параллельный процесс и использую execv. Проблема заключается в том, что при параллельном процессе execv выполняется только в одном потомке, а не в обоих. Я знаю, что оба дочерних процесса выполняются из-за заявлений printf о pid.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>

  int main(int argc, char * argv[]) {
    char input[100];
    char * path[15];
    path[0] = "/bin/";
    char str[200];
    FILE * fp;
    if (argc == 2) {
      if ((fp = fopen(argv[1], "r")) == NULL) {
        printf("Error! opening file");
        // Program exits if file pointer returns NULL.
        exit(1);
      }
    }
    do {
      if (argc != 2) {
        printf("dash> ");
        fgets(input, 100, stdin);
      } else if (argc == 2) {
        if (fgets(input, 100, fp) == NULL)
          exit(0);
        printf(input);
      }
      input[strcspn(input, "\n")] = '\0';
      char * token = strtok(input, " ");
      int c = 0;
      if ((strcmp(token, "exit") != 0) && (strcmp(token, "cd") != 0) && (strcmp(token, "path") != 0)) {
        do {
          if (path[c] == NULL) {
            printf("p error");
            break;
          }
          strcpy(str, path[c]);
          strcat(str, token);
          c++;
        } while (access(str, X_OK) == -1);
      }
      if (strcmp(token, "exit") == 0) {
        exit(0);
      } else if (strcmp(token, "cd") == 0) {
        token = strtok(NULL, " ");
        if (chdir(token) == -1)
          printf("errorcd");
      } else if (strcmp(token, "path") == 0) {
        int counter = 0;
        while (path[counter] != NULL) {
          path[counter] = strtok(NULL, " ");
          counter++;
        }
      } else {
        char * myargs[15];
        char * par[15];
        myargs[0] = token;
        int counter = 0;
        int track = 0;
        int and = 1;
        while (myargs[counter] != NULL) // &&(strcmp(myargs[counter],">"))!=0){
        {
          counter++;
          myargs[counter] = strtok(NULL, " ");
        }
        int a = 0;
        int i = 0;
        int j = 0;
        int t = 0;
        // printf(myargs[i]);
        for (i = 0; i <= counter - 1; i++) {
          if (strcmp(myargs[i], "&") == 0)
            and++;
        }
        int temp = and + 1;
        // printf("No. %d",and);
        int rc[15];
        for (i = 0; i < and; i++) {
          a = 0;
          for (j = t; j <= counter - 1; j++) {
            par[a] = myargs[j];
            a++;
            if (strcmp(myargs[j], "&") == 0) {
              t = j + 1;
              break;
            }
          }

          if (and == 0)
            par[j + 1] = NULL;
          else
            par[j] = NULL;
          rc[i] = fork();
          if (rc[i] < 0) // fork failed
          {
            printf("Error");
            return 0;
          } else if (rc[i] == 0) // child process
          {
            printf("\nhello, I am child (pid:%d)\n", (int) getpid());
            if (j > 1) {
              if (strcmp(par[j - 2], ">") == 0) {
                int fd = open(par[j - 1], O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
                dup2(fd, 1); // make stdout go to file
                dup2(fd, 2); // make stderr go to file - you may choose to not do this
                // or perhaps send stderr to another file
                // }
                par[counter - 2] = NULL;
              }
            }
            execv(str, par);
            printf("failed");
          }
        } // parent process
        int status;
        pid_t pid;

        while (temp > 0) {
          // waitpid(pid[i], 0, 0);
          pid = wait( & status);
          printf("Child with PID %ld exited with status 0x%x.\n", (long) pid, status);
          --temp; // TODO(pts): Remove pid from the pids array.
        }
      }
    } while (strcmp(input, "exit") != 0);
    return 0;
  }

Я знаю, что этот код довольно плохой, я давно не занимался программированием и немного заржавел. В любом случае, вот пример ввода и вывода.

dash> pwd & ls

hello, I am child (pid:40037)

hello, I am child (pid:40036)
/home/012/a/ax/axh161330/project1
Child with PID 40036 exited with status 0x0.
Child with PID 40037 exited with status 0x8b.
Child with PID -1 exited with status 0x8b.

Как видите, была выполнена только одна команда execv (pwd), а другая никогда не выполнялась (ls). Первая команда будет работать всегда, а вторая - нет.

Можете ли вы запустить свой код через clang-формат или что-то еще, чтобы его очистить? Очевидно, случайный отступ делает практически невозможным чтение.

Shawn 10.09.2018 07:58

С кодом много проблем; Написание полного толкования по этому вопросу заняло бы очень много времени. Код не MCVE (минимальный воспроизводимый пример). У вас не было бы кода для обработки таких команд, как exit, cd, path в MCVE, и данные всегда поступали бы из файла (или всегда из стандартного ввода). Ваш код для печати пути только случайно обнаруживает конец списка указателей; вы не инициализируете их все нулевыми указателями, поэтому они могут быть нулевыми только случайно. Вы можете улучшить диагностику, распечатав команду в дочернем элементе.

Jonathan Leffler 10.09.2018 08:16

Ваш цикл for (i = 0; i <= counter - 1; i++) лучше записать как for (i = 0; i < counter; i++) - это более идиоматический C и имеет тот же эффект. Однако то, что я сказал, не гарантирует решения ваших проблем. Скорее всего, вам нужно увидеть, что говорят диагностические выводы. Например, есть вероятность, что ваш код полностью пропускает ls. Кроме того, ваш ls находится в /bin/ls или в /usr/bin/ls (или они одинаковы, потому что один из каталогов является символической ссылкой на другой)?

Jonathan Leffler 10.09.2018 08:16

Сообщения об ошибках должны быть распечатаны в стандартном формате ошибки. После сбоя вызова одной из функций exec*() (путем возврата; нет необходимости проверять возвращаемое значение), сообщите об ошибке и выйдите из программы (exit() или один из быстрых выходов). Не позволяйте заблудившемуся ребенку продолжать; это приводит к путанице.

Jonathan Leffler 10.09.2018 08:24

Итак, я знаю, что мне не хватает множества точек проверки ошибок, которые я сделаю после того, как выясню текущую проблему. Ваше право на добавление выхода после exec. ls - это каталог символических ссылок, поэтому работают как user / bin, так и bin /. Какие диагностические выводы вы имеете в виду?

Adil Hamid 10.09.2018 08:28

btw printf(input) - одна из самых известных ошибок безопасности (уязвимость строки формата) - всегда используйте printf("%s", input) (или fputs, put)

Antti Haapala 10.09.2018 08:48

Вы печатаете информацию "привет, я ребенок", но было бы полезно иметь "и я собираюсь выполнить команду xxxx с аргументами yyyy zzzz" в выводе. Я не готов читать больше вашего кода; Мне очень не нравится макет, и я считаю его почти нечитаемым. Я действительно заметил, что если вы хотите, чтобы команды выполнялись параллельно, вы не можете позволить себе ждать завершения каждой команды, прежде чем переходить к запуску следующей. Я думаю, что ваш код ожидания находится не в том месте, но я могу ошибаться.

Jonathan Leffler 10.09.2018 08:49

дело в том, что для меня он выходит с 0x8b, это -11/137 в человеческих числах, кстати. SIGSEGV. Пора начинать изучать отладку.

Antti Haapala 10.09.2018 08:54
valgrind ./a.out - разыменование нулевого указателя.
Antti Haapala 10.09.2018 08:55

@JonathanLeffler, я добавил оператор print перед execv, но по какой-то причине он запускается только один раз при вызове 2 команд, даже если этот оператор printf находится наверху дочернего элемента, он запускает их обе. Из-за этого я чувствую, что, возможно, он не ждет достаточно долго для запуска 2-й команды или чего-то в этом роде

Adil Hamid 10.09.2018 19:09
0
10
149
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Эта адаптация вашего кода работает, по крайней мере, для pwd & ls в качестве командной строки. Одна из проблем заключалась в том, что цикл поиска имени пути к команде находился в неправильном месте. Еще одна проблема заключалась в том, что петля wait() оказалась не в том месте. Другая проблема заключалась в том, что аргументы команды не были должным образом разделены.

Код показывает печать (довольно много), которую я добавил, чтобы убедиться, что я понял, что происходит, и которая высветила некоторые проблемы.

Исходный код (exec29.c):

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    char input[100];
    char *path[15];
    path[0] = "/bin/";
    FILE *fp;

    if (argc == 2)
    {
        if ((fp = fopen(argv[1], "r")) == NULL)
        {
            fprintf(stderr, "Error! opening file %s\n", argv[1]);
            exit(1);
        }
    }

    do
    {
        if (argc != 2)
        {
            printf("dash> ");
            if (fgets(input, 100, stdin) == NULL)
                exit(0);
        }
        else if (argc == 2)
        {
            if (fgets(input, 100, fp) == NULL)
                exit(0);
        }

        input[strcspn(input, "\n")] = '\0';
        printf("%s\n", input);
        char *token = strtok(input, " ");

        int temp = 0;
        if (strcmp(token, "exit") == 0)
        {
            exit(0);
        }
        else if (strcmp(token, "cd") == 0)
        {
            token = strtok(NULL, " ");
            if (chdir(token) == -1)
                fprintf(stderr, "error: cd %s\n", token);
        }
        else if (strcmp(token, "path") == 0)
        {
            int counter = 0;
            while (path[counter] != NULL)
            {
                path[counter] = strtok(NULL, " ");
                if (path[counter] != NULL)
                    printf("PATH: %s\n", path[counter]);
                counter++;
            }
        }
        else
        {
            char *myargs[15];
            char *par[15];
            myargs[0] = token;
            int counter = 0;
            int n_cmds = 1;
            while (myargs[counter] != NULL)
            {
                counter++;
                myargs[counter] = strtok(NULL, " ");
            }

            for (int i = 0; i < counter; i++)
                printf("%d: [[%s]]\n", i, myargs[i]);

            for (int i = 0; i < counter; i++)
            {
                if (strcmp(myargs[i], "&") == 0)
                    n_cmds++;
            }
            temp = n_cmds;
            printf("Num commands: %d\n", n_cmds);

            int t = 0;
            for (int i = 0; i < n_cmds; i++)
            {
                int a = 0;
                for (int j = t; j < counter; j++)
                {
                    if (strcmp(myargs[j], "&") == 0)
                    {
                        t = j + 1;
                        break;
                    }
                    par[a++] = myargs[j];
                }
                par[a] = NULL;
                int rc = fork();
                if (rc < 0)
                {
                    fprintf(stderr, "Error: fork failed\n");
                    return 1;
                }
                else if (rc == 0)
                {
                    char str[200];
                    printf("hello, I am child (pid:%d)\n", (int)getpid());
                    int c = 0;
                    do
                    {
                        if (path[c] == NULL)
                        {
                            fprintf(stderr, "path error - no PATH left\n");
                            break;
                        }
                        strcpy(str, path[c++]);
                        strcat(str, par[0]);
                    } while (access(str, X_OK) == -1);
                    printf("Cmd: [[%s]]\n", str);

                    char **args = par;
                    while (*args != NULL)
                        printf("[[%s]]\n", *args++);
                    fflush(stdout);
                    execv(str, par);
                    fprintf(stderr, "%s: failed\n", str);
                    exit(1);
                }
            }
        }

        while (temp > 0)
        {
            int status;
            pid_t pid = wait(&status);
            printf("%d: Child with PID %ld exited with status 0x%.4x.\n",
                   (int)getpid(), (long)pid, status);
            --temp;
        }

    } while (strcmp(input, "exit") != 0);
    return 0;
}

Учитывая файл test-exec, содержащий:

pwd & ls
pwd -P & ls -l

Я получил пример вывода (exec29 test-exec):

pwd & ls
0: [[pwd]]
1: [[&]]
2: [[ls]]
Num commands: 2
hello, I am child (pid:4247)
Cmd: [[/bin/pwd]]
[[pwd]]
hello, I am child (pid:4248)
Cmd: [[/bin/ls]]
[[ls]]
/Users/jleffler/soq/so-5225-1783
4246: Child with PID 4247 exited with status 0x0000.
exec23.c    exec29      exec29.c    exec29.dSYM exec97.c    makefile    test-exec   test-exec-2
4246: Child with PID 4248 exited with status 0x0000.
pwd -P & ls -l
0: [[pwd]]
1: [[-P]]
2: [[&]]
3: [[ls]]
4: [[-l]]
Num commands: 2
hello, I am child (pid:4249)
Cmd: [[/bin/pwd]]
[[pwd]]
[[-P]]
hello, I am child (pid:4250)
Cmd: [[/bin/ls]]
[[ls]]
[[-l]]
/Users/jleffler/soq/so-5225-1783
4246: Child with PID 4249 exited with status 0x0000.
total 80
-rw-r--r--  1 jleffler  staff  3722 Sep 10 20:43 exec23.c
-rwxr-xr-x  1 jleffler  staff  9644 Sep 10 21:41 exec29
-rw-r--r--  1 jleffler  staff  4054 Sep 10 21:41 exec29.c
drwxr-xr-x  3 jleffler  staff    96 Sep 10 21:05 exec29.dSYM
-rw-r--r--  1 jleffler  staff  4549 Sep 10 21:45 exec97.c
-rw-r--r--  1 jleffler  staff   163 Sep 10 21:41 makefile
-rw-r--r--  1 jleffler  staff    24 Sep 10 21:47 test-exec
-rw-r--r--  1 jleffler  staff    34 Sep 10 21:38 test-exec-2
4246: Child with PID 4250 exited with status 0x0000.

Я удалил код, который, казалось, был связан с перенаправлением ввода-вывода; Я не удалял код, связанный с cd, exit или path, но и не тестировал их.

Один небольшой, непонятный момент: стандартный заголовок <iso646.h> определяет макрос and, который расширяется до &&. Лучше не использовать какие-либо имена из этого заголовка в качестве имен переменных, хотя люди действительно редко используют заголовок в C (но имена зарезервированы как «альтернативные представления» в C++).

Спасибо, да, я понял это в понедельник. Путь был неправильным, это была проблема, поэтому, когда я исправил это, он сработал.

Adil Hamid 13.09.2018 18:30

Другие вопросы по теме