Восстановление stdout после использования dup

Используя fork, я создал дочерний элемент, и в нем я выполняю команду ls, используя execl. Чтобы отправить вывод родителю, я использовал pipe и dup. Затем родитель печатает вывод. Код дает ожидаемый результат, но когда я попытался восстановить обратно stdout, который я изначально сохранил в stdout_holder, на терминале ничего не печатается (когда я использовал printf("hello") или оператор execl под ним). Однако после нескольких наблюдений было замечено, что hello печатается только тогда, когда ничего не делается после перенаправления "1" в первый раз. (Если я ничего не делаю после dup(fd[1],1) и просто делаю dup(stdout_holder,1) ) Почему это происходит?

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<string.h>
int main()
 {int fd[2],stdout_holder;
 char str;
 pid_t pid;
 pipe(fd);
 pid=fork();
 if(pid==0)
  { stdout_holder=dup(1);
    close(fd[0]);
   printf("stdout_holder=%d\n",stdout_holder); 
   fd[1]=dup2(fd[1],1);
   execl("/bin/ls","ls","-l",NULL);
   stdout_holder=dup2(stdout_holder,1);
   printf("hello\n"); //Terminal doesnt show output.
   execl("/bin/ls","ls","-l",NULL); //Terminal doesnt show output

 }
else
 { close(fd[1]);
   wait(&pid);
   while(read(fd[0],&str,1)>0)
   printf("%c",str);
  } 
}

person Dv Nikhil    schedule 22.01.2016    source источник
comment
execl() не возвращает результат в случае успеха, а заменяет образ процесса. Вы должны раскошелиться ().   -  person Ctx    schedule 22.01.2016


Ответы (3)


Есть несколько проблем:

  1. execl() не возвращается (кроме случаев ошибки), вам нужно снова fork() или использовать, например. система(). На execl буферы не очищаются автоматически, поэтому вывод printf (по крайней мере, для меня) не достигает stdout.

  2. Первый вывод printf("stdout_holder= ... и ls поступает напрямую на стандартный вывод, а не через конвейер (стандартный вывод не заменяется). Вам нужно будет использовать dup2() в первую очередь или close(1) перед вызовом dup().

person jofel    schedule 22.01.2016
comment
Спасибо :) относительно вашей второй проблемы (согласно тому, что я читал и пробовал), ls после printf не переходит в стандартный вывод, потому что dup2 автоматически закрывает 1... dup2 (fd1, fd2) .. когда это вызывается, если fd2 - это файловый дескриптор открытого файла, он автоматически закрывает его (поэтому close (1) не нужен).. вот почему ls перешел в канал, и я получил вывод, когда я печатал в родительском процессе. - person Dv Nikhil; 23.01.2016

Всякий раз, когда вы вызываете execl(execv, execlp и т. д.), он запускает выполнение новой программы (создает новый образ процесса). Выполнение этой новой программы приводит к тому, что процесс полностью забывает о своем предыдущем образе процесса. Функция execl не возвращается к тому же образу процесса, если не обнаруживает какую-либо ошибку.

if(pid==0)
  { stdout_holder=dup(1);
    close(fd[0]);
   printf("stdout_holder=%d\n",stdout_holder); 
   fd[1]=dup2(fd[1],1);
   execl("/bin/ls","ls","-l",NULL); //creates a new process image
   //will never reach here unless there is an error in the execl call
   stdout_holder=dup2(stdout_holder,1);//Line 7
   printf("hello\n");
   execl("/bin/ls","ls","-l",NULL);// Line 9
 }

После того, как дочерний процесс завершит первый вызов execl, он завершится и, следовательно, никогда не достигнет оставшегося кода (от строки 7 до строки 9). Выполнение нового образа процесса полностью изменяет содержимое памяти, копируя в новые места только строки аргументов и среды.

Надеюсь, что это ответ на ваш вопрос.

person p-kar    schedule 22.01.2016
comment
Спасибо :) я на самом деле пытаюсь увидеть пример восстановления stdout ... если я заменю оператор execl операторами printf (скажем, 2).. они не перенаправляются в канал и не печатаются на терминале ... после того, как я восстановлю stdout и дать один оператор printf, все операторы появляются на терминале (2 предыдущих и один, данный сейчас).. я думаю, перенаправление вывода printf имеет проблему ..? Большое вам спасибо :) изменить: извините, мой плохой ... я забыл прокомментировать часть печати в родительском процессе, который печатал предыдущие утверждения. - person Dv Nikhil; 23.01.2016

Как и в строке 63 этого файла, вы должны сохраните дескриптор файла stdout перед его изменением:

int moutfd = dup(STDOUT_FILENO);
dup2(fd, STDOUT_FILENO);

// Do the going-to-be-buffered jobs

dup2(moutfd, STDOUT_FILENO);
close(moutfd);
close(fd);
person pouyan    schedule 24.01.2016