C 在shell和error.log文件中写入子级的输出
我试图通过管道将孩子的输出传输到一个文件中,并由父亲编写C 在shell和error.log文件中写入子级的输出,c,pipe,fork,C,Pipe,Fork,我试图通过管道将孩子的输出传输到一个文件中,并由父亲编写 #include <stdio.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <stdbool.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <sys
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int prepare_log(void){
int fd = open("error.log",
O_CREAT | O_RDWR | O_APPEND,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH );
return fd;
}
void log_stderr(int pipe_ends[], int outfile){
close(pipe_ends[1]);
dup2(pipe_ends[0],outfile);
close(pipe_ends[0]);
}
void child(int pipe_ends[], char* argv[]){
close(pipe_ends[0]);
dup2(pipe_ends[1],1);
close(pipe_ends[1]);
execvp(argv[0], argv);
}
void bury(){
int status;
wait(0);
}
int main(){
int fd = prepare_log();
char* argv[] = {"seq", "10", NULL};
int pipe1[2];
if(pipe(pipe1) == -1){
printf("Dont' create Pipe\n");
exit(EXIT_FAILURE);
}
pid_t pid = fork();
if(pid < 0){
perror("ERROR");
exit(EXIT_FAILURE);
} else if(pid > 0){
log_stderr(pipe1, fd);
bury();
} else if (pid == 0){
child(pipe1,argv);
}
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
int准备日志(无效){
int fd=open(“error.log”,
O|u CREAT | O|RDWR | O|u APPEND,
S|IRUSR | S|IWUSR | S|IWGRP | S|IWGRP | S|IROTH | S|IWOTH);
返回fd;
}
无效日志文件(内部管道末端[],内部输出文件){
关闭(管道末端[1]);
dup2(管端[0],输出文件);
关闭(管道末端[0]);
}
无效子项(int pipe_end[],char*argv[]){
关闭(管道末端[0]);
dup2(管端[1],1);
关闭(管道末端[1]);
execvp(argv[0],argv);
}
void bury(){
智力状态;
等待(0);
}
int main(){
int fd=准备日志();
char*argv[]={“seq”,“10”,NULL};
int-pipe1[2];
如果(管道(管道1)=-1){
printf(“不创建管道”);
退出(退出失败);
}
pid_t pid=fork();
if(pid<0){
佩罗(“错误”);
退出(退出失败);
}否则,如果(pid>0){
日志文件(管道1,fd);
bury();
}否则如果(pid==0){
儿童(pipe1,argv);
}
}
目前,我只尝试通过管道将child的输出传递给父亲,然后将其写入文件。
我的最终目标也是向终端显示它。我的想法是使用3个管道并重定向我们在代码中看到的第一个管道,作为第2个和第3个管道的输入。然后
用dup2(pipe2[1],文件1)重定向第二个管道的输出,用dup2(pipe2[1],标准输出)重定向第三个管道的输出。程序设计
父进程必须从子进程读取响应,并安排将该信息写入两次,一次写入日志文件,一次写入终端。或者,您必须将子对象的输出安排到一个类似tee
的程序,该程序将其输入的副本写入多个目的地
除非您使用一个管道将子管道的输出重定向到tee
,否则更多管道将没有帮助
关闭管道文件描述符
子进程中没有关闭足够的文件描述符
经验法则:如果 将管道的一端连接到标准输入或标准输出,关闭两个 返回的原始文件描述符 尽快。 特别是,在使用任何 函数族 如果将描述符与 或 使用
F_DUPFD
或F_DUPFD\u CLOEXEC
如果父进程不通过 在安装管道时,必须确保管道两端提前关闭 足够的(例如,在等待之前)让它的孩子可以接受 读取(或获取信号管信号或写入错误)时的EOF指示 写入),而不是无限期地阻塞。 即使父级使用管道而不使用
dup2()
,它也应该
通常情况下,至少关闭管道的一端-这是非常罕见的
在单个管道两端读写的程序
请注意,O_CLOEXEC
选项
,
而fcntl()
的FD\u CLOEXEC
和F\u DUPFD\u CLOEXEC
选项也可以考虑
我开始讨论这个问题
如果你使用
及其广泛的支持功能系列(共21项功能),
您将需要查看如何在生成的进程中关闭文件描述符
(,
等等)
请注意,使用dup2(a,b)
比使用close(b)更安全;dup(a)代码>
由于种种原因。
一个是,如果要强制文件描述符的大小大于
通常的数字,dup2()
是唯一可行的方法。
另一个是,如果a
与b
相同(例如,两者都0
),则dup2()
正确处理它(在复制a
之前,它不会关闭b
)
而单独的close()
和dup()
却失败得很厉害。
这不太可能,但并非不可能
充分固定码
这是您的程序的固定版本。log\u stderr()
中的逻辑是奇数;它不做的一件事是写入标准错误。我对它进行了修改,这样它就可以从管道中读取并写入标准输出和日志文件。请注意,复制信息需要一次读取,但需要两次写入。错误检查很简单,几乎不存在。bury()
函数现在报告儿童死亡。代码保存在pipe71.c
中,并编译为pipe71
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
static
int prepare_log(void)
{
int fd = open("error.log",
O_CREAT | O_RDWR | O_APPEND,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH );
return fd;
}
static
void log_stderr(int pipe_ends[], int outfile)
{
char buffer[512];
int nbytes;
close(pipe_ends[1]);
while ((nbytes = read(pipe_ends[0], buffer, sizeof(buffer))) > 0)
{
write(STDOUT_FILENO, buffer, nbytes);
write(outfile, buffer, nbytes);
}
close(pipe_ends[0]);
close(outfile);
}
static
void child(int pipe_ends[], char *argv[])
{
dup2(pipe_ends[1], 1);
close(pipe_ends[0]);
close(pipe_ends[1]);
execvp(argv[0], argv);
fprintf(stderr, "failed to execute %s\n", argv[0]);
exit(EXIT_FAILURE);
}
static
void bury(void)
{
int status;
int corpse;
while ((corpse = wait(&status)) > 0)
printf("%d: child %d exited with status 0x%.4X\n", (int)getpid(), corpse, status);
}
int main(void)
{
int fd = prepare_log();
char *argv[] = {"seq", "10", NULL};
int pipe1[2];
pipe(pipe1);
pid_t pid = fork();
if (pid > 0)
{
log_stderr(pipe1, fd);
bury();
}
else if (pid == 0)
{
child(pipe1, argv);
}
return 0;
}
您没有在子项中关闭足够的文件描述符。经验法则:如果将管道的一端连接到标准输入或标准输出,请尽快从pipe()
关闭两个原始文件描述符。特别是,这意味着在使用任何函数族之前。该规则也适用于dup()
或F_DUPFD
。进程是无性别的。s/father/parent/这是否意味着我应该关闭管道的两端bevor runn the exec in child,但只关闭父管道的write and,这样他仍然可以读取?是的,这确实意味着您需要同时关闭(pipe_ends[0])代码>和关闭(管道末端[1])代码>在execv()之前
。通常,如果不关闭所有未使用的描述符,则在检测EOF时可能会遇到问题。在这种情况下,您可能还可以;你可能不会。但是草率的编码使得调试程序为什么挂起变得很困难,这是管道关闭不当的常见症状需要当前已注释掉的代码>。将管道的读取端连接到指定为outfile
的文件描述符似乎很奇怪。这将导致混淆,部分原因是这并不意味着写入管道的信息将自动写入输出文件,部分原因是仅对输出文件执行读取操作<
$ rm error.log
$ ./pipe71
1
2
3
4
5
6
7
8
9
10
99989: child 99990 exited with status 0x0000
$ cat error.log
1
2
3
4
5
6
7
8
9
10
$