Проблема перенаправления ввода при использовании execvp? - PullRequest
1 голос
/ 10 апреля 2011

Я реализовал простую программу, которая имитирует выполнение команды $ls -l | wc -c с использованием простых каналов и вызовов execvp.

Теперь После перенаправления stdin и stdout при запуске программы приглашение оболочки исчезает и ожидает нажатия клавиши ввода.

Любой способ решения этой проблемы. Плз критиковать мой код также ..

Спасибо

 /* Create pipe */
  ret_val=pipe(pipe_fd);
  /*Error check */
   if(ERROR==ret_val)
   {
    perror("Pipe creation error \n");
    _exit(FAILURE);
   }

/*Fork First Child */
pid_one = fork() ;

if( 0 == pid_one ) /*child process block */
{


    /* First sub process */
    /*printf("First sub process is %d \n",getpid());*/

    /* redirect stdout to pipe's write end  for sub process one*/
    dup2(pipe_fd[1],1);


    /*close pipe read end */
    close(pipe_fd[0]);

    execvp(cmd_one_tokens[0],cmd_one_tokens);   

    /* if execvp returns then if must have failed */
    printf("Unknown Command \n ");
    //exit(10);
}
else /*main process block */
{
    /*printf(" Main  process is %d \n",getpid());*/

    /*Wait for first sub process to finish */
    //wait(&status);

    /*printf("Exit status of first child is %d \n ", WEXITSTATUS(status) );*/

    /*Fork second subprocess */
    pid_two = fork();

    if( 0 == pid_two ) /*second child process block */
    {
        /* redirect stdin  to pipe's read end  for sub process two */
        dup2(pipe_fd[0],0);

    //  close(0);       /* close normal stdin */
        //  dup(pipe_fd[0]);   /* make stdib same as pfds[0] */


        /*close pipe write end */
        close(pipe_fd[1]);

        /* Second  sub process */
        /*printf("Second sub process is %d \n",getpid()); */

        execvp(cmd_two_tokens[0] , cmd_two_tokens); 
        /* if execvp returns then if must have failed */
        printf("Unknown Command \n ");

    }
    else        /*main process block */
    {
        /* printf(" Main  process is %d \n",getpid()); */
        status=-1; /*reset status */

        /*Waiting for the second sub process to finish in No hang fashion */
            waitpid ( -1 , &status ,WNOHANG);
        /*printf("Exit status of second  child is %d \n ", WEXITSTATUS(status) ); */

    }
}

Ответы [ 3 ]

2 голосов
/ 10 апреля 2011

Вы должны закрыть дескрипторы файла канала в основном процессе, после того как второй разветвлен.Пока вы не закроете их, дочерний процесс (wc) будет ожидать ввода в канале, который главный процесс все еще имеет открытым.Вы должны быть очень осторожны, чтобы закрыть все ненужные концы трубы.

1 голос
/ 10 апреля 2011
pid_one = fork() ;
if( 0 == pid_one ) /*child process block */

Вы не проверяете fork(2) на наличие ошибки, что вполне реально. (Пользователь может столкнуться с пределом RLIMIT_NPROC, kernel.threads-max, нехваткой памяти для хранения структур задач и т. Д.)

Более идиоматическое использование fork(2) выглядит так:

if(-1 == (pid = fork()) {
    perror("fork");
    exit(1); /* or return -1 or similar */
} else if (0 == pid) {
    /* executing as child */
} else {
    /* parent, pid is child */
}
execvp(cmd_two_tokens[0] , cmd_two_tokens); 
/* if execvp returns then if must have failed */
printf("Unknown Command \n ");

Обратите внимание, что существует много причин, по которым execvp(3) может потерпеть неудачу; просто печать «Неизвестная команда» может оставить ваших пользователей очень запутанными в будущем. Было бы лучше позвонить по номеру perror("execvp"); и дать вашим пользователям возможность узнать реальную причину, по которой их execvp(3) звонок не удался.

 waitpid ( -1 , &status ,WNOHANG);

Использование WNOHANG здесь может быть опасно; если система работает «правильно», ваш родитель может получить этот код еще до того, как у ребенка появится шанс начать выполнение. Поскольку вы просили, чтобы он немедленно вернулся, если ни один ребенок не вышел, вероятно, когда он, наконец, выйдет, ребенок превратится в зомби - ваш код не использует возможность снова ждать ребенка.

Я не уверен, каково лучшее решение: если вы используете SA_NOCLDWAIT до sigaction(2), чтобы полностью не создавать зомби, у вас не будет возможности когда-либо собрать статус выхода ребенка. Установка обработчика сигнала SIGCHLD может помешать остальной части процесса; у ваших клиентов может быть причина установить это самостоятельно. Использование блокировки waitpid(2) может остановить обработку в другом месте. А использование неблокирующего waitpid(2) означает, что вам все равно придется собирать статус ребенка иногда , возможно, путем опроса. (Но вы не можете использовать -1 для pid в этом случае, так как вы можете случайно получить другой дочерний процесс.)

1 голос
/ 10 апреля 2011

Ваш код не делает то, что вы описываете, что вы хотите сделать:

Вы создаете канал, форкируете новый процесс, перенаправляете его стандартный поток в канал и заставляете его выполнять какую-то программу (пока все хорошо), затем в родительском процессе вы ждете, пока ваш ребенок завершит работу, и только после этого запускаете второй процесс , перенаправьте его stdin на другой конец канала и заставьте выполнить другую программу.

Это не то, что делает "ls | wc" - в оболочке они работают одновременно. Удалите первое ожидание ().

...