2015-12-18 4 views
4

Я пишу программу для выполнения другой программы в виде разветвленного процесса и перенаправляю ее вывод в файл или/dev/null по требованию.C - перенаправление stdout после разворачивания дочернего элемента из родителя

В настоящее время я разветвил и выполнил внешнюю программу с помощью execvp(). Затем перенаправил stdout из потока, созданного до разветвления, так как разветвленный процесс наследует родительскую таблицу дескриптора файла, позволяющую мне перенаправить после вымывания.

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

Вот код без битов проверки ошибок.

struct params { 
    const char *p; 
    int fd; 
    int wait; 
}; 

#define EXIT_NOEXEC 126 
#define EXIT_NOTFOUND 127 
#define EXIT_MISC 127 

static void dofile(struct params* st); 
void dupit(const char *p, struct params* st); 
void* reload_config(void* para); 

int 
main(int argc, char *argv[]) { 
    int exit_status, prog_status; 
    struct params init; 
    pid_t prog_pid; 


    dofile(&init); 

    prog_pid = fork(); 
    if (prog_pid == 0) { 
     execvp(*argv, argv); 
     exit_status = (errno == ENOENT) ? EXIT_NOTFOUND : EXIT_NOEXEC; 
     err(exit_status, "%s", argv[0]); 
     exit(EXIT_FAILURE); 
    } else { 
     while (wait(&prog_status) != prog_pid); 
     return prog_status; 
    } 
} 

static void dofile(struct params* st) { 
    const char *p 
    p = out.txt; 
    dupit(p, st); 

} 


void dupit(const char *p, struct params* st) { 
    pthread_t tid; 
    st->wait = 0; 
    int err = pthread_create(&(tid), NULL, &reload_config, st); 
    if (err != 0) { 
     printf("\ncan't create thread :[%s]", strerror(err)); 
     exit(1); 
    } else { 
     while (st->wait == 0) { 
      sleep(1) 
     } 
    } 
} 

void* reload_config(void* para) { 
    struct params *passed = (struct params *) para; 
    int pre_config = 3; 
    int cur_config = 1; 
    int saved_stdout = dup(STDOUT_FILENO); 
    char infile[5]; 
    int devNull = open("/dev/null", O_WRONLY); 
    int file = open("out.txt", O_WRONLY); 
    FILE *config; 
    config = fopen("config.txt", "r"); 
    if (access("config.txt", F_OK) != -1) { 
     while (1) { 
      fgets(infile, 5, config); 
      fclose(config); 
      cur_config = infile[0] - '0'; 
      printf("output from thread, current config = %d\n", cur_config); 
      if (pre_config != cur_config) { 
       if (cur_config == 1) { 
        if (dup2(file, STDOUT_FILENO) == -1) { 
         err(EXIT_MISC, NULL); 
        } 
       } else { 
        dup2(devNull, STDOUT_FILENO); 
       } 
       pre_config = cur_config; 
      } 
      if (passed->wait==0) { 
       passed->wait = 1; 
      } 
      sleep(1); 
     } 
    } else { 
     if (dup2(passed->fd, STDOUT_FILENO) == -1) { 
      err(EXIT_MISC, NULL); 
     } 
    } 
} 

Ну, я немного изменил код, чтобы вы, ребята, поняли, поэтому некоторые части не имеют смысла. Но вы получаете основную идею.

Как я могу перенаправить детское stdout как пожелаю после форкирования.

+0

AFAIK, процесс не может напрямую изменять потоки другого процесса. Родительскому процессу необходимо будет использовать механизм IPC, чтобы сообщить дочернему процессу, для чего перенаправляется новый файл, а сам процесс-потомк должен выполнить перенаправление. – kaylum

+0

Но начальное перенаправление работает, что означает, что ребенок действительно унаследовал родительскую таблицу fd. Но для меня непонятно, после первого звонка я не могу перенаправить выступление ребенка. – ivcode

+1

Это было * перед тем * вилка. То есть в то время был только один процесс. Как только вилка выполнена, есть два процесса, которые имеют одинаковые дескрипторы файлов. Но с этого момента они независимы. То есть вы можете настроить дескрипторы файлов, как вам нравится, прежде чем вилка и дочерний процесс после запуска получат эти дескрипторы файлов - точно так, как вы описали. Но родитель не может изменить дескрипторы дочерних файлов после вилки. – kaylum

ответ

1

Поскольку вы спросили, вот простой пример. Некоторые сокращения были приняты для краткости, но, надеюсь, это дает вам некоторую идею. Программа открывает файл1 и перенаправляет stdout в этот файл. Затем он делает вилку. Детский процесс записывает счетчик на stdout (через printf) каждые 1 секунду. Через несколько секунд родительский процесс использует IPC, pipe в этом примере, чтобы сообщить ребенку переключить файл перенаправления.

#include <stdio.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <string.h> 
#include <stdlib.h> 
#include <errno.h> 

int main(int argc, char **argv) 
{ 
    pid_t pid; 
    const char *file1 = "file1.txt"; 
    const char *file2 = "file2.txt"; 
    int pipefd[2]; 
    int fd; 
    int rval; 

    fd = open(file1, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU); 
    if (fd == -1) { 
     perror("file1 open"); 
     exit(-1); 
    } 

    /* 
    * This pipe will be used by parent process to tell child which file 
    * to redirect to. 
    */ 
    rval = pipe2(pipefd, O_NONBLOCK); 
    if (fd == -1) { 
     perror("pipe"); 
     exit(-1); 
    } 

    /* Redirect stdout to the file opened before the fork. */ 
    dup2(fd, STDOUT_FILENO); 

    pid = fork(); 

    if (pid == -1) { 
     perror("fork"); 
     exit(-1); 
    } else if (pid == 0) { 
     /* Child process. */ 
     int ix; 
     char redirect_file[100]; 

     close(pipefd[1]); 

     for (ix = 0; ix < 10; ix++) { 

      printf("%d\n", ix); 
      sleep(1); 

      rval = read(pipefd[0], redirect_file, sizeof(redirect_file)); 
      if (rval > 0) { 
       /* 
       * Parent process has written a filename to the pipe. 
       */ 
       fd = open(redirect_file, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU); 
       if (fd == -1) { 
        perror("file2 open"); 
        exit(-1); 
       } 

       /* Ensure previous output has been written to current file. */ 
       fflush(stdout); 

       /* Change redirect now. */ 
       dup2(fd, STDOUT_FILENO); 
      } 
     } 
    } else { 
     /* Parent process. */ 
     close(pipefd[0]); 

     /* Wait a little and then tell child to change redirect file. */ 
     sleep(5); 
     write(pipefd[1], file2, strlen(file2) + 1); 

     wait(); 
    } 
} 

Если эта программа запускается вы увидите, что половина выходной ребенок пошел в file1 (первый переориентировать) и другая половина продукции идет в file2 (второе перенаправление).

$ cat file1.txt 
0 
1 
2 
3 
4 
$ cat file2.txt 
5 
6 
7 
8 
9 

Последнее примечание. Примерная программа делает первый dup перед fork. Я сделал это так, потому что так показывался ваш код, а также подчеркивал до и после fork аспект проблемы. Но в реальном коде обычный способ сделать это - сделать fork сначала, затем dup и, наконец, exec. dup выполняется после fork, так что пострадает только дочерний процесс, а не родительский (если это действительно не то, что вы хотите).

+0

Спасибо @kaylum. Хотя это выглядит многообещающим, после вызова execvp() в forked-процессе я не могу придумать способ работы подпрограммы. Даже резьба не работает. – ivcode

+0

«Я не могу придумать способ работы подпрограммы». Не уверен, что вы подразумеваете под этим. Вы имеете в виду подпрограмму «dup»? В новом процессе должен быть записан этот код «dup». Если нет, то вы застряли. Как уже упоминалось, процесс не может заставить другой процесс изменять потоки без сотрудничества другого процесса. Можете ли вы изменить код для нового процесса, который является 'exec'ed? – kaylum

+0

Я только говорю, что во время вызова execvp() я не смогу выполнить "/ * Изменить перенаправление сейчас. * /' Dup2 (fd, STDOUT_FILENO); '", который вам удалось сделать в цикле. – ivcode

Смежные вопросы