읽기 및 쓰기를 동시에 열기
popen에서 반환된 파일 설명자를 읽고 쓸 수 있습니까?C를 통해 제어하고 싶은 대화형 프로세스가 있습니다.만약 포펜으로 이것이 불가능하다면, 그것을 피할 방법이 없을까요?
이미 답변했듯이, popen은 한 방향으로 작동합니다.읽고 써야 하는 경우 파이프()로 파이프를 만들고 포크() 및 exec 함수로 새 프로세스를 확장한 다음 dup2()로 입력 및 출력을 리디렉션할 수 있습니다.어쨌든 나는 오픈보다 exec을 더 선호합니다. 왜냐하면 그것은 당신에게 프로세스를 더 잘 제어할 수 있기 때문입니다(예: 당신은 그것의 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);
//close unused pipe ends
close(outpipefd[1]);
close(inpipefd[0]);
//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);
}
당신은 종종 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
ㅠㅠpopen()
친구들은 양방향 커뮤니케이션을 제공하지 않습니다. 하위 프로세스의 버퍼링으로 인해 교착 상태에 빠지기 쉽기 때문입니다.모든 임시 파이프 구조와socketpair()
답변에서 논의된 해결책은 동일한 문제로 어려움을 겪고 있습니다.
UNIX에서는 표준 출력이 tatty인 경우를 제외하고 대부분의 명령이 한 줄을 읽고 즉시 처리하여 인쇄할 수 없습니다.하고 "stdio"라는 단어를 입니다.write()
버퍼가 가득 차거나 stdio 스트림이 닫힐 때까지 시스템 호출(일반적으로 프로그램 또는 스크립트가 입력에서 EOF를 확인한 후 종료하려고 하기 때문).파이프를 통해 해당 프로그램의 stdin에 글을 쓰고 이제 해당 프로그램의 stdout에서 답변을 기다리면(입력 파이프를 닫지 않고) stdio 버퍼에 해당 답변이 고착되어 절대 나오지 않습니다. 이것은 교착 상태입니다.
라인 을 속일 수 예:grep
) 를 사용하여 대화를 함으로써 버퍼링을 하지 않도록 합니다. 를 확인하십시오. 그러나 일반적인 경우 EOF를 사용하여 각 메시지의 끝을 알리고 명령어(또는 명령어 파이프라인)의 버퍼를 플러시할 수 있도록 하여 각 메시지에 대해 다른 하위 프로세스를 다시 실행해야 합니다.분명히 성과 면에서 좋은 것은 아닙니다.
perlipc man 페이지에서 이 문제에 대한 자세한 내용을 확인하십시오(Perl의 양방향 파이프에 대한 것이지만 버퍼링 고려 사항은 기본 프로그램에 사용되는 언어에 관계없이 적용됨).
popen()
파이프는 읽기 또는 쓰기 모드에서만 열 수 있으며 둘 다 열 수는 없습니다.해결 방법은 이 스레드를 참조하십시오.
netresolve 백엔드 중 하나에서 스크립트와 대화 중이므로 스크립트에 글을 써야 합니다.stdin
그리고 그것으로부터 읽습니다.stdout
다음 함수는 파이프로 리디렉션된 stdin 및 stdout을 사용하여 명령을 실행합니다.당신은 그것을 사용하고 당신의 취향에 맞게 조정할 수 있습니다.
static bool
start_subprocess(char *const command[], int *pid, int *infd, int *outfd)
{
int p1[2], p2[2];
if (!pid || !infd || !outfd)
return false;
if (pipe(p1) == -1)
goto err_pipe1;
if (pipe(p2) == -1)
goto err_pipe2;
if ((*pid = fork()) == -1)
goto err_fork;
if (*pid) {
/* Parent process. */
*infd = p1[1];
*outfd = p2[0];
close(p1[0]);
close(p2[1]);
return true;
} else {
/* Child process. */
dup2(p1[0], 0);
dup2(p2[1], 1);
close(p1[0]);
close(p1[1]);
close(p2[0]);
close(p2[1]);
execvp(*command, command);
/* Error occured. */
fprintf(stderr, "error running %s: %s", *command, strerror(errno));
abort();
}
err_fork:
close(p2[1]);
close(p2[0]);
err_pipe2:
close(p1[1]);
close(p1[0]);
err_pipe1:
return false;
}
https://github.com/crossdistro/netresolve/blob/master/backends/exec.c#L46
(Can popen() make 양방향 pipe like pipe() + fork()?에서 동일한 코드를 사용했습니다.
사용하다forkpty
(비표준이지만 API는 매우 훌륭하며, API가 없으면 언제든지 자신의 구현을 들를 수 있습니다.)exec
하위 프로세스에서 통신하려는 프로그램입니다.
또는, 만약 tty 의미론이 마음에 들지 않는다면, 당신은 다음과 같은 것을 쓸 수 있습니다.forkpty
하지만 두 개의 파이프를 사용하거나, 각각의 통신 방향에 하나씩 사용합니다.socketpair
유닉스 소켓을 통해 외부 프로그램과 통신합니다.
사용할 수 없습니다.popen
양방향 파이프를 사용합니다.
실제로 일부 OS는 양방향 파이프를 지원하지 않으며, 이 경우 소켓 쌍(socketpair
이 유일한 방법입니다.
으로 작동합니다. 는 popen을 . 나는 a를 사용해 왔습니다.popen()
파이프를 양방향으로..
프로세스 읽기 및 하위프로기및쓰기읽스세쓰stdin
그리고.stdout
popen(으)로 .
잘 작동하는 것 같습니다.
저는 제가 알기 전에 그것이 효과가 있을 것이라고 생각했고, 실제로 그렇게 됩니다.위의 게시물에 따르면 작동하지 않아야 합니다.조금 걱정이 되는군요.
gcc on raspbian (벌거벗은 피비안)
언급URL : https://stackoverflow.com/questions/6171552/popen-simultaneous-read-and-write
'programing' 카테고리의 다른 글
인덱스 이름이 Mysql의 전체 데이터베이스에서 고유해야 합니까? (0) | 2023.07.29 |
---|---|
두 날짜 사이의 차이(월 단위 및 일 단위)를 sql로 가져옵니다. (0) | 2023.07.29 |
DOM에 HTML 문자열 추가 (0) | 2023.07.24 |
괄호가 없는 "raise exception()"과 "raise exception"의 차이점이 있습니까? (0) | 2023.07.24 |
NLS_NCHAR_CHARACSET과 NLS_CHARACSET for Oracle 간의 차이 (0) | 2023.07.24 |