C子进程的控制缓冲 [英] C Control buffering of Child process

查看:69
本文介绍了C子进程的控制缓冲的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的目标是控制通过execvp执行时子进程的缓冲.

My goal is to control buffering of child process when executed with execvp.

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

More precisely, I want to redirect stdout and stderr to the same file descriptor (this is wanted behaviour, I cannot change that). But, how the default buffering mechanism work provokes unexpected behaviour. For example, when this python script is executed in your terminal:

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

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

stdout is line buffered, so the first print is instantly flushed (print adds by default a newline in python) then stderr is unbuffered, and thus directly prompted which lead to:

 test sdout recup
 test stderr recup

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

When I execute the same script with my C code (see the code in the end), I got all the time:

test stderr recup
test sdout recup

由于stdout不是终端(它是一个管道),因此stdout变为完全缓冲状态,而stderr仍为非缓冲状态,从而导致此顺序.

Since stdout is not a terminal, (it's a pipe), stdout become fully buffered, while stderr is still unbuffered, leading to this order.

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

I need a way to control those modes (from C, not via another process) to preserve the same terminal output, and also later to unbuffer stdout as well (for another goal) but I really don't know how to do. I have seen some code that work with files pointer instead of file descriptors (FD) but I cannot find same functions for the field.

此外,我什至不能确定这是可以从父进程控制的.所以我在这里.

Also, I am not even sure that this is controllable from the parent process. So here I am.

这是主要代码:output.h:

Here is the main code: 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:

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上找到一个方便的构建,用于编译未显示的代码依赖性,并在需要时进行测试:

You can find on github a convenient build to compile not shown dependencies of the code you saw plus a test to mess around if you want:

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

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

This commands create the binary in bin/tests directory.

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

The associated test source code is in tests/execution/executor.c

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

The test runs an execution of such a showed python script and compare the obtained output with my already presented expectations. For some reason the test segfault when ran from make test (ctest) but now when you run it manually.

推荐答案

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

my goal is to control buffering of child process when executed with execvp

因此,基本上,您想复制 stdbuf .

So basically you want to replicate the job of stdbuf.

coreutils stdbuf 来源它表明stdbuf只是设置环境变量<代码> _STDBUF_I = $ MODE <代码> _STDBUF_O = $ MODE 和 _STDBUF_E = $ MODE ,然后再选择 libstdbuf 是然后在子进程开始执行之前立即执行-它读取环境变量 _STDBUF_I _STDBUF_O _STDBUF_E ,并且仅应用从子进程中的环境变量.

Fast screening at coreutils stdbuf sources it shows that stdbuf just sets the environment variables _STDBUF_I=$MODE _STDBUF_O=$MODE and _STDBUF_E=$MODE and then ld preloads the libstdbuf.so library. The libstdbuf is then executed inside the childs process right before it starts - it reads the environment variables _STDBUF_I _STDBUF_O and _STDBUF_E and just applies the proper buffering mode obtained from environment variables inside the childs process.

您似乎想重新发明 stdbuf ,只需执行相同的操作即可.(或者,因为您的问题在我看来确实像XY问题,只需使用 stdbuf .在外壳中通常在管道中使用 stdbuf -oL ...).

As you seem to want to reinvent stdbuf, just do the same as it does. (Or, as your question really seems to me like an XY problem, just use stdbuf. It's typical in the shell to use stdbuf -oL in pipes...).

这篇关于C子进程的控制缓冲的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆