As explained in this answer, I'd be expecting the reader process to catch the EOF right after the writer process closes all related file descriptors.
But that doesn't happen and this program ends up stuck in an endless loop.
Parent waits for it's child to finish & child waits for EOF signalizing closed pipe.
Why the reader process doesn't receive EOF?
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <wait.h>
#define STRING_TO_SEND "Hello, world!\n"
int main() {
int fd[2], i = 0;
__pid_t pid;
char _char;
ssize_t nbytes;
pipe(fd);
pid = fork();
if (pid == -1) {
// Error
perror("Error forking!");
return EXIT_FAILURE;
} else if (pid == 0) {
// Child
close(fd[1]);
while ((nbytes = read(fd[0], &_char, 1)) != EOF) {
if (nbytes == 0)
continue;
putchar(_char);
}
close(fd[0]);
} else {
// Parent
close(fd[0]);
for(;;) {
_char = STRING_TO_SEND[i++];
write(fd[1], &_char, 1);
if (_char == '\0')
break;
}
close(fd[1]);
close(STDOUT_FILENO);
while (wait(NULL)>0) {}
}
return 0;
}
You simply misunderstood the "end of file" indication of read() which simply means nothing more to read for read() (read() returns 0 in that case). But read() doesn't actually return the value EOF. So your condition should be:
while ((nbytes = read(fd[0], &_char, 1)) > 0) {
Also __pid_t is an internal type of your C library. You shouldn't use that; just use pid_t.
See read(2)'s man page for details.
EOF is a constant usually defined to -1 that stdio (the C library buffering layer around the raw system calls) uses to signal end of file in functions like getchar() which conflate the returned character with an end-of-file signal.
read signals end-of-file by simply returning 0. Note that it can also return -1 if there's an error (e.g., you can get EINTR if the read is interrupted by a signal handler before it read anything).
Consequently, what you want is something like:
while ((nbytes = read(fd[0], &_char, 1)) > 0){ /*...*/ }
if (0>nread) { /*report error (or maybe repeat if it's EINTR)*/ }
Manpages (read(2)) or the POSIX spec for read document all this.
Related
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?
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.
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);
}
}
source: https://man7.org/linux/man-pages/man2/pipe.2.html
You can convert the pipe into an ordinary stream and then use whatever function you find convenient to read the data. Here, getdelim() can be used to read all text up to a NUL byte which need not be sent over the pipe. Error checking is partially omitted for brevity.
Also be aware that if you want to continue interacting directly with the pipe even after opening the stream, you'll probably want to disable buffering on the stream.
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void) {
int fds[2];
if(pipe(fds) == -1) {
perror("Failed to create pipe");
exit(EXIT_FAILURE);
}
const pid_t pid = fork();
if(pid == -1) {
perror("Failed to fork");
exit(EXIT_FAILURE);
}
if(!pid) {
close(fds[0]);
const char *const msg = "Hello, world!";
if(write(fds[1], msg, strlen(msg)) == -1) {
perror("Failed to write");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
close(fds[1]);
FILE *const stream = fdopen(fds[0], "r");
if(!stream) {
perror("Failed to create stream");
exit(EXIT_FAILURE);
}
char *text = NULL;
assert(wait(NULL) != -1);
getdelim(&text, &(size_t){0}, '\0', stream);
fclose(stream);
assert(text);
puts(text);
free(text);
}
I'm trying to communicate with an external program which, if executed, will run a terminal interface.
Normally I'll have to provide some inputs (e.g. "1+1") and then read the output of the program (e.g. "2").
Since I need a two-way communication I wasn't able to use popen().
My problem is the following:
Whenever I have a part of the code that asks for inputs, for example containing std::cin >> input I run into the same issue, the read command never exits.
Here I wrote a minimal example, all the child process does is reading the input and repeating it.
When I try to run this code what happens is that I see the first print "Parent says:" and I can provide the input and send it using write. However, when I try to call again the read() function the see the outcome, it never exit.
I've noticed that if I close the pipe that goes from the parent to the child (fd_p2c[1]), then I can read successfully.
This is clearly not what I want, since in my application I'd like to keep both communications open.
Any suggestions on what could be done to fix this problem?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
int status, buf_length;
// Input and output
char buf[256];
char msg[256];
char child_read[256];
int fd_c2p[2]; // file descriptor pipe child -> parent
int fd_p2c[2]; // file descriptor pipe parent -> child
pipe(fd_c2p);
pipe(fd_p2c);
// Spawn a new process with pid
pid_t pid = fork(); // Fork process
if (pid == 0) {
// Child
// Close the unused end of the pipe
if (close(fd_p2c[1]) != 0 || close(fd_c2p[0]) != 0) {
fprintf(stderr, "Faild to close unused end of pipe\n");
exit(1);
}
// Set the comunication
if (dup2(fd_p2c[0], STDIN_FILENO) != 0 ||
dup2(fd_c2p[1], STDOUT_FILENO) != 1 ||
dup2(fd_c2p[1], STDERR_FILENO) != 2) {
fprintf(stderr, "Faild to duplicate the end of the pipes\n");
exit(1);
}
// These two pipe ends are not needed anymore
if (close(fd_p2c[0]) != 0 || close(fd_c2p[1]) != 0) {
fprintf(stderr, "Faild to close unused end of pipe\n");
exit(1);
}
// ask kernel to deliver SIGTERM in case the parent dies
prctl(PR_SET_PDEATHSIG, SIGTERM);
// Moch program
while (1) {
fprintf(stdout, "Parent says: ");
fflush(stdout);
scanf("%s", child_read);
fprintf(stdout, " >> Child repeat: %s\n", child_read);
fflush(stdout);
}
exit(1);
} else {
// Parent
// These two pipe ends are not needed anymore
if (close(fd_p2c[0]) != 0 || close(fd_c2p[1]) != 0) {
fprintf(stderr, "Faild to close unused end of pipe\n");
exit(1);
}
}
// Read output and send input
while (1) {
// Read from child
while (buf_length = read(fd_c2p[0], buf, sizeof(buf) - 1)) {
buf[buf_length] = '\0';
printf("%s", buf);
}
// Enter message to send
scanf("%s", msg);
if (strcmp(msg, "exit") == 0)
break;
// Send to child
write(fd_p2c[1], msg, strlen(msg));
//close(fd_p2c[1]);
}
printf("KILL");
kill(pid, SIGKILL); // send SIGKILL signal to the child process
waitpid(pid, &status, 0);
}
One problem is in the child-process with:
scanf("%s", child_read);
With the %s format there's only three things that will stop scanf from waiting for more input:
Error
End of file
Space
Assuming nothing goes wrong, there will be no errors. And since the parent process keeps the pipe open there will be no end of file. And since the parent process writes only what it itself reads with scanf("%s", ...) there will be no spaces in the data sent.
All in all, the child process will wait indefinitely for scanf to return, which it never will.
Let's call a word what scanf("%s") is able to extract.
This is a contiguous sequence of characters that are not separators (space, tab, new-line...).
The (redirected) standard input of the child reads a word with scanf("%s", child_read);.
This word is known as ended when a separator is read or EOF is reached.
In the parent, write(fd_p2c[1], msg, strlen(msg)); sends a word (and nothing more right after it) because msg is extracted just before as a word.
Note that when you input a word with the keyboard, you also hit the enter key which sends the new-line separator in the standard input. At this time, the terminal makes this line available to scanf(), the word is known as ended and the separator is ignored (in this specific situation, but we could obtain it with fgetc()).
For example, if in the standard input of the parent we input "abc\n",
the parent obtains the word "abc" which is sent as is to the child.
Then the child receives a word starting with "abc" but not ended yet: scanf("%s") is still waiting some other characters after c to make this word longer or a separator or EOF to detect the end of this word.
You can for example send a separator after this word.
// Send to child
write(fd_p2c[1], msg, strlen(msg));
char lf='\n';
write(fd_p2c[1], &lf, 1);
Or maybe it's better to rely on fgets() (instead of scanf("%s")) to obtain a line (not just a word) both in the child and the parent.
By the way, the while/read in the parent looks weird to me.
I would do something like this.
// Read from child
buf_length = (int)read(fd_c2p[0], buf, sizeof(buf) - 1);
if (buf_length <= 0) {
break;
}
If you need a two-ways communication, you could use some unix(7) socket, or several pipe(7)-s, or some fifo(7).
You could use some JSONRPC library. Or XDR (perhaps ONC/RPC/XDR) or ASN/1 for binary communication between heterogeneous computers in a data center, or MPI. If you can use some supercomputer, it probably has proprietary libraries to ease message passing between processes running on different nodes.
Consider using OpenMPI.
I'm trying to communicate with an external program which, if executed, will run a terminal interface.
Maybe you then need some pty(7) with termios(3) ? Then take inspiration from the source code of xterm or of rxvt
You probably need some event loop around a poll(2) before attempting a read(2) (or recv(2)...) or a write(2) (or send(2))
You could find open source libraries (e.g. Glib, libev, ...) to help you, and you certainly should study for inspiration their source code.
Since in general, I wouldn't know the length of the message in that I need to read form fd_c2p, I need to create a look that listens to the pipe till its empty.
To do it is necessary to add the O_NONBLOCK to the file descriptor in the parent as suggested by #some-programmer-dude:
// Parent
// close unused pipe ends
// These two pipe ends are not needed anymore
if (close(fd_p2c[0]) != 0 || close(fd_c2p[1]) != 0) {
fprintf(stderr, "Faild to close unused end of pipe\n");
exit(1);
}
// Add O_NONBLOCK the the fd that reads from the child
int c2p_flags = fcntl(fd_c2p[0], F_GETFL);
fcntl(fd_c2p[0], F_SETFL, c2p_flags | O_NONBLOCK);
Now, when I read the output of the child from the file descriptor fd_c2p[0], it returns an error whenever we attempt to read from an empty file.
Reading the error code in errno should match EWOULDBLOCK.
To know when to stop to read from fd_c2p[0] some knowledge on the output is needed.
This particular reading should stop when the last character of the line when it reaches EWOULDBLOCK and the previous message ended with :.
// Read from child
end_of_message = false;
while (1) {
buf_length = read(fd_c2p[0], buf, sizeof(buf) - 1);
if (buf_length == -1)
{
if (end_of_message && errno == EWOULDBLOCK)
break;
else if (errno == EWOULDBLOCK)
continue;
else {
fprintf(stderr, "reading from pd_c2p returned an error different "
"from `EWOULDBLOCK'\n");
exit(errno);
}
}
buf[buf_length] = '\0';
printf("%s", buf);
end_of_message = buf[buf_length - 1] == ':';
}
This patch solves the problem of reading from the file when one is not sure about how many lines are there before the program asks for inputs.
It should also be safe when the message contains : at any position. To test it, one can reduce the different buffers to a smaller size (e.g. from 256 to 1).
It was also pointed out from by #prog-fh, that in principle one would like to have inputs that contains spaces as well. To accomodate can use fgets instead of scanf:
// Enter message and send it over to the chid process
while (fgets(msg, 256, stdin) != NULL) {
if (msg[strlen(msg)] == '\0')
write(fd_p2c[1], msg, strlen(msg));
else {
fprintf(stderr, "Error encounter while reading input\n");
exit(1);
}
if (msg[strlen(msg) - 1] == '\n')
break;
else
continue;
}
Among the advantages of using fgets there is the fact that the string will keep the newline \n at the end, meaning that there is no need to push an extra character to the write buffer once we are done reading the message.
The complete code is then
#include <cerrno>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
int status, buf_length;
bool end_of_message = false;
int fd_c2p[2]; // file descriptor pipe child -> parent
int fd_p2c[2]; // file descriptor pipe parent -> child
// Input and output
char buf[256];
char msg[256];
char child_read[256];
// We need two pipes if we want a two way comunication.
pipe(fd_c2p);
pipe(fd_p2c);
// Spawn a new process with pid
pid_t pid = fork(); // Fork process
if (pid == 0) {
// Child
// Close the unused end of the pipe
if (close(fd_p2c[1]) != 0 || close(fd_c2p[0]) != 0) {
fprintf(stderr, "Faild to close unused end of pipe\n");
exit(1);
}
// Set the comunications
if (dup2(fd_p2c[0], STDIN_FILENO) != 0 ||
dup2(fd_c2p[1], STDOUT_FILENO) != 1 ||
dup2(fd_c2p[1], STDERR_FILENO) != 2) {
fprintf(stderr, "Faild to duplicate the end of the pipes\n");
exit(1);
}
// These two pipe ends are not needed anymore
if (close(fd_p2c[0]) != 0 || close(fd_c2p[1]) != 0) {
fprintf(stderr, "Faild to close unused end of pipe\n");
exit(1);
}
// ask kernel to deliver SIGTERM in case the parent dies
prctl(PR_SET_PDEATHSIG, SIGTERM);
// Moch Program
while (1) {
fprintf(stdout, "Parent says:");
fflush(stdout);
fgets(child_read, 256, stdin);
fprintf(stdout, " >> Child repeat: %s", child_read);
while (child_read[strlen(child_read) - 1] != '\n') {
fgets(child_read, 256, stdin);
fprintf(stdout, " >> Child repeat: %s", child_read);
}
fflush(stdout);
}
// Nothing below this line should be executed by child process.
// If so, it means that thera has beed a problem so lets exit:
exit(1);
} else {
// Parent
// close unused pipe ends
// These two pipe ends are not needed anymore
if (close(fd_p2c[0]) != 0 || close(fd_c2p[1]) != 0) {
fprintf(stderr, "Faild to close unused end of pipe\n");
exit(1);
}
// Add O_NONBLOCK the the fd that reads from the child
int c2p_flags = fcntl(fd_c2p[0], F_GETFL);
fcntl(fd_c2p[0], F_SETFL, c2p_flags | O_NONBLOCK);
}
// Now, you can write to fd_p2c[1] and read from fd_c2p[0] :
while (1) {
// Read from child
end_of_message = false;
while (1) {
buf_length = read(fd_c2p[0], buf, sizeof(buf) - 1);
if (buf_length == -1)
{
if (end_of_message && errno == EWOULDBLOCK)
break;
else if (errno == EWOULDBLOCK)
continue;
else {
fprintf(stderr, "reading from pd_c2p returned an error different "
"from `EWOULDBLOCK'\n");
exit(errno);
}
}
buf[buf_length] = '\0';
printf("%s", buf);
end_of_message = buf[buf_length - 1] == ':';
}
// Enter message and send it over to the chid process
while (fgets(msg, 256, stdin) != NULL) {
if (msg[strlen(msg)] == '\0')
write(fd_p2c[1], msg, strlen(msg));
else {
fprintf(stderr, "Error encounter while reading input\n");
exit(1);
}
if (msg[strlen(msg) - 1] == '\n')
break;
else
continue;
}
// Check if the user wants to exit the program
if (strcmp(msg, "exit\n") == 0)
break;
}
printf("KILL");
kill(pid, SIGKILL); // send SIGKILL signal to the child process
waitpid(pid, &status, 0);
}
In the code below, is it safe to rely on read() failure to detect termination of child?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void)
{
int pipefd[2];
pipefd[0] = 0;
pipefd[1] = 0;
pipe(pipefd);
pid_t pid = fork();
if (pid == 0)
{
// child
close(pipefd[0]); // close unused read end
while ((dup2(pipefd[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {} // send stdout to the pipe
while ((dup2(pipefd[1], STDERR_FILENO) == -1) && (errno == EINTR)) {} // send stderr to the pipe
close(pipefd[1]); // close unused write end
char *argv[3];
argv[0] = "worker-app";
argv[1] = NULL;
argv[2] = NULL;
execvp("./worker-app", argv);
printf("failed to execvp, errno %d\n", errno);
exit(EXIT_FAILURE);
}
else if (pid == -1)
{
}
else
{
// parent
close(pipefd[1]); // close the write end of the pipe in the parent
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
while (1) // <= here is it safe to rely on read below to break from this loop ?
{
ssize_t count = read(pipefd[0], buffer, sizeof(buffer)-1);
printf("pipe read return %d\n", (int)count);
if (count > 0)
{
printf("child: %s\n", buffer);
}
else if (count == 0)
{
printf("end read child pipe\n", buffer);
break;
}
else if (count == -1)
{
if (errno == EINTR)
{ continue;
}
printf("error read child pipe\n", buffer);
break;
}
}
close(pipefd[0]); // close read end, prevent descriptor leak
int waitStatus = 0;
waitpid(pid, &waitStatus, 0);
}
fprintf(stdout, "All work completed :-)\n");
return EXIT_SUCCESS;
}
Should I add something in the while(1) loop to detect child termination? What specific scenario could happen and break this app ?
Some thoughts of improvements below. However would I just waste CPU cycles?
Use kill with special argument 0 that won't kill the process but just check if it is responsive:
if (kill(pid, 0)) { break; /* child exited */ };
/* If sig is 0, then no signal is sent, but error checking is still performed; this can be used to check for the existence of a process ID or process group ID. https://linux.die.net/man/2/kill */
Use waitpid non-blocking in the while(1) loop to check if child has exited.
Use select() to check for pipe readability to prevent read() from possibly hanging?
Thanks!
Regarding your ideas:
If the child spawns children of its own, the read() won't return 0 until all of its descendants either die or close stdout and stderr. If it doesn't, or if the child always outlives all of its descendants, then just waiting for read() to return 0 is good enough and won't ever cause a problem.
If the child dies but the parent hasn't yet wait(2)ed on it, then kill(pid, 0) will succeed as if the child were still alive (at least on Linux), so this isn't an effective check from within your parent program.
A non-blocking waitpid() on its own would appear to fix the problem with the child having children of its own, but would actually introduce a subtle race condition. If the child exited right after the waitpid() but before the read(), then the read() would block until the rest of the descendants exited.
On its own, if you used select() in a blocking way, it's no better than just calling read(). If you used select() in a non-blocking way, you'd just end up burning CPU time in a loop.
What I'd do:
Add a no-op signal handler function for SIGCHLD, just so that it causes EINTR when it occurs.
Block SIGCHLD in the parent before you start looping.
Use non-blocking reads, and use pselect(2) to block to avoid spinning the CPU forever.
During the pselect, pass in a sigset_t that doesn't have SIGCHLD blocked, so that it's guaranteed to cause an EINTR for it when it eventually gets sent.
Somewhere in the loop, do a non-blocking waitpid(2), and handle its return appropriately. (Make sure you do this at least once after blocking SIGCHLD but before calling select for the first time, or you'll have a race condition.)
I am using these two programs of this answer. This answer uses named-pipes and not pipes, am I correct?
I have written main.c, which is actually the code of my actual project, minimized to this specific question (that's why I have a for loop for example).
#include <unistd.h>
#include <sys/wait.h>
#include <stddef.h>
#include <limits.h>
#include <stdio.h>
int main(void) {
pid_t pid;
int i;
for(i = 0; i < 2; ++i) {
pid = fork();
if (pid == -1) {
// error, failed to fork()
perror("failed to fork()");
return 1;
} else if (pid == 0) {
// child code
if(i < 1) {
// the writer.c
char* arg_list[] = { "w", NULL };
execv( "w", arg_list );
printf("exec FAILED\n");
} else {
// the reader.c
char* arg_list[] = { "r", NULL };
execv( "r", arg_list );
printf("exec FAILED\n");
}
}
}
// parent code
int status;
// wait for all children to terminate
while ((pid = wait(&status)) > 0) {
if (status == 1) {
printf("The child process terminated with an error!\n");
return -1;
}
}
printf("All children are done\n");
return 0;
}
The problem is that sometimes, the reader receives garbage (or most likely nothing) and it hangs up.
Sample output:
Received: Hi
All children are done
samaras#samaras-A15:~/test$ ./m
Received: Hi
All children are done
samaras#samaras-A15:~/test$ ./m
Received: <----------------- This is garbage, that is not reproducible
^C
So, what am I missing?
No need to read below that point.
My guesses are (not checked, so if I am correct, I still need clarification):
The reader runs before writer, that's why it has garbage, but then why it hangs?
or
I need to write a wrapper function read_all() (and one for the write case as well?) that collects all the data that the pipe spits, but then why if I replace "Hi" with "H", I have the same behaviour?
EDIT:
In case my first guess is the case, I put a loop for reading, but it will execute forever in the case that reader starts first.
In the case that I see garbage, after running with strace -f I got this:
...
[pid 3326] read(-1, 0xbfddd80c, 1024) = -1 EBADF (Bad file descriptor)^C
Process 3324 resumed
Process 3325 detached
Process 3326 detached
Your loops (or lack of) have nothing to do with it. When your reader opens (open()) the pipe for reading before writer creates the pipe, then the file descriptor your readers waits on is invalid (-1). So even when writer writes something later on, reader just waits on an invalid fd (-1) and is never going to read anything. Trivially, you could solve it with:
while( (fd = open(myfifo, O_RDONLY)) == -1);
in reader so that it waits until pipe is available. I am actually wondering if there can be a better approach than this. One other way I can think of is a loop over access(), but it's not massively different to this...
I need help with this sample application. When I run it, it gets stuck after the child process prints "Child sending!".
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#define INPUT 0
#define OUTPUT 1
int main()
{
int fd1[2];
int fd2[2];
int pid;
if (pipe(fd1) < 0)
exit(1);
if (pipe(fd2) < 0)
exit(1);
if ((pid = fork()) < 0)
{
perror("fork");
exit(1);
}
else if (pid == 0)
{
close(fd1[INPUT]);
close(fd2[OUTPUT]);
char *str = "Hello World!";
printf("Child sending!\n");
write(fd1[OUTPUT], str, strlen(str));
char *bufferc = (char *)malloc(1000);
char *readbufferc = (char *)malloc(80);
int rdc;
int gotdata = 0;
while (gotdata == 0)
while ((rdc = read(fd2[INPUT], readbufferc, sizeof(readbufferc))) > 0)
{
strncat(bufferc,readbufferc,rdc);
gotdata = 1;
}
printf("Child received: %s",bufferc);
free(readbufferc);
free(bufferc);
exit(0);
}
else
{
close(fd1[OUTPUT]);
close(fd2[INPUT]);
int rd;
char *buffer = (char *)malloc(1000);
char *readbuffer = (char *)malloc(80);
int gd = 0;
while (gd == 0)
while ((rd = read(fd1[INPUT],readbuffer, sizeof(readbuffer))) > 0)
{
strncat(buffer, readbuffer,rd);
gd = 1;
}
printf("Parent received: %s\n",buffer);
free(readbuffer);
printf("Parent sending!");
write(fd2[OUTPUT], buffer, strlen(buffer));
free(buffer);
}
return 0;
}
On a side note, is there a way to debug when I use fork because gdb automatically goes to the parent process
After the child writes to the parent, it must close the write end of the pipe so the parent knows it has reached EOF.
There are many bugs in your code. Why are you using fd2 without initializing it? Remove it.
Now its stuck at "Child sending" because pipe read is a blocking call and you are putting it in a while loop which will never return. Please refer to man page of pipe.
If you want to break that while loop, close all write ends of that pipe.
Also to debug child process, use gdb command follow-fork-mode as child before call to fork() while debugging.
Several things wrong:
fd2 is just never initialized.
The parent will never exit this:
while ((rd = read(fd1[INPUT],readbuffer, sizeof(readbuffer))) > 0)
{
strncat(buffer, readbuffer,rd);
gd = 1;
}
If there is no data to read, read will block and just not return. The only thing that would make it exit is if the connection was closed and the child doesn't close it.
You are calling read() in the expectation that if there is nothing to read, it will return with zero bytes read. However, what you are seeing is because read() is waiting for some data before returning. To address this, you need to do one of two things:
set your socket to do non-blocking reads (not recommended)
use select() or poll() to see whether there is some data to read before you read it
Also, several other points:
don't cast the returns from malloc()
check that malloc() does not return NULL
replace the whole gotdata thing with a break instruction