Даже после выхода из первой команды вашего конвейера (и при закрытии stdout=~fdPipe[1]
) у родителя по-прежнему fdPipe[1]
открыто.
Таким образом, вторая команда конвейера имеет stdin=~fdPipe[0]
, который никогда не получает EOF, потому что другая конечная точка канала все еще открыта.
Вам необходимо создать новый pipe(fdPipe)
для каждого |
и обязательно закрыть обе конечные точки в родительском элементе; т.е.
for cmd in cmds
if there is a next cmd
pipe(new_fds)
fork
if child
if there is a previous cmd
dup2(old_fds[0], 0)
close(old_fds[0])
close(old_fds[1])
if there is a next cmd
close(new_fds[0])
dup2(new_fds[1], 1)
close(new_fds[1])
exec cmd || die
else
if there is a previous cmd
close(old_fds[0])
close(old_fds[1])
if there is a next cmd
old_fds = new_fds
if there are multiple cmds
close(old_fds[0])
close(old_fds[1])
Также, чтобы быть более безопасным, вы должны обработать случай перекрытия fdPipe
и {STDIN_FILENO,STDOUT_FILENO}
перед выполнением любой из операций close
и dup2
. Это может произойти, если кому-то удалось запустить вашу оболочку с закрытым stdin или stdout, и это приведет к большой путанице с кодом здесь.
Редактировать
fdPipe1 fdPipe3
v v
cmd1 | cmd2 | cmd3 | cmd4 | cmd5
^ ^
fdPipe2 fdPipe4
Помимо того, что вы закрываете конечные точки канала в родительском объекте, я пытался подчеркнуть, что fdPipe1
, fdPipe2
и т. Д. не может быть таким же pipe()
.
/* suppose stdin and stdout have been closed...
* for example, if your program was started with "./a.out <&- >&-" */
close(0), close(1);
/* then the result you get back from pipe() is {0, 1} or {1, 0}, since
* fd numbers are always allocated from the lowest available */
pipe(fdPipe);
close(0);
dup2(fdPipe[0], 0);
Я знаю, что вы не используете close(0)
в вашем нынешнем коде, но последний абзац предупреждает вас об этом случае.
Редактировать
Следующее минимальное изменение вашего кода заставляет его работать в указанном вами случае сбоя:
@@ -12,6 +12,4 @@
pid_t pid;
- pipe(fdPipe);
-
while(1) {
bBuffer = readline("Shell> ");
@@ -29,4 +27,6 @@
} while(aPtr);
+ pipe(fdPipe);
+
for(i = 0; i < pCount; i++) {
aCount = -1;
@@ -72,4 +72,7 @@
}
+ close(fdPipe[0]);
+ close(fdPipe[1]);
+
for(i = 0; i < pCount; i++) {
waitpid(lPids[i], &status, 0);
Это не будет работать для более чем одной команды в конвейере; для этого вам понадобится что-то вроде этого: (не проверено, так как вы должны исправить и другие вещи)
@@ -9,9 +9,7 @@
int main(int argc, char *argv[]) {
char *bBuffer, *sPtr, *aPtr = NULL, *pipeComms[NUMPIPES], *cmdArgs[10];
- int fdPipe[2], pCount, aCount, i, status, lPids[NUMPIPES];
+ int fdPipe[2], fdPipe2[2], pCount, aCount, i, status, lPids[NUMPIPES];
pid_t pid;
- pipe(fdPipe);
-
while(1) {
bBuffer = readline("Shell> ");
@@ -32,4 +30,7 @@
aCount = -1;
+ if (i + 1 < pCount)
+ pipe(fdPipe2);
+
do {
aPtr = strsep(&pipeComms[i], " ");
@@ -43,11 +44,12 @@
if(pid == 0) {
- if(i == 0) {
- close(fdPipe[0]);
+ if(i + 1 < pCount) {
+ close(fdPipe2[0]);
- dup2(fdPipe[1], STDOUT_FILENO);
+ dup2(fdPipe2[1], STDOUT_FILENO);
- close(fdPipe[1]);
- } else if(i == 1) {
+ close(fdPipe2[1]);
+ }
+ if(i != 0) {
close(fdPipe[1]);
@@ -70,4 +72,17 @@
}
}
+
+ if (i != 0) {
+ close(fdPipe[0]);
+ close(fdPipe[1]);
+ }
+
+ fdPipe[0] = fdPipe2[0];
+ fdPipe[1] = fdPipe2[1];
+ }
+
+ if (pCount) {
+ close(fdPipe[0]);
+ close(fdPipe[1]);
}