I am working on a program where the main program forks itself and the child process calls exec. I have set it up so that the parent process has 2 pipes StdOutPipe
and StdInPipe
, and the child process calls dup so that stdout
writes to the StdOutPipe
and stdin
reads from StdInPipe
. Then the parent process calls wait, after which i would like to read the entirety of the StdOutPipe into a buffer. I know you can do so by reading one character at a time, but is there a faster way to do so?
CodePudding user response:
For performance reasons, one typically reads a chunk at a time, not a character at a time.
- Loop,
- Attempt to enlarge the buffer so it can fit CHUNK_SIZE more bytes.
- If an error occurred,
- Fail.
- Attempt to read CHUNK_SIZE bytes from the pipe into the unused part of the buffer.
- If an error occurred,
- Fail.
- If EOF was reached,
- Break.
- Increased the total number of bytes read by the number of bytes read.
CodePudding user response:
A pipe is basically a byte stream which means:
- There's no concept of messages or message boundaries with pipes
- The process reading from a pipe can read blocks of data of any size, regardless of the size of blocks written by the writing process
A read from a pipe is usually blocked until atleast a byte is written to the pipe.
That said, here's how i would implement your issue.
- Create two pipes, stdinpipe and stdoutpipe
- Do a fork
- Parent process should close the write end of the pipes and sit in a loop, waiting until data is written to pipe
- Child process should close the read end of the pipes and duplicate STDOUT to stdoutpipe and STDIN to stdinpipe
- Child process can then do an exec.
Sample code:
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#define STDPIPE_BUFFER_SIZE 4096
#define ARGV_SIZE 3
int main()
{
// Stdoutpipe and stdint pipe
int stdoutpipe[2], stdinpipe[2], stdin_char_count, stdout_char_count, stdout_read, stdin_read;
pid_t pid;
char stdinbuffer[STDPIPE_BUFFER_SIZE], stdoutbuffer[STDPIPE_BUFFER_SIZE];
char *argv[ARGV_SIZE]; // arguments to exec
if (pipe(stdinpipe) == -1 || pipe(stdoutpipe) == -1)
exit(1); // error occurred
// Fork and exec
switch (pid = fork())
{
case -1:
exit(1); // error
case 0:
// child close the read end of both pipes
if (close(stdinpipe[0]) == -1 || close(stdoutpipe[0]) == -1)
exit(1);
// have the pipes as the new STDIN and STDOUT
if (dup2(stdinpipe[1], STDIN_FILENO) == -1 || dup2(stdoutpipe[1], STDOUT_FILENO) == -1)
exit(1);
argv[0] = "/usr/bin/ssh"; // replace with your own program [ssh -V in my case]
argv[1] = "-V";
argv[2] = NULL;
execve(argv[0], argv, NULL);
exit(1); // if we get here something horribly bad happened
default:
// parent process
stdin_char_count = 0;
stdout_char_count = 0;
// parent close write end of both pipes
if (close(stdinpipe[1]) == -1 || close(stdoutpipe[1]) == -1)
exit(1);
for (;;)
{
stdin_read = read(stdinpipe[0], stdinbuffer, STDPIPE_BUFFER_SIZE);
stdout_read = read(stdinpipe[0], stdinbuffer, STDPIPE_BUFFER_SIZE);
if (stdin_read == 0 && stdout_read == 0)
{
stdinbuffer[stdin_char_count] = '\0';
stdoutbuffer[stdout_char_count] = '\0';
break;
}
if (stdin_read == -1 && stdout_read == -1)
exit(1); // we cant recover from this
stdin_char_count = stdin_read;
stdout_char_count = stdout_read;
}
printf("%s\n", stdoutbuffer);
wait(NULL);
}
}