11.7.2. Изменения в коде
11.7.2. Изменения в коде
Как только в parseCommand() будут правильно отражены структуры данных, то запуск команд в правильном порядке становится довольно простым при достаточном внимании к деталям. Прежде всего, мы добавляем цикл в parseCommand() для запуска дочерних процессов, поскольку теперь их может быть множество. Прежде чем войти в цикл, мы устанавливаем nextin и nextout, которые являются файловыми дескрипторами, используемыми в качестве стандартных потоков ввод и вывода для следующего запускаемого процесса. Для начала мы используем те же stdin и stdout, что и оболочка.
Теперь посмотрим, что происходит внутри цикла. Основная идея описана ниже.
1. Если это финальный процесс в задании, убедиться, что nextout указывает на stdout. В противном случае нужно подключить вывод этого задания к входному концу неименованного канала.
2. Породить новый процесс. Внутри дочернего перенаправить stdin и stdout, как указано с помощью nextin, nextout и всех специфицированных ранее перенаправлений.
3. Вернувшись обратно в родительский процесс, закрыть nextin и nextout, используемые только что запущенным дочерним процессом (если только они не являются потоками ввода и вывода самой оболочки).
4. Теперь настроить следующий процесс в задании для приема его ввода из вывода процесса, который мы только что создали (через nextin).
Вот как эти идеи перевести на С.
365: nextin=0, nextout=1;
366: for (i=0; i<newJob.numProgs; i++) {
367: if ((i+1) < newJob.numProgs) {
368: pipe(pipefds);
369: nextout = pipefds[1];
370: } else {
371: nextout = 1;
372: }
373:
374: if (!(newJob.progs[i].pid = fork())) {
375: if (nextin != 0) {
376: dup2(nextin, 0);
377: close(nextin);
378: }
379:
380: if (nextout != 1) {
381: dup2(nextout, 1);
382: close(nextout);
383: }
384:
385: /* явное перенаправление перекрывает каналы */
386: setupRedirections(newJob.progs+i);
387:
388: execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
389: fprintf(stderr, "exec() of %s failed: %s ",
390: newJob.progs[i].argv[0],
391: strerror(errno));
392: exit(1);
393: }
394:
395: /* поместить наш дочерний процесс в группу процессов,
396: чей лидер - первый процесс канала */
397: setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
398:
399: if (nextin != 0) close(nextin);
400: if (nextout != 1) close (nextout);
401:
402: /* Если больше нет процессов, то nextin - мусор,
403: но это не имеет значения */
404: nextin = pipefds[0];
Единственный код, добавленный в ladsh2.с для обеспечения перенаправлений — это функция setupRedirections(), код которой останется неизменным во всех последующих версиях ladsh. Ее задача состоит в обработке спецификаторов struct redirectionSpecifier для дочерних заданий и соответствующей модификации дочерних файловых дескрипторов. Мы рекомендуем просмотреть код этой функции в приложении Б.