将 child 的输出写入 shell 和文件 error.log
Write the output of the child in shell and in file error.log
我尝试将 child 的输出通过管道传输到一个文件中,然后由父亲写入。
#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);
}
}
目前我只尝试将 child 的输出通过管道传递给父亲,然后将其写入文件。
我的最终目标也是将其显示到终端。我的想法是使用 3 个管道并重定向我们在代码中可以看到的第一个管道,作为第二个和第三个管道的输入。然后
使用 dup2(pipe2[1],file1) 重定向第二个管道的输出,使用 dup2(pipe2[1], stdout).
重定向第三个管道的输出
程序设计
parent 进程将不得不从 child 读取响应,并安排将该信息写入两次,一次写入日志文件,一次写入终端。或者您将不得不安排 child 的输出转到像 tee
这样的程序,该程序将其输入的副本写入多个目的地。
除非您使用一个管道将 child 的输出重定向到 tee
。
,否则更多的管道将无济于事
正在关闭管道文件描述符
您在 child 进程中没有关闭足够的文件描述符。
经验法则:如果你
dup2()
管道的一端连接到标准输入或标准输出,同时关闭
返回的原始文件描述符
pipe()
尽早。
特别是,您应该在使用任何
exec*()
函数族。
如果您使用以下任一方式复制描述符,则该规则也适用
dup()
或者
fcntl()
F_DUPFD
或 F_DUPFD_CLOEXEC
.
如果 parent 进程不会通过以下方式与它的任何 children 通信
管道,它必须确保尽早关闭管道的两端
足够(例如,在等待之前)以便其 children 可以接收
EOF 指示读取(或获取 SIGPIPE 信号或写入错误
写),而不是无限期地阻塞。
即使 parent 使用管道而不使用 dup2()
,它也应该
通常至少关闭管道的一端——这种情况极为罕见
在单个管道的两端读写的程序。
请注意 O_CLOEXEC
选项
open()
,
并且 fcntl()
的 FD_CLOEXEC
和 F_DUPFD_CLOEXEC
选项也可以考虑因素
进入这个讨论。
如果你使用
posix_spawn()
及其广泛的支持功能系列(总共 21 个功能),
您将需要查看如何在生成的进程中关闭文件描述符
(posix_spawn_file_actions_addclose()
,
等)。
请注意,使用 dup2(a, b)
比使用 close(b); dup(a);
更安全
由于各种原因。
一个是如果你想强制文件描述符大于
通常的数字,dup2()
是唯一明智的方法。
另一个是如果 a
与 b
相同(例如两者都是 0
),则 dup2()
正确处理它(它在复制 a
之前不会关闭 b
)
而单独的 close()
和 dup()
则失败得很厉害。
这种情况不太可能,但并非不可能。
足够固定的代码
这是您程序的固定版本。 log_stderr()
中的逻辑很奇怪;它没有做的一件事是写入标准错误。我已经修改了它,所以它确实从管道读取并写入标准输出和日志文件。请注意,复制信息需要一次读取,两次写入。错误检查不足 non-existent。 bury()
函数现在报告 child 死亡。代码保存在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;
}
但是,它确实有效:
$ 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
$
我尝试将 child 的输出通过管道传输到一个文件中,然后由父亲写入。
#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);
}
}
目前我只尝试将 child 的输出通过管道传递给父亲,然后将其写入文件。 我的最终目标也是将其显示到终端。我的想法是使用 3 个管道并重定向我们在代码中可以看到的第一个管道,作为第二个和第三个管道的输入。然后 使用 dup2(pipe2[1],file1) 重定向第二个管道的输出,使用 dup2(pipe2[1], stdout).
重定向第三个管道的输出程序设计
parent 进程将不得不从 child 读取响应,并安排将该信息写入两次,一次写入日志文件,一次写入终端。或者您将不得不安排 child 的输出转到像 tee
这样的程序,该程序将其输入的副本写入多个目的地。
除非您使用一个管道将 child 的输出重定向到 tee
。
正在关闭管道文件描述符
您在 child 进程中没有关闭足够的文件描述符。
经验法则:如果你
dup2()
管道的一端连接到标准输入或标准输出,同时关闭
返回的原始文件描述符
pipe()
尽早。
特别是,您应该在使用任何
exec*()
函数族。
如果您使用以下任一方式复制描述符,则该规则也适用
dup()
或者
fcntl()
F_DUPFD
或 F_DUPFD_CLOEXEC
.
如果 parent 进程不会通过以下方式与它的任何 children 通信
管道,它必须确保尽早关闭管道的两端
足够(例如,在等待之前)以便其 children 可以接收
EOF 指示读取(或获取 SIGPIPE 信号或写入错误
写),而不是无限期地阻塞。
即使 parent 使用管道而不使用 dup2()
,它也应该
通常至少关闭管道的一端——这种情况极为罕见
在单个管道的两端读写的程序。
请注意 O_CLOEXEC
选项
open()
,
并且 fcntl()
的 FD_CLOEXEC
和 F_DUPFD_CLOEXEC
选项也可以考虑因素
进入这个讨论。
如果你使用
posix_spawn()
及其广泛的支持功能系列(总共 21 个功能),
您将需要查看如何在生成的进程中关闭文件描述符
(posix_spawn_file_actions_addclose()
,
等)。
请注意,使用 dup2(a, b)
比使用 close(b); dup(a);
更安全
由于各种原因。
一个是如果你想强制文件描述符大于
通常的数字,dup2()
是唯一明智的方法。
另一个是如果 a
与 b
相同(例如两者都是 0
),则 dup2()
正确处理它(它在复制 a
之前不会关闭 b
)
而单独的 close()
和 dup()
则失败得很厉害。
这种情况不太可能,但并非不可能。
足够固定的代码
这是您程序的固定版本。 log_stderr()
中的逻辑很奇怪;它没有做的一件事是写入标准错误。我已经修改了它,所以它确实从管道读取并写入标准输出和日志文件。请注意,复制信息需要一次读取,两次写入。错误检查不足 non-existent。 bury()
函数现在报告 child 死亡。代码保存在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;
}
但是,它确实有效:
$ 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
$