how to establish a non blocking read from unix pipes? - c

Let‘s assume we have a pipe int InPipe[2];. How can you read the input until the pipe is empty without blocking when the whole available Data input was read?
I know this question has been asked several times, but I couldn’t assemble a suitable function.
This is my Code so far:
int InPipe[2];
char buffer[1024];
int rc;
while (true){
read(InPipe[0], buffer, sizeof(buffer));
fprintf(stdout, “%s“, buffer);
bzero(&buffer, sizeof(buffer)); // Clearing Buffer
}
Any Ideas, Suggestions, Code Snippets

Reading from a pipe
Attempts to read from a pipe that is currently empty block until at
least one byte has been written to the pipe. If the write end
of a pipe is closed, then a process reading from the pipe will
see end-of-file (i.e., read() returns 0) once it has read all remaining
data in the pipe.
taken from linux interface programming.
You cant! the process reading from the pipe will be blocked in this situation.
Due to people comments, i am adding this section:
we can use a pipe to allow communication between two processes. To con-nect two processes using a pipe, we follow the pipe() call with a call to fork(). immediately after the fork(), one process closes its descriptor for the write end of the pipe, and the other closes its descriptor for the read end. For example, if the parent is to send data to the child, then it would close its read descriptor for the pipe, filedes[0], while the child would close its write descriptor for the pipe, filedes[1], then the code for this will be:
int filedes[2];
if (pipe(filedes) == -1) /* Create the pipe */
errExit("pipe");
switch (fork()) /* Create a child process */
{
case -1:
errExit("fork");
case 0: /* Child */
if (close(filedes[1]) == -1) /* Close unused write end */
errExit("close");
/* Child now reads from pipe */
break;
default: /* Parent */
if (close(filedes[0]) == -1) /* Close unused read end */
errExit("close");
/* Parent now writes to pipe */
break;
}

There are a couple of key things mentioned in the comments, i.e. non-blocking IO, and performing the read in its own thread, along with some other suggestions. The example here goes into detail explaining its architecture. Only the code section is reproduced below as I believe it to be a decent illustration of several of these comments. The example code is commented throughout. Read them, they serve as a good tutorial:
// C program to demonstrate use of fork() and pipe()
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<string.h>
#include<sys/wait.h>
int main()
{
// We use two pipes
// First pipe to send input string from parent
// Second pipe to send concatenated string from child
int fd1[2]; // Used to store two ends of first pipe
int fd2[2]; // Used to store two ends of second pipe
char fixed_str[] = "forgeeks.org";
char input_str[100];
pid_t p;
if (pipe(fd1)==-1)
{
fprintf(stderr, "Pipe Failed" );
return 1;
}
if (pipe(fd2)==-1)
{
fprintf(stderr, "Pipe Failed" );
return 1;
}
scanf("%s", input_str);
p = fork(); //Note - the return of fork can be less than, greater
// than or equal to zero. Each is significant in
// knowing how to direct program flow, as shown
// in this section...
if (p < 0)
{
fprintf(stderr, "fork Failed" );
return 1;
}
// Parent process
else if (p > 0)
{
char concat_str[100];
close(fd1[0]); // Close reading end of first pipe
// Write input string and close writing end of first
// pipe.
write(fd1[1], input_str, strlen(input_str)+1);
close(fd1[1]);
// Wait for child to send a string
wait(NULL);
close(fd2[1]); // Close writing end of second pipe
// Read string from child, print it and close
// reading end.
read(fd2[0], concat_str, 100);
printf("Concatenated string %s\n", concat_str);
close(fd2[0]);
}
// child process
else
{
close(fd1[1]); // Close writing end of first pipe
// Read a string using first pipe
char concat_str[100];
read(fd1[0], concat_str, 100);
// Concatenate a fixed string with it
int k = strlen(concat_str);
int i;
for (i=0; i<strlen(fixed_str); i++)
concat_str[k++] = fixed_str[i];
concat_str[k] = '\0'; // string ends with '\0'
// Close both reading ends
close(fd1[0]);
close(fd2[0]);
// Write concatenated string and close writing end
write(fd2[1], concat_str, strlen(concat_str)+1);
close(fd2[1]);
exit(0);
}
}

Related

Reading from a pipe even after the write end is closed

Can you please explain me why is the child process able to read even after the parent closes its write end?
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int fd[2];
char buffer[20];
pipe(fd);
if ( fork() == 0 ) //child
{
close(fd[0]); //As it is writing close the read end
strcpy(buffer, "Hello World");
write(fd[1], buffer, sizeof(buffer));
close(fd[1]);
}
else //parent
{
close(fd[1]); //As it is reading closing the write end
while(1)
{
read(fd[0], buffer, sizeof(buffer));
printf("Buffer:%s\n", buffer);
sleep(1);
}
close(fd[0]);
}
}
O/P: Child continuously prints:
Buffer:Hello World
Why is the child able to receive even when the parent terminates? Shouldn't read get EOF?
Why is the child able to receive even when the parent terminates? Shouldn't read get EOF?
At that point the parent process is basically reading nothing (i.e.: read() is returning 0) and printing over and over what it had read in a previous call to read().
You have to look at the value returned by the read() system call. That value is of type int, and basically :
-1: error, something went wrong.
0: nothing else remaining to read, i.e.: EOF (what you were looking for).
Otherwise: the number of bytes read by read() that were stored into buffer.
You can rewrite the parent's while-loop accordingly:
while(1) {
int count = read(fd[0], buffer, sizeof(buffer));
switch (count) {
case 0: // EOF
break;
case -1: // read() error
// ... error handling ...
break;
default: // fine
// count contains the number of bytes read
buffer[count] = '\0'; // NUL character, to indicate the end of string
printf("Buffer:%s\n", buffer);
sleep(1);
}
}

Unable to read from a pipe after something is written to it by a child process

I create a function exec_in_child which takes the command arguments, pipe file descriptors (fds), read_flag and write_flag as input. When write_flag is set to 1, the child process should duplicate stdout to fds[1], and then execute the command. When read_flag is set to 1, the child should duplicate the stdin to fds[0] and the execute the command.
Do I have to close one end of the pipe when I'm reading/writing to
the other end?
The code below doesn't work. I'm trying to execute /bin/ls inside a child process, write the stdout to the pipe, and then read
it off in the parent process and print it. I'm not able to read in
the parent process.
Can I read and write to the pipe inside the same process without closing other? This situation arises when I want to child to read
from pipe, execute, and then write to the pipe.
#include <stdio.h> /* printf */
#include <stdlib.h>
#include <string.h> /* strlen, strcpy */
int exec_in_child(char *arguments[], const int temp[], int , int);
int main()
{
ssize_t bytes_read;
char *curr_dir = (char *)malloc(500);
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
char *arguments[] = {"/bin/pwd",0};
exec_in_child(arguments, pipefd, 0, 1);
bytes_read = read(pipefd[0], curr_dir, strlen(curr_dir));
printf("%s = %d\n", "bytes read from pipe" ,(int)bytes_read);
printf("%s: %s\n","character read from the pipe",curr_dir);
return 0;
}
int exec_in_child(char * arguments[], const int fds[], int read_flag, int write_flag) {
pid_t pid;
pid = fork();
if (pid < 0) {
perror("Error: Fork Failed");
}
else if (pid == 0){ /*inside the child process */
if (read_flag == 1) {
dup2(fds[0], 0);
perror("Dup2 stdin");
}
if (write_flag == 1) {
dup2(fds[1], 1);
perror("Dup2 stdout");
}
execv(arguments[0], arguments);
perror("Error in child");
exit(1);
} /* if (pid == 0) */
else {
while(pid != wait(0));
} /* if(pid < 0) */
return 0;
}
I get this result:
hmwk1-skk2142(test) > ./a.out
Dup2 stdout: Success
bytes read from pipe = 0
character read from the pipe:
To answer your questions:
1) You do not need to close either end of the pipe in order to use the other end. However, you generally want to close any end(s) of the pipe you're not using. The biggest reason to do this is that the pipe will only close when all open write file descriptors are closed.
2) Your code isn't working because you're using strlen() improperly. This function calculates the length of a string by searching for the null (0) character. When you malloc() the storage for curr_dir you have no guarantee of what resides there (though it will usually be zeroed, as in this case).
Thus, your call strlen(curr_dir) returns zero, and the read() system call thinks you want to read up to zero bytes of data. Change your read call to the following:
bytes_read = read(pipefd[0], curr_dir, 500);
and your code will work perfectly.
3) You can read and write to any pipe you've got a valid file descriptor to. A single process can absolutely read and write the same pipe.

Forking and pipes: does this C program contain a race condition?

I'm learning about interprocess communication and came across the below example program.
I don't understand what's to prevent the parent process from attempting the read (as part of the else condition at the bottom of the program) before the child process has completed the write.
What (if anything) constrains the parent process from attempting the read from standard input before the child process has written to standard output?
int main(void)
{
int fd[2], nbytes;
pid_t childpid;
char string[] = "Hello, world!\n";
char readbuffer[80];
pipe(fd);
if((childpid = fork()) == -1)
{
perror("fork");
exit(1);
}
if(childpid == 0)
{
/* Child process closes up input side of pipe */
close(fd[0]);
/* Send "string" through the output side of pipe */
write(fd[1], string, (strlen(string)+1));
exit(0);
}
else
{
/* Parent process closes up output side of pipe */
close(fd[1]);
/* Read in a string from the pipe */
nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
printf("Received string: %s", readbuffer);
}
return(0);
}
Nothing prevents the parent from starting the read() call before the child has written anything to the pipe, but the parent process won't get any data until after the child has written data to the pipe (and that write will be atomic because it is less than the length of the pipe buffer). The parent will hang waiting for either some data to arrive on the pipe or every writing end of the pipe to be closed.
Note that if nbytes == 0 after the read, the output from printf() is indeterminate because readbuffer is not initialized.

Send data between pipes in C with While loop

I'm trying to send data between two pipes, that will go from parent->child->parent->child etc and so on until I exit the loop. Right now I'm trying to just pass an integer and increment it for each read done on it. At the moment it seems like each process is only incrementing it's own value and it's read component isn't working correctly. Are my pipes setup wrong?
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#define BUFFER_SIZE 25
#define READ 0
#define WRITE 1
int main(void)
{
pid_t pid;
//open two pipes, one for each direction
int mypipefd[2];
int mypipefd2[2];
/* create the pipe */
if (pipe(mypipefd) == -1 || pipe(mypipefd2) == -1) {
fprintf(stderr,"Pipe failed");
return 1;
}
/* now fork a child process */
pid = fork();
if (pid < 0) {
fprintf(stderr, "Fork failed");
return 1;
}
if (pid > 0) { /* parent process */
int parentVal = 0;
while(1) {
close(mypipefd[READ]); //close read end, write and then close write end
parentVal++;
write(mypipefd[WRITE],&parentVal,sizeof(parentVal));
printf("Parent: writes value : %d\n", parentVal);
close(mypipefd[WRITE]);
close(mypipefd2[WRITE]); //close write end, read, and then close read end
read(mypipefd2[READ],&parentVal,sizeof(parentVal));
printf("Parent: reads value : %d\n", parentVal);
close(mypipefd2[READ]);
}
}
else { /* child process */
int childVal = 0;
while(1) {
close(mypipefd[WRITE]);
read(mypipefd[READ],&childVal,sizeof(childVal));
printf("child: read value : %d\n", childVal);
childVal++;
close(mypipefd[READ]);
close(mypipefd2[READ]); //close read end, write and then close write end
write(mypipefd2[WRITE],&childVal,sizeof(childVal));
printf("child: write value : %d\n",childVal);
close(mypipefd2[WRITE]);
}
}
}
In addition to what Jonathan Leffler said, I want to say that you should add checks to make sure that when read fails, you deal with the condition gracefully.
if (pid > 0) { /* parent process */
int parentVal = 0;
close(mypipefd[READ]); // The parent is not going to read from the first pipe.
// Close the read end of the pipe.
close(mypipefd2[WRITE]); // The parent is not going to write to the second pipe.
// Close the write end of the pipe.
while(1) {
parentVal++;
write(mypipefd[WRITE],&parentVal,sizeof(parentVal));
printf("Parent: writes value : %d\n", parentVal);
// If the chld closes the write end of the second pipe,
// break out of the loop.
if ( read(mypipefd2[READ],&parentVal,sizeof(parentVal)) > 0 )
{
printf("Parent: reads value : %d\n", parentVal);
}
else
{
break;
}
}
close(mypipefd[WRITE]); // Close the write end of the first pipe
close(mypipefd2[READ]); // Close the read end of the second pipe
}
else { /* child process */
int childVal = 0;
close(mypipefd[WRITE]); // The child is not going to write to the first pipe.
// Close the write end of the pipe.
close(mypipefd2[READ]); // The child is not going to read from the second pipe.
// Close the read end of the pipe.
while(1) {
// If the parent closes the write end of the first pipe,
// break out of the loop.
if ( read(mypipefd[READ],&childVal,sizeof(childVal)) > 0 )
{
printf("child: read value : %d\n", childVal);
}
else
{
break;
}
childVal++;
write(mypipefd2[WRITE],&childVal,sizeof(childVal));
printf("child: write value : %d\n",childVal);
}
close(mypipefd[READ]); // Close the read end of the first pipe
close(mypipefd2[WRITE]); // Close the write end of the second pipe
}
Your problem is that you're too enthusiastic about closing file descriptors. (That's a pleasant change from the usual; more often people don't close enough file descriptors.)
Each of the processes should close the ends of the pipes it is not going to use, but should do that before the loop. It should not close the pipes it is reading from or writing to if you want to iterate more than once. Those closes should be after the loop (and could be left out altogether since the program will terminate once the loop terminates, though it is generally better to explicitly close what you explicitly open). You should probably make sure that the loop does terminate (e.g. when the count reaches 1000).

Sending multiple strings using pipes to child process

I have a task in Linux and I can't get it work.
I have a program that receives a text file as parameter. It then creates a child process using fork() and sends to the child process, line by line the content of the text file received as parameter. The child process needs to count the lines and return to the parent process the number of lines received.
This is what I have until now, but somewhat the child process does not receive all the lines. For my test I used a text file with 9 lines. The parent sent 9 lines as strings but the child process received only 2 or 3 of them.
What am I doing wrong?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char string[80];
char readbuffer[80];
int pid, p[2];
FILE *fp;
int i=0;
if(argc != 2)
{
printf("Syntax: %s [file_name]\n", argv[0]);
return 0;
}
fp = fopen(argv[1], "r");
if(!fp)
{
printf("Error: File '%s' does not exist.\n", argv[1]);
return 0;
}
if(pipe(p) == -1)
{
printf("Error: Creating pipe failed.\n");
exit(0);
}
// creates the child process
if((pid=fork()) == -1)
{
printf("Error: Child process could not be created.\n");
exit(0);
}
/* Main process */
if (pid)
{
// close the read
close(p[0]);
while(fgets(string,sizeof(string),fp) != NULL)
{
write(p[1], string, (strlen(string)+1));
printf("%s\n",string);
}
// close the write
close(p[1]);
wait(0);
}
// child process
else
{
// close the write
close(p[1]);
while(read(p[0],readbuffer, sizeof(readbuffer)) != 0)
{
printf("Received string: %s\n", readbuffer);
}
// close the read
close(p[0]);
}
fclose(fp);
}
A pipe is a unidirectional interprocess communication channel. You have to create 2 pipes, one to speak to the child process, the other to read data back.
Remember to close the unused side of the pipe on both processes.
You are sending the null terminator to the other process:
write(p[1], string, (strlen(string)+1));
That makes the result confusing because when you print what you've received, you only see up to the null terminator.
If you do this instead:
write(p[1], string, strlen(string));
you should get what you expect.
You're not counting the number of lines, you're counting the number of times read(2) returns.
When using pipes, read(2) will pull as much data as possible from the pipe: min(pipe_available, space_available). It doesn't care for newlines, 0 bytes etc. Simple tricks to make it work:
Use a loop to walk readbuffer and look for \n
Use fdopen + fgets (I have a feeling this is probably flawed)
look into manpage of pipe ( man 2 pipe ), the program you're trying to write is as an example there, compare it with yours :)

Resources