команда wait не будет ждать завершения дочернего процесса c cpp c ++ - PullRequest
2 голосов
/ 14 октября 2010

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

Родитель выполнил команду wait (NULL) или wait ((void *) pid), но он не ждет.

вот код:

#include <string.h>
#include <fstream>
#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
using namespace std;

int main(int argc, char * argv[])
{    
        char* commands[strlen(argv[1])];
        char *command = NULL;
        command = strtok(argv[1],"|");
        int i = 0;
        while(command != NULL)
        {
                commands[i] = command;
                i++;
                command = strtok(NULL,"|");
        }

        int numberOfCommands = i;

        pid_t pid;
        int pfd[2];
        char* prgname = NULL;
        if(pipe(pfd) == -1)
        {
                perror("error on pipe call");
                return(1);
        }

        for(int j = 0;j<numberOfCommands;j++)
        {
                cout<<commands[j]<<endl;
        }

        pid = fork();
        if(pid == 0){//child process
                printf("Child: My PID = %d\n", getpid());
                printf("Child: Running...\n");
                close(pfd[0]); //close read end of pipe
                dup2(pfd[1],1);//connect the pipes
                close(pfd[1]);//close extra file descriptors
                prgname = commands[0];//first command
                cout<<"child starting command: "<<prgname<<endl;
                execlp(prgname, prgname, 0);//Load the program
                **printf("Child: Done sleeping, returning.\n");**
        }
        else
        {
                printf("Parent: My PID = %d\n", getpid());
                **wait((void*)pid); //also tried wait(NULL); same effect
                printf("Parent: Running...\n");**
                close(pfd[1]); //close the write end of the pipe
                dup2(pfd[0],0);//connect the pipes
                close(pfd[0]); //close extra file descriptor
                prgname = commands[1];//now run the second command
                cout<<"parent starting command: "<<prgname<<endl;
                execlp(prgname, prgname, 0);//Load the programm
        }
        cout<<"all done"<<endl;
        return 0;
}

Не бери жирные линии. Я ожидал бы, что родительский процесс будет ожидать команды wait (), и потомок выведет «Дочерний сон закончен ...», а затем завершит работу, а затем родительский процесс выведет «Родитель: работает ...»

Что я делаю не так!

Спасибо!

Обновление: полный вывод в программу:

dmegs
more
Child: My PID = 30070
Child: Running...
Parent: My PID = 30066
Parent: Running...
parent starting command: more
child starting command: dmegs
Child: Done sleeping, returning.
all done

Ответы [ 4 ]

3 голосов
/ 14 октября 2010

Я вижу четыре проблемы:

1) execlp() не удается: execlp() (или любое из семейства функций exec) полностью заменяет текущий запущенный образ процесса в случае успеха - это не такожидается возвращение, если что-то пойдет не так.Но вы видите сообщение «Ребенок: Закончено спать, возвращаются», поэтому оно не может быть успешно выполнено.(В вашем примере, я бы предположил, что это, вероятно, потому что dmegs должен был быть dmesg.)

2) printf() и cout буферизация вывода означает, что нет никакой гарантии, что выполучают вывод в том порядке, в котором это происходит.Если вы хотите отладить это путем вывода на печать, вам лучше будет печатать на stderr (например, с fprintf(stderr, ...)), который (по умолчанию) не буферизован.

3) Как отмечали другие, wait((void *)pid) неправильно.wait(NULL) или waitpid(pid, NULL, 0).

4) Является ли эта проблема проблемой или нет, зависит от платформы, но ... аргумент завершающего нулевого указателя на execlp() должен быть явно записан как (char *)0а не просто 0, чтобы гарантировать, что он передается как указатель, а не как целое число.Как правило, в C * 0 в контексте указателя является по определению нулевым указателем, но при передаче параметров в функции с переменным числом аргументов компилятору не хватает информации, чтобы знать, что вы пытаетесь использовать его в указателеконтекст, и поэтому передаст его как целое число, если вы явно не приведете его.Это может привести к неприятностям на платформах, где указатели и целые числа не имеют одинаковый размер.


Поэтому я считаю, что wait() работает, ребенок на самом деле не выполняет нужную вам команду, ивывод из parent и child запутывается из-за буферизации.


Вот немного измененная версия вашего кода, которая не использует C ++, исключает обработку команд и просто каналывывод от sleep 5 до cat (что довольно бессмысленно, поскольку sleep не генерирует никакого вывода в любом случае, но задержка полезна, чтобы увидеть, что происходит):

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

int main(void)
{    
        pid_t pid;
        int pfd[2];
        if(pipe(pfd) == -1)
        {
                perror("error on pipe call");
                return(1);
        }

        pid = fork();
        if(pid == 0){//child process
                fprintf(stderr, "Child: My PID = %d\n", getpid());
                fprintf(stderr, "Child: Running...\n");
                close(pfd[0]); //close read end of pipe
                dup2(pfd[1],1);//connect the pipes
                close(pfd[1]);//close extra file descriptors
                fprintf(stderr, "child starting command: sleep 5\n");
                execlp("sleep", "sleep", "5", (char *)0);//Load the program
                fprintf(stderr, "child: execlp failed\n");
        }
        else
        {
                fprintf(stderr,"Parent: My PID = %d\n", getpid());
                wait(NULL);
                fprintf(stderr,"Parent: Running...\n");
                close(pfd[1]); //close the write end of the pipe
                dup2(pfd[0],0);//connect the pipes
                close(pfd[0]); //close extra file descriptor
                fprintf(stderr,"parent starting command: cat\n");
                execlp("cat", "cat", (char *)0);//Load the programm
        }
        fprintf(stderr,"all done\n");
        return 0;
}

Вывод:

$ gcc -Wall -o wait wait.c
$ ./wait
Child: My PID = 27846
Child: Running...
child starting command: sleep 5
Parent: My PID = 27845

(здесь задержка 5 секунд)

Parent: Running...
parent starting command: cat
$
1 голос
/ 14 октября 2010

Обычно вы включаете результат, чтобы учесть случай ошибки

pid = fork();
switch( pid ) {
 case -1: // parent fail
 case 0: // child success
 default: // parent success
}

Ожидание конкретного ребенка, которого вы хотели бы использовать

waitpid( pid, NULL, 0 );

или ждет любого ребенка

pid_t child = waitpid( -1, NULL, 0 );
0 голосов
/ 14 октября 2010
wait((void*)pid);

Вы не должны бросать вещи в void *, только чтобы компилятор перестал жаловаться. :)

Похоже, вы, вероятно, хотите waitpid : http://linux.die.net/man/2/waitpid

Обновление:

Вам необходимо проверить, действительно ли работал вызов execlp. Для сравнения:

$ ./a.out "dmegs|more"
dmegs
more
Parent: My PID = 20806
Child: My PID = 20807
Child: Running...
Parent: Running...
parent starting command: more
child starting command: dmegs
Child: Done sleeping, returning.
all done

с:

$ ./a.out "dmesg|more"
dmesg
more
Parent: My PID = 20876
Child: My PID = 20877
Child: Running...
^C

В первом случае, поскольку execlp не может найти "dmegs", дочерний процесс в основном завершается немедленно. Это разблокирует родительский процесс и позволяет его выполнить.

0 голосов
/ 14 октября 2010

почему ты делаешь

wait((void*)pid)

ожидание принимает указатель на статус

   #include <sys/types.h>
   #include <sys/wait.h>

   pid_t wait(int *status);

Вы почти наверняка передаете не записываемый адрес. Протестируйте код ожидания, и я готов поспорить, что вы жалуетесь;

Также смешивание printf и couts - это способ запутаться, их схемы буферизации / очистки могут быть разными

...