Pat*_*ryk 26
正如已经回答的那样,popen在一个方向上工作.如果需要读写,可以使用pipe()创建管道,通过fork()和exec函数跨越一个新进程,然后使用dup2()重定向其输入和输出.无论如何我更喜欢exec而不是popen,因为它可以让你更好地控制这个过程(例如你知道它的pid)
编辑:
正如评论所建议的那样,管道只能在一个方向上使用.因此,您必须创建单独的管道进行读写.由于之前发布的示例是错误的,我删除了它并创建了一个新的,正确的:
#include<unistd.h>
#include<sys/wait.h>
#include<sys/prctl.h>
#include<signal.h>
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
int main(int argc, char** argv)
{
pid_t pid = 0;
int inpipefd[2];
int outpipefd[2];
char buf[256];
char msg[256];
int status;
pipe(inpipefd);
pipe(outpipefd);
pid = fork();
if (pid == 0)
{
// Child
dup2(outpipefd[0], STDIN_FILENO);
dup2(inpipefd[1], STDOUT_FILENO);
dup2(inpipefd[1], STDERR_FILENO);
//ask kernel to deliver SIGTERM in case the parent dies
prctl(PR_SET_PDEATHSIG, SIGTERM);
//replace tee with your process
execl("/usr/bin/tee", "tee", (char*) NULL);
// Nothing below this line should be executed by child process. If so,
// it means that the execl function wasn't successfull, so lets exit:
exit(1);
}
// The code below will be executed only by parent. You can write and read
// from the child using pipefd descriptors, and you can send signals to
// the process using its pid by kill() function. If the child process will
// exit unexpectedly, the parent process will obtain SIGCHLD signal that
// can be handled (e.g. you can respawn the child process).
//close unused pipe ends
close(outpipefd[0]);
close(inpipefd[1]);
// Now, you can write to outpipefd[1] and read from inpipefd[0] :
while(1)
{
printf("Enter message to send\n");
scanf("%s", msg);
if(strcmp(msg, "exit") == 0) break;
write(outpipefd[1], msg, strlen(msg));
read(inpipefd[0], buf, 256);
printf("Received answer: %s\n", buf);
}
kill(pid, SIGKILL); //send SIGKILL signal to the child process
waitpid(pid, &status, 0);
}
Run Code Online (Sandbox Code Playgroud)
你想要一些经常被称为 popen2 的东西。这是一个没有错误检查的基本实现(通过网络搜索找到,而不是我的代码):
// http://media.unpythonic.net/emergent-files/01108826729/popen2.c
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include "popen2.h"
int popen2(const char *cmdline, struct popen2 *childinfo) {
pid_t p;
int pipe_stdin[2], pipe_stdout[2];
if(pipe(pipe_stdin)) return -1;
if(pipe(pipe_stdout)) return -1;
//printf("pipe_stdin[0] = %d, pipe_stdin[1] = %d\n", pipe_stdin[0], pipe_stdin[1]);
//printf("pipe_stdout[0] = %d, pipe_stdout[1] = %d\n", pipe_stdout[0], pipe_stdout[1]);
p = fork();
if(p < 0) return p; /* Fork failed */
if(p == 0) { /* child */
close(pipe_stdin[1]);
dup2(pipe_stdin[0], 0);
close(pipe_stdout[0]);
dup2(pipe_stdout[1], 1);
execl("/bin/sh", "sh", "-c", cmdline, NULL);
perror("execl"); exit(99);
}
childinfo->child_pid = p;
childinfo->to_child = pipe_stdin[1];
childinfo->from_child = pipe_stdout[0];
close(pipe_stdin[0]);
close(pipe_stdout[1]);
return 0;
}
//#define TESTING
#ifdef TESTING
int main(void) {
char buf[1000];
struct popen2 kid;
popen2("tr a-z A-Z", &kid);
write(kid.to_child, "testing\n", 8);
close(kid.to_child);
memset(buf, 0, 1000);
read(kid.from_child, buf, 1000);
printf("kill(%d, 0) -> %d\n", kid.child_pid, kill(kid.child_pid, 0));
printf("from child: %s", buf);
printf("waitpid() -> %d\n", waitpid(kid.child_pid, NULL, 0));
printf("kill(%d, 0) -> %d\n", kid.child_pid, kill(kid.child_pid, 0));
return 0;
}
#endif
Run Code Online (Sandbox Code Playgroud)
原因popen()和朋友不提供双向通信是因为子进程中的缓冲会导致死锁.socketpair()在答案中讨论的所有临时管道和解决方案都存在同样的问题.
在UNIX下,大多数命令都不可信任读取一行并立即处理并打印它,除非它们的标准输出是tty.原因是默认情况下stdio缓冲用户空间中的输出,并推迟write()系统调用,直到缓冲区已满或stdio流关闭(通常是因为程序或脚本在输入时看到EOF后即将退出).如果你通过管道写入这样一个程序的stdin,现在等待该程序的stdout的答案(不关闭入口管道),答案就会停留在stdio缓冲区中,永远不会出现 - 这是一个死锁.
你可以grep通过使用伪tty与它们交谈来欺骗一些面向行的程序(例如)到不缓冲; 看看libexpect(3).但是在一般情况下,您必须为每条消息重新运行不同的子进程,允许使用EOF来指示每条消息的结束并导致命令(或命令管道)中的任何缓冲区被刷新.表现明显不是好事.
在perlipc手册页中查看有关此问题的更多信息(它适用于Perl中的双向管道,但无论主程序使用何种语言,缓冲注意事项都适用).
| 归档时间: |
|
| 查看次数: |
21015 次 |
| 最近记录: |