C控制子进程的缓冲

C控制子进程的缓冲,c,child-process,output-buffering,C,Child Process,Output Buffering,我的目标是在使用execvp执行时控制子进程的缓冲 更准确地说,我想将stdout和stderr重定向到同一个文件描述符(这是需要的行为,我无法更改)。但是,默认缓冲机制的工作方式会引发意外行为。例如,在终端中执行此python脚本时: print("test sdout recup") print("test stderr recup", file=sys.stderr) stdout是行缓冲的,因此第一次打印会立即刷新(print在python中默认

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

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

print("test sdout recup")
print("test stderr recup", file=sys.stderr)
stdout是行缓冲的,因此第一次打印会立即刷新(print在python中默认添加一个换行符) 然后stderr被取消缓冲,因此直接提示导致:

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

test stderr recup
test sdout recup
由于stdout不是一个终端(它是一个管道),stdout被完全缓冲,而stderr仍然是无缓冲的,这导致了这个命令

我需要一种方法来控制这些模式(从C开始,而不是通过另一个进程),以保持相同的终端输出,并且稍后还要取消缓冲stdout(为了另一个目标),但我真的不知道怎么做。我见过一些使用文件指针而不是文件描述符(FD)的代码,但我找不到该字段的相同函数

此外,我甚至不确定这是否可以从父进程控制。我来了

以下是主要代码: 产出.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...*/
此命令在bin/tests目录中创建二进制文件

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


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

由于子程序是一个单独的程序,它将按自己的意愿进行缓冲,因此您需要告诉它不要缓冲或更改程序

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

putenv("PYTHONUNBUFFERED=1");

由于子程序是一个单独的程序,它将按自己的意愿缓冲,因此您需要告诉它不要缓冲或更改程序

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

putenv("PYTHONUNBUFFERED=1");
我的目标是在使用execvp执行时控制子进程的缓冲

所以基本上你想复制你的工作

它的快速筛选显示stdbuf刚刚设置,然后。然后在Child进程开始之前在其内部执行-它读取环境变量
\u STDBUF\u I
\u STDBUF\u O
\u STDBUF\u E
,并仅应用从Child进程内部的环境变量获得的适当缓冲模式

正如您似乎想要重新发明stdbuf一样,只需按原样操作即可。(或者,在我看来,您的问题确实像一个XY问题,只需使用
stdbuf
。在shell中,在管道中使用
stdbuf-oL
是很典型的…)

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

所以基本上你想复制你的工作

它的快速筛选显示stdbuf刚刚设置,然后。然后在Child进程开始之前在其内部执行-它读取环境变量
\u STDBUF\u I
\u STDBUF\u O
\u STDBUF\u E
,并仅应用从Child进程内部的环境变量获得的适当缓冲模式


正如您似乎想要重新发明stdbuf一样,只需按原样操作即可。(或者,你的问题在我看来确实像一个XY问题,只需使用
stdbuf
。在shell中,在管道中使用
stdbuf-oL
是很典型的…。

如果有人遇到同样的问题。从父代码执行任何操作似乎都是不可能的。我尝试将管道作为流打开(fdopen),并在child和parents中的每个管道末端调用setvbuf。它默默地失败了

所以我猜无论你在execve之前做什么,加载程序都会再次设置stdin和stdout缓冲,并删除你的设置。所以我做了一个C模块,专门用来处理envp和argv,以便使用环境变量技巧,比如KamilCuk从C提出的stdbuf解决方案。 以下是如何使用其中的一个子集来回答此问题:

#include "argv.h"
#include <unistd.h>


void execvp_buffer_control( char* command, char** argv, char** buffering_mode){
//Like excevp but offer control over child buffering
char **stdbuf_trick_envp = build_stdbuf_exec_envp(buffering_mode);
execve(command, argv,get_envp_appended(stdbuf_trick_envp));// This return an envp vector equal to current envp concatenated with the good stuff to use stdbuff trick 
}
//Where buffering_mode is something as below:
char *mode[] = {DEFAULT_BUFFERING, LINE_BUFFERED, "65536"};# 65536 is the size of the fully buffered stderr stream buffer 
#包括“argv.h”
#包括
void execvp_buffer_控件(char*命令、char**argv、char**buffering_模式){
//与excevp类似,但提供对子缓冲的控制
char**stdbuf\u trick\u envp=build\u stdbuf\u exec\u envp(缓冲模式);
execve(command,argv,get_envp_added(stdbuf_trick_envp));//返回一个等于当前envp的envp向量,该向量与使用stdbuff技巧的好东西连接在一起
}
//其中缓冲模式如下所示:
字符*模式[]={DEFAULT_BUFFERING,LINE_BUFFERED,“65536”};#65536是完全缓冲的stderr流缓冲区的大小
现在,如果编译这段代码,并在构建时将变量LIBSTDBUF_PATH定义为stdbuflib的路径,它将按预期工作。如果您不设置它,它将在编译时发出警告,并且您的execvp\u buffer\u控件将充当基本execvp

我创建这个模块时考虑到了灵活性。它包含一个函数,用于将诸如stdbuf之类的envp技巧与其他也使用LD_PRELOAD的技巧合并,以便在某些envp变量发生冲突时混合功能。我希望看到人们将request me拉入他们自己的函数,创建一个适当的envp向量,比如build_stdbuf_exec_envp,这样这个模块对于以可组合的方式调整子进程的行为非常有用

一切都经过测试。我可能会单独为模块创建一个存储库,但现在它位于第一个发布的存储库中,源代码位于src/common文件中。Argv.h使用unistd.h检索当前环境。但是如果你用一个crossplaform函数替换它
#include "argv.h"
#include <unistd.h>


void execvp_buffer_control( char* command, char** argv, char** buffering_mode){
//Like excevp but offer control over child buffering
char **stdbuf_trick_envp = build_stdbuf_exec_envp(buffering_mode);
execve(command, argv,get_envp_appended(stdbuf_trick_envp));// This return an envp vector equal to current envp concatenated with the good stuff to use stdbuff trick 
}
//Where buffering_mode is something as below:
char *mode[] = {DEFAULT_BUFFERING, LINE_BUFFERED, "65536"};# 65536 is the size of the fully buffered stderr stream buffer