Подтвердить что ты не робот

Написал мою собственную оболочку... застрял на трубах?

В течение последних нескольких дней я пытался написать свою собственную реализацию оболочки, но, похоже, я застрял на том, что трубы правильно работают. Я могу разобрать строку и разблокировать команды между каналами (например: ls | sort) индивидуально, но не может заставить их подключать входные данные от одного к другому.

Думаю, я просто не понимаю, как правильно использовать dup2() и трубы.

Теперь я включил свой код, который все еще не работает...:( Так что застрял...

void forkAndExecute( char* arrayOfWords[] , vector<pid_t> *vectorOfPIDs , bool hasNextCmd , bool hasPrevCmd) {

int fd[ 2 ];
pid_t pid;

if( hasNextCmd ){
    pipe(fd);
}

pid = fork();

//error if PID < 0
if( pid < 0 ) {
    cerr << ">>> fork failed >>>" << endl;
    exit(-1);
}
//child process if PID == 0
else if( pid == 0 ) {
    if ( hasPrevCmd ){
        dup2(fd[0] , 0);
        close(fd[0]);
        close(fd[1]);

    }
    if ( hasNextCmd ){
        dup2(fd[1],1);
        close(fd[0]);
        close(fd[1]);
    }
    execvp( arrayOfWords[0] , arrayOfWords );
    cout << ">>> command not found >>>" << endl;
    //if logic reaches here, exec failed
    exit(0);
} 
//parent process
else{
    close(fd[0]);
    close(fd[1]);
    //if( ! isLastCmd ){

    //}
    vectorOfPIDs->push_back(pid);
}

}

4b9b3361

Ответ 1

Первое предложение: Символьные константы лучше магических чисел.

const int PIPE_READ = 0;
const int PIPE_WRITE = 1;
int fd[2];
pipe(fd);
// Now you can refer to fd[PIPE_READ] and fd[PIPE_WRITE].

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

Вы хотите создать два процесса с первым этапом процесса, связанным со вторым процессом stdin. Правильно?

Итак, в C это означает, что вам нужно принять вызов pipe, передать fd[PIPE_WRITE] в первый дочерний процесс, который будет dup2 равен 1, и передать fd[PIPE_READ] во второй дочерний процесс, который будет dup2 равен 0.

Просто глядя на прототип forkAndExecute' показывает, что он не может этого сделать:

void forkAndExecute( char* arrayOfWords[] , vector *vectorOfPIDs , 
    bool hasNextCmd , bool hasPrevCmd);

Он обрабатывает только одну команду и, глядя на список аргументов, если он не обращается к злым глобальным переменным, нет способа получить файловый дескриптор из PrevCmd или получить файловый дескриптор из своего NextCmd.

Подумайте о том, как управлять дескрипторами файлов, которые вам нужны, и перепроектируйте forkAndExecute, чтобы иметь возможность использовать их.

Ответ 2

Общий процесс добавит обработку ошибок в этот базовый процесс (псевдокод):

pipe(fds)
if (fork() is child) {
  dup2(fds[1], 1)
  close(fds[0])
  close(fds[1])
  exec("ls")
}
if (fork() is child) {
  dup2(fds[0], 0)
  close(fds[0])
  close(fds[1])
  exec("sort")
}
close(fds[0])
close(fds[1])
wait()

Создайте первую трубку. Затем пропустите дочерние процессы, чтобы они наследовали его. Удалите дескрипторы файла до 0 (stdin) и 1 (stdout), чтобы процессы считывали и записывали соответствующие места. Закройте любой оставшийся файловый дескриптор, который вы не хотите, чтобы дочерние процессы видели или блокировали, когда работа завершена. Exec - фактические дочерние процессы. Подождите, пока они закончатся, и все готово!

Ответ 3

ok Это работает для меня. Надеюсь, это поможет вам:

/************************
function: void pipeCommand(char** cmd1, char** cmd2)
comment: This pipes the output of cmd1 into cmd2.
**************************/
void pipeCommand(char** cmd1, char** cmd2) {
  int fds[2]; // file descriptors
  pipe(fds);
  // child process #1
  if (fork() == 0) {
    // Reassign stdin to fds[0] end of pipe.
    dup2(fds[0], STDIN_FILENO);
    close(fds[1]);
    close(fds[0]);
    // Execute the second command.
    // child process #2
    if (fork() == 0) {
        // Reassign stdout to fds[1] end of pipe.
        dup2(fds[1], STDOUT_FILENO);
        close(fds[0]);
        close(fds[1]);
        // Execute the first command.
        execvp(cmd1[0], cmd1);
    }
    wait(NULL);
    execvp(cmd2[0], cmd2);
    }
    close(fds[1]);
    close(fds[0]);
    wait(NULL);
}

Ответ 4

Вот учебник по UNIX-каналам, в частности о том, как построить конвейеры в оболочечной архитектуре:

http://www.cse.ohio-state.edu/~mamrak/CIS762/pipes_lab_notes.html

Не много полностью написанного кода, но он довольно хорошо описывает концепции.

Вы также можете скачать исходный код для практически любой оболочки, например bash, tcsh, zsh и т.д.

Ответ 5

Попробуйте прочитать исходный код Bash, чтобы увидеть, как они это сделали.

Ответ 6

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

Это действительно полезно для примеров по многим темам МПК. У меня все еще есть копия на столе, на которую я ссылаюсь время от времени. За $2 - $9, это очень хорошая ценность для того, что вы получаете.

Для чего это стоит, просто подумал, что я упоминаю об этом.

Ответ 7

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

Ответ 8

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

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

Ответ 9

Ну, у меня нет ответа, но я работаю над той же проблемой atm. Я поделюсь тем, что у меня есть. Он работает для двух команд, но после того, как он завершен, i/o сломаны. странным образом я еще не смог понять. позвоните водопроводчику!

void pipeCommand(char** cmd1, char** cmd2) {
  int fds[2]; // file descriptors
  pipe(fds);
  int oldIn, oldOut;
  // child process #1
  if (fork() == 0) {
    // Reassign stdin to fds[0] end of pipe.
    oldIn = dup(STDIN_FILENO);
    dup2(fds[0], STDIN_FILENO);
    close(fds[1]);
    close(fds[0]);
    // Execute the second command.
    execvp(cmd2[0], cmd2);
  // child process #2
  } else if ((fork()) == 0) {
    oldOut = dup(STDOUT_FILENO);
    // Reassign stdout to fds[1] end of pipe.
    dup2(fds[1], STDOUT_FILENO);
    close(fds[0]);
    close(fds[1]);
    // Execute the first command.
    execvp(cmd1[0], cmd1);
  // parent process
  } else
    wait(NULL);
    dup2(oldIn, STDIN_FILENO);
    dup2(oldOut, STDOUT_FILENO);
    close(oldOut);
    close(oldIn);
}

У меня есть ощущение, что это связано с тем, что я или не делаю после wait()