2014-10-12 1 views
2

Я пытаюсь реализовать образец оболочки, такой как программа, которая выполняет команду ls | wcОшибка дескриптора файла при реализации трубопровода в C

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

туалет: стандартный ввод: Плохой дескриптор файла туалет: -: Плохой дескриптор файла

Пожалуйста, обратите внимание на код и обеспечивают входы Примечание: 1) синтаксического анализа является библиотека, которая принимает введенный ввод и возвращает каждую команду в виде связанного списка с аргументами и необходимыми данными. Анализировать работает отлично 2) Я выполняющийся каждую команду в другой подпроцесс следовательно вилке

#include <stdio.h> 
#include <stdlib.h> 
#include "parse.h" 

int pip[3][2]; 
int main(int argc, char *argv[], char *envp[]) 
{ 
    Pipe p; 
    Cmd c; 
    pipe(pip[0]); 
    pipe(pip[1]); 
    pid_t pid; 
    pid=fork(); 
    char *host = "armadillo"; 
    printf("%s%% ", host); 
    p = parse(); 
    c=p->head; 
    printf("1 \n"); 
    pid=fork(); 

    if(pid==0) 
    { 
     close(pip[0][0]); 
     close(STDOUT_FILENO); 
     dup2(pip[0][1],STDOUT_FILENO); 
     execvp(c->args[0],c->args); 
    } 
    else 
    { 
     waitpid(pid,NULL,0); 
    } 
    printf("2 \n"); 

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

    c=c->next; 
    printf("%s \n",c->args[0]); 
    pid=fork(); 
    if(pid==0) 
    { 
     close(STDIN_FILENO); 
     dup2(pip[0][0],STDIN_FILENO); 
     close(pip[0][1]); 
     execvp(c->args[0],c->args); 
    } 
    else 
    { 
     waitpid(pid,NULL,0); 
     close(pip[0][1]); 
     close(pip[0][0]); 
    } 

} 
+1

Почему вы открываете два раза? Я имею в виду 'fork' Call, который вы делаете, прежде чем делать что-либо, почему вы это делаете? –

+0

@JoachimPileborg Я выполняю каждую команду в другом подпроцессе Итак, одна вилка для ls и других для wc – kairav

+0

Будьте осторожны, когда вы закрываете дескрипторы труб. Кроме того, все процессы в конвейере должны иметь возможность запускаться одновременно; если вы дождались завершения первого перед запуском второго, вы можете написать ему больше данных, чем вписывается в канал, поэтому он блокирует ожидание второго процесса для чтения из канала, но второй процесс не будет запускаться до тех пор, пока после первого завершения, поэтому ничего очень не происходит в течение очень долгого времени. –

ответ

1

Я взял ленивый выход, и написал свой собственный, а не исправить другой код. Относитесь к этому как «еще один пример установки труб в C», но он может помочь указать проблемы с кодом OP.

/* 
* hard-wired example program exploring how to implement 
* 
*  system("ls | wc"); 
* 
* using calls to pipe(2), fork(2), execvp(2) and wait(2) 
*/ 

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

static void 
do_close(int fd) 
{ 
    if (close(fd) == -1) { 
     perror("close"); 
     exit(1); 
    } 
} 

static void 
do_execvp(char *const cmd[]) 
{ 
    execvp(cmd[0], cmd); 

    /* 
    * if execvp returns in this text, an error occured. 
    */ 

    perror("execvp"); 

    exit(1); 
} 

static void 
dup_and_exec(int fd, int *pp, char *const cmd[]) 
{ 
    if (dup2(pp[fd], fd) == -1) { 
     perror("dup2"); 
     exit(1); 
    } 

    do_close(pp[0]); 
    do_close(pp[1]); 

    do_execvp(cmd); 
} 

int 
main(void) 
{ 
    char *const ls_cmd[] = { "ls", 0 }; 
    char *const wc_cmd[] = { "wc", 0 }; 

    int fds[2]; 

    int w_stat; 
    pid_t ls_pid, wc_pid, w_pid; 

    /* create a single pipe to connect our writer and reader processes */ 

    if (pipe(fds) == -1) { 
     perror("pipe"); 
     exit(1); 
    } 

    /* create the writer process: ls */ 

    ls_pid = fork(); 

    if (ls_pid == -1) { 
     perror("fork"); 
     exit(1); 
    } 

    if (ls_pid == 0) { 
     /* this is the child - do the "ls" command */ 

     dup_and_exec(1, fds, ls_cmd); /* no return from here */ 
    } 

    /* create the reader process: wc */ 

    wc_pid = fork(); 

    if (wc_pid == -1) { 
     perror("fork"); 
     exit(1); 
    } 

    if (wc_pid == 0) { 
     /* this is the child - do the "wc" command */ 

     dup_and_exec(0, fds, wc_cmd); /* no return from here */ 
    } 

    /* parent process */ 

    /* 
    * It's important to close the pipe completely in the parent, 
    * so (in particular) there's no process that could be an 
    * additional writer to the "write" side of the pipe. 
    * 
    * We need to arrange things so that our reader process (the "wc" 
    * process in this example) will see EOF when the only writer (the 
    * "ls" process) closes its output and exits. 
    * 
    * If this parent process does not close the write side of the pipe, 
    * it remains open, since it's shared across fork(2), so the reader 
    * (wc) won't ever see EOF and exit, and this parent process won't 
    * ever see the wc exit, and everything hangs. 
    * 
    * The core problems will have started with the parent, which all 
    * children know to be true. 
    * 
    * The next lines also close the "read" side of the pipe, which 
    * is a bit cleaner, but won't affect proper operation of this 
    * sample program. But closing all un-needed file descriptors is 
    * good hygiene: for longer running applications, or for library 
    * code that could be called from longer running programs, avoiding 
    * any leaks of file descriptors is a good thing. 
    */ 

    do_close(fds[0]); 
    do_close(fds[1]); 

    while ((w_pid = wait(&w_stat)) > 0) { 
     printf("%s process exited", w_pid == ls_pid ? "ls" : "wc"); 
     if (WIFEXITED(w_stat)) { 
      printf(" (status %d)", WEXITSTATUS(w_stat)); 
     } 
     fputs("\n", stdout); 
    } 

    if (w_pid == -1 && errno != ECHILD) { 
     perror("wait"); 
     exit(1); 
    } 

    return 0; 
} 
4

Основная проблема здесь:

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

... 

dup2(pip[0][0],STDIN_FILENO); 
close(pip[0][1]); 

Здесь вы сначала закрыть дескрипторы файлов, затем в программу, которую вы пытаетесь использовать снова.

+0

Но Если я комментирую закрытие, то программа не заканчивается, и я не вижу никакого вывода – kairav

1

Есть несколько проблем в вашем коде:


Вы делаете внучку первоначального процесса

pid=fork(); 
char *host = "armadillo"; 
printf("%s%% ", host); 
p = parse(); 
c=p->head; 
printf("1 \n"); 
pid=fork(); // this fork here is wrong 

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

Ваш код будет что-то подобное в той части:

pid_t pid; 
pid=fork(); 
char *host = "armadillo"; 
printf("%s%% ", host); 
p = parse(); 
c=p->head; 
printf("1 \n"); 
// pid=fork(); // it'll be in another part 

if (pid == -1) { 
    // print error 
    exit(1); 
} else if (pid == 0) { 
    //child 
    close(pip[0][0]); 
    close(STDOUT_FILENO); 
    dup2(pip[0][1],STDOUT_FILENO); 
    close(pip[0][1]); // I added this 
    execvp(c->args[0],c->args); 
} 
//parent 
waitpid(pid,NULL,0); // it's not a good idea but I leave it here 
printf("2 \n"); 

// now you can fork again and use the same pid variable 
pid=fork(); 

Вы ждете ребенка, чтобы закончить.

if(pid==0) 
{ 
    close(pip[0][0]); 
    close(STDOUT_FILENO); 
    dup2(pip[0][1],STDOUT_FILENO); 
    execvp(c->args[0],c->args); 
} 
else 
{ 
    waitpid(pid,NULL,0); // you have more commands to execute yet, so you must do it before this 
} 

Waitpid не было бы необходимости вообще, если вы используете родительский процесс, чтобы выполнить последнюю команду на трубе (wc). Но это зависит от вас, если вы хотите иметь родительский процесс. Если это так, вы должны позвонить waitpid, когда все дети выполняют свои задачи.


Вы не должны закрывать трубу перед тем dup2 .. ошибка вы вывесили, кажется, из-за этого.

wc: standard input: Bad file descriptor 0 0 0 wc: -: Bad file descriptor 

После dup2 необходимо закрыть трубу в ребенке.

close(pip[0][0]); // it's ok 
close(STDOUT_FILENO); // it's ok but not necessary 
dup2(pip[0][1],STDOUT_FILENO); 
// here you have to close(pip[0][1]) due to you have already duped it in STDOUT_FILENO 
execvp(c->args[0],c->args); 

Если у вас есть родитель, вы должны закрыть его только один раз, когда оба ребенка его обманули.

printf("2 \n"); 

close(pip[0][1]); 
close(pip[0][0]); // You're closing the file descriptor which wc needs to read. 

Вы не проверяет все возможные состояния возврата некоторых функций.

pipe 
fork 
execvp 
dup2 

Есть еще что-то улучшить

int pip[3][2]; // in your case with `int pip[2]` would be enough 
pipe(pip[0]); 
pipe(pip[1]); // in your case you have to create just one pipe 
+0

, фактически я получил ответ. Я удалил 2 оператора закрытия в середине, и когда я использовал другую переменную pid для второго pid, котор она работала. Так что проблема была со вторым waitpid, ожидавшим неправильного значения pid – kairav