【问题标题】:C - Redirecting stdout after forking of child from parentC - 从父级分叉子级后重定向标准输出
【发布时间】:2015-12-18 03:30:01
【问题描述】:

我正在编写一个程序来执行另一个程序作为分叉进程,并将其输出重定向到文件或 /dev/null 按需。

目前我已经使用 execvp() 分叉并执行了外部程序。 然后从分叉之前创建的线程重定向标准输出,因为分叉的进程将继承父文件描述符表,允许我在分叉后重定向。

但是,我最初可以将标准输出重定向到所需的文件,并且父母和孩子的标准输出都被重定向。但是,如果我尝试再次将其重定向到另一个文件,则仅重定向父母的标准输出,孩子的标准输出保持不变。

这是没有所有错误检查位的代码。

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);
        }
    }
}

好吧,我稍微更改了代码以便你们理解,所以有些部分没有意义。但是你明白了基本的想法。

如何在分叉后按照我的意愿重定向孩子的标准输出。

【问题讨论】:

  • AFAIK,一个进程不能直接改变另一个进程的流。父进程将需要使用 IPC 机制来告诉子进程要重定向到哪个新文件,而子进程本身需要进行重定向。
  • 但是初始重定向有效,这意味着孩子确实继承了父母的fd表。但我不清楚的是,在初次通话后,我无法重定向孩子的标准输出。
  • 那是分叉之前。也就是说,当时只有一个进程。一旦分叉完成,就会有两个进程具有相同的文件描述符。但从那时起,它们就独立了。也就是说,您可以在 fork 之前设置文件描述符,并且子进程一旦启动就会获得这些文件描述符 - 正如您所描述的那样。但是在fork之后,父级没有办法改变子文件描述符。
  • 谢谢@kaylum,你是对的。这很有帮助。上面提到的 IPC 实施的一个小例子现在是黄金 :)
  • 在贴出的代码中的某些地方调用了函数:err()。在代码的其他地方,使用了变量err。让变量名与系统函数相同是一种非常糟糕的编程习惯。强烈建议将变量 err 更改为唯一名称

标签: c redirect stdout


【解决方案1】:

既然你问了,这里是一个简单的例子。为简洁起见,我们采用了一些捷径,但希望它能给您一些想法。程序打开 file1 并将标准输出重定向到该文件。然后它会分叉。子进程每 1 秒向标准输出写入一个计数器(通过printf)。几秒钟后,父进程使用 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

最后一点。示例程序在fork 之前执行第一个dup。我这样做是因为这就是您的代码的显示方式,也是为了强调问题的前后fork 方面。但在实际代码中,执行此操作的常规方法是先执行fork,然后执行dup,最后执行execdupfork 之后完成,因此只有子进程而不是父进程受到影响(除非这确实是您想要的)。

【讨论】:

  • 谢谢@kaylum。尽管这看起来很有希望,但在分叉进程中调用 execvp() 之后,我想不出一种方法来保持子例程工作。连线程都行不通。
  • “我想不出办法让子程序继续工作”。不知道你的意思是什么。您是指执行dup 的子程序吗?新进程需要将dup 代码写入其中。如果没有,那么你被卡住了。如前所述,一个进程不能强迫另一个进程在没有其他进程合作的情况下更改流。您能否更改 execed 新进程的代码?
  • 我只是说,在 execvp() 调用期间,我将无法执行“/* 现在更改重定向。*/ dup2(fd, STDOUT_FILENO);” 你已经设法在循环中执行。
猜你喜欢
  • 2023-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多