Написал мою собственную оболочку... застрял на трубах?
В течение последних нескольких дней я пытался написать свою собственную реализацию оболочки, но, похоже, я застрял на том, что трубы правильно работают. Я могу разобрать строку и разблокировать команды между каналами (например: 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);
}
}
Ответы
Ответ 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()