C子进程的控制缓冲

问题描述 投票:0回答:1

我的目标是用execvp执行时控制子进程的缓冲。

更确切地说,我想将stdout和stderr重定向到相同的文件描述符(这是需要的行为,我无法更改)。但是,默认缓冲机制的工作方式会引发意外行为。例如,在您的终端中执行此python脚本时:

print("test sdout recup")
print("test stderr recup", file=sys.stderr)

stdout是行缓冲的,因此第一个打印立即被刷新(默认情况下,打印在python中添加了换行符)然后stderr是无缓冲的,因此直接提示导致:

 test sdout recup
 test stderr recup

当我用我的C代码执行相同的脚本时(请参见最后的代码),我一直都在得到:

test stderr recup
test sdout recup

由于stdout不是终端,(它是一个管道),stdout变得完全缓冲,而stderr仍然是非缓冲的,导致此顺序。

我需要一种方法来控制那些模式(从C,而不是通过另一个进程)以保留相同的终端输出,并且以后也要取消缓冲stdout(出于另一个目标),但是我真的不知道该怎么做。我已经看到一些使用文件指针而不是文件描述符(FD)的代码,但是我找不到该字段的相同功能。

而且,我什至不确定这是否可以从父进程中控制。所以我在这里。

以下是主要代码:output.h:

#include <stddef.h>//size_t
typedef struct Output
{
  char* out;
  int status;
  double times;
} Output;


Output* Output_new();
/*Return an correctly initialized Ouput in regard to the buffer size*/
size_t read_append_into_Output( int fd, Output* out, size_t* current_size );
/*Append the result in the output buffer and manage size properly(actualize constructor with new default size, prevent overflow...*/

executor.c:

#include "executor.h"
#include "argv.h"//buildarg

#include <unistd.h>//fork
#include <stdio.h>//pipe
#include <stdlib.h>//EXIT_SUCCESS
#include <sys/wait.h>
#include <string.h> //strlen
#include <errno.h>//perror


#define READ 0
#define WRITE 1

void child_life(char** argv){
    /*Do child stuff*/
    // char* expected[] = {"test.py", "test", NULL};
    execvp(*argv, argv);
    perror("Process creation failed");


}

//TODO better control over when to send in pipe
void parent_life(int read_fd, int write_fd, char** prompt, size_t prompt_number, Output* output){
    //inject prompt
    for (int i=0; i<prompt_number; i++){
        write(write_fd, prompt[i], strlen(prompt[i]));//TODO dont call strlen and control ourself the size?
    }
    size_t readed=0;
    size_t max_read=0;
    while (max_read==readed){//we stop when we read less what we should or error
        max_read= read_append_into_Output(read_fd, output,&readed);
    }
    output->out[readed]=0;
}

Output* executor_get_output(char* command, char** prompt, size_t prompt_number, double timout)
{

    Output* output=Output_new();
    int pipe_father[2];
    int pipe_son[2];

    pipe(pipe_father);
    pipe(pipe_son);

    pid_t cpid;

    int argc;
    char** argv= buildargv(command,&argc); // We do it here because code betwen fork and exec is dangerous (must not contain malloc for exemple)

    cpid = fork();



    if (cpid == 0) {    /* Child reads from pipe */
        /*Listening on father pipe*/
        close(pipe_father[WRITE]);          /* Close unused write end */
        dup2(pipe_father[READ], STDIN_FILENO); /*Replace STDIN by our pipe*/


        /*Redirecting stdout and stder to the write pipe*/
        close(pipe_son[READ]);          
        dup2(pipe_son[WRITE], STDOUT_FILENO); /*Replace STDOUT by our pipe*/
        dup2(pipe_son[WRITE], STDERR_FILENO);

        child_life( argv);

        //EXIT (executed only if exevp failed)
        close(pipe_father[READ]);
        close(pipe_son[WRITE]);
        _exit(EXIT_FAILURE);

    } 
    //Parent code
    close(pipe_father[READ]);          /* Close unused read end */
    close(pipe_son[WRITE]);
    parent_life( pipe_son[READ], pipe_father[WRITE], prompt, prompt_number, output);

    //EXIT
    close(pipe_father[WRITE]);          /* Reader will see EOF */
    waitpid(cpid, NULL,0);                /* Wait for child terminaison*/

    close (pipe_son[READ]);
    return output;


}

您可以在github上找到一个方便的构建,用于编译未显示的代码依赖关系,如果需要,还可以进行测试:

git clone -b dev https://github.com/crazyhouse33/authbreak.git
cd authbreak/build
cmake ..
make executor

此命令在bin / tests目录中创建二进制文件。

相关的测试源代码在tests / execution / executor.c中

测试运行了这样一个显示的python脚本,并将获得的输出与我已经给出的期望进行比较。由于某种原因,测试段从make test(ctest)运行时才运行,但是现在您手动运行时。

谢谢。

c child-process output-buffering
1个回答
0
投票

由于子程序是一个单独的程序,它会缓冲其希望的状态,因此您需要告诉它不要缓冲或更改程序。

由于您的孩子似乎是python脚本,您可以通过设置PYTHONUNBUFFERED环境变量使其工作,但这仅适用于python:

putenv("PYTHONUNBUFFERED=1");
© www.soinside.com 2019 - 2024. All rights reserved.