This is a quiz from my class, and it invovles concept around fork and pipe. I just have a several confusions about this code.
1) What does if((pid = fork() == 0) means? is it just checking fork using pid(process id), why does loop start with this?
2)close (p[1]); what does this part mean? closing the first integer of array P?
3)The while loop start after close, does it mean it read into p[0]'s size if it is not zero?
4.The two write lines, what does that mean, and why are they both named 1? are they happening at the same time?
#include <stdio.h>
#include <stdlib>
#include <string.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int p[2];
int i, pid, status;
char buffer[20];
pipe(p);
if((pid = fork() == 0) {
close (p[1]);
while (( i = read (p[0], buffer, sizeof("abcdefghi"))) != 0)
{ buffer [i] = '\0';
printf("read %d bytes: %s\n", i, buffer);
}
close(p[0]);
exit (0);
}
write(p[1], "abcdefghi', sizeof("abcdefghi"));
write(p[1], "123456789', sizeof("123456789"));
close(p[0]);
close(p[1]);
while(wait(&status)!= pid);
return(0);
}
You really should RTFM but :-
fork() creates an identical copy of the current procedure running from the same line of code. The only difference between the two copies is the return code from fork(). This will be 0 if you are in the newly created copy or the process id of the newly created copy if you are in the original executable (or -1 if something went wrong).
pipe(p) creates a pipe and returns two file handles in the array "p". the first handle is the output from the pipe opened for reading, the second handle is the input to the pipe open for writing. So close(p[1]) closes the input to the pipe ( this is in the new process which reads from the pipe, it is considered good practice to close the file descriptor you are not using!)
The while loop is checking "i" the return code from the read from the pipe file, this will return 0 when there is nothing to read.
Related
I'm writing a program with two processes that communicate through a pipe. The child process reads some parameters from the parent, executes a shell script with them and returns the results to the parent process line by line.
My code worked just fine until I wrote the while(read()) part at the end of the parent process. The child would execute the shell script, read its echo's from popen() and print them to standard output.
Now I tried to write the results to the pipe as well and read them in the while() loop at the parent's end but it blocks and neither will the child process print the result to standard output. Apparently it won't even reach the point after reading the data from the pipe sent by the parent.
If I comment out the while() at the parent process, the child will print the results and return, and the program will end smoothly.
Why does the while(read()) block even if I closed the writing end of the pipe in both parent and child processes?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
int read_from_file(char **directory, int *octal) {
FILE *file = fopen("input", "r");
if (file == NULL) {
perror("error opening file");
exit(1);
}
fscanf(file, "%s %d", *directory, octal);
}
int main(int argc, char *argv[]) {
char *directory = malloc(256);
int *octal = malloc(sizeof *octal);
pid_t pid;
int pfd[2];
char res[256];
if (pipe(pfd) < 0) {
perror("Error opening pipe");
return 1;
}
if ((pid = fork()) < 0)
perror("Error forking");
if (pid == 0) {
printf("client here\n");
if (read(pfd[0], directory, 256) < 0)
perror("error reading from pipe");
if (read(pfd[0], octal, sizeof(int)) < 0)
perror("error reading from pipe");
// This won't get printed:
printf("client just read from pipe\n");
// close(pfd[0]);
char command[256] = "./asd.sh ";
strcat(command, directory);
char octal_c[5];
sprintf(octal_c, " %d", *octal);
strcat(command, octal_c);
FILE *f = popen(command, "r");
while (fgets(res, 256, f) != NULL) {
printf("%s", res);
if (write(pfd[1], res, 256) < 0)
perror("Error writing res to pipe");
}
fclose(f);
close(pfd[1]);
close(pfd[0]);
fflush(stdout);
return 1;
}
read_from_file(&directory, octal);
if (write(pfd[1], directory, 256) < 0)
perror("Error writing dir to pipe");
if (write(pfd[1], octal, sizeof(int)) < 0)
perror("error writing octal to pipe");
int r;
close(pfd[1]);
while (r = read(pfd[0], res, 256)) {
if (r > 0) {
printf("%s", res);
}
}
close(pfd[0]);
while (wait(NULL) != -1 || errno != ECHILD);
}
Since the child demonstrably reaches ...
printf("client here\n");
... but seems not to reach ...
printf("client just read from pipe\n");
... we can suppose that it blocks indefinitely on one of the two read() calls between. With the right timing, that explains why the parent blocks on its own read() from the pipe. But how and why does that blocking occur?
There are at least three significant semantic errors in your program:
pipes do not work well for bidirectional communication. It is possible, for example, for a process to read back the bytes that it wrote itself and intended for a different process. If you want bidirectional communication then use two pipes. In your case, I think that would have avoided the apparent deadlock, though it would not, by itself, have made the program work correctly.
write and read do not necessarily transfer the full number of bytes requested, and short reads and writes are not considered erroneous. On success, these functions return the number of bytes transferred, and if you want to be sure to transfer a specific number of bytes then you need to run the read or write in a loop, using the return values to track progress through the buffer being transferred. Or use fread() and fwrite() instead.
Pipes convey undifferentiated streams of bytes. That is, they are not message oriented. It is not safe to assume that reads from a pipe will be paired with writes to the pipe, so that each read receives exactly the bytes written by one write. Yet your code depends on that to happen.
Here's a plausible failure scenario that could explain your observations:
The parent:
fork()s the child.
after some time performs two writes to the pipe, one from variable directory and the other from variable octal. At least the first of those is a short write.
closes its copy of the write end of the pipe.
blocks attempting to read from the pipe.
The child:
reads all the bytes written via its first read (into its copy of directory).
blocks on its second read(). It can do this despite the parent closing its copy of the write end, because the write end of the pipe is still open in the child.
You then have a deadlock. Both ends of the pipe are open in at least one process, the pipe is empty, and both processes are blocked trying to read bytes that can never arrive.
There are other possibilities that arrive at substantially the same place, too, some of them not relying on a short write.
The parent process was trying to read from the pipe before the child could have read from it and write the results to it. Using two different pipes for the two-way communication solved the problem.
I am trying to prove one of my doubts, that two non-related processes can share the fd of half-duplex pipe and have communication.
I have created two programs for that. But then I had this another question, that what happens to the pipe if process dies?Because my reader got some garbage message, when I printed out the message.
Writer
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd[2];
char str[] = "hello\n";
if(pipe(fd) < 0)
perror("Pipe creation failed\n");
//Since i am a writer, i should close the reading end as a best practice
close(fd[0]);
/*
The processes need not to be related processes, in order to use the half duplex pipes. fd is just a number/identifier
which can be shared across different processes
*/
printf("Hey there !!! use this file descriptor for reading : %d\n", fd[0]);
//writing message
write(fd[1],str,strlen(str)+1);
return 0;
}
Reader
#include <stdio.h>
#include <unistd.h>
int main()
{
int fd,bytesRead;
char buffer[1024];
printf("please enter the fd :");
scanf("%d",&fd);
bytesRead = read(fd,buffer,1024);
printf("Bytes Read : %d\nMessage : %s\n", bytesRead, buffer);
return 0;
}
You can't do this.
The table of file descriptors is per-process; every process has its own separate set of open file descriptors (note the distinction between open file descriptors, which are per-process, and open file descriptions, which are system-wide, discussed in open(2)). If you want to share a file descriptor between processes, you need to either inherit it over a fork(2) or pass it through a unix(7) domain socket via sendmesg(2) with SCM_RIGHTS in the cmesg(3) header.
(On Linux, you can also pass around paths to /proc/[PID]/fd/..., and other systems may have their own non-portable equivalents).
What is happening in your case is that the read is failing (you're giving it a file descriptor which is not open), leaving your buffer with uninitialized garbage. Since you don't check the return value, you never know that it failed.
In pipe man page,
pipe() creates a pipe, a unidirectional data channel that can be used for interprocess communication. The array pipefd is used to return two file descriptors referring to the ends of the pipe. pipefd[0] refers to the read end of the pipe. pipefd[1] refers to the write end of the pipe. Data written to the write end of the pipe is buffered by the kernel until it is read from the read end of the pipe.
The pipe is mainly used for the related process(parent and child). You are not able to use the pipe for non related process.
In related process, one end is closed. In other end process gets the SIGPIPE signal.
Example program using pipe :-
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int
main(int argc, char *argv[])
{
int pipefd[2];
pid_t cpid;
char buf;
if (argc != 2) {
fprintf(stderr, "Usage: %s <string>\n", argv[0]);
exit(EXIT_FAILURE);
}
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */
close(pipefd[1]); /* Close unused write end */
while (read(pipefd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
_exit(EXIT_SUCCESS);
} else { /* Parent writes argv[1] to pipe */
close(pipefd[0]); /* Close unused read end */
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}
The above program creates a pipe, and then fork(2)s to create a child process; the child inherits a duplicate set of file descriptors that refer to the same pipe. After the fork(2), each process closes the descriptors that it doesn't need for the pipe (see pipe(7)). The parent then writes the string contained in the program's command-line argument to the pipe, and the child reads this string a byte at a time from the pipe and echoes it on standard output.
int main()
{
char *msg="hello";
char buff[MAX];
int p[2];
pipe(p);
int i,pid=fork();
if(pid>0){
//close(p[1]);
read(p[0],buff, MAX);
}
else
{
printf("child exiting\n");
}
}
Why does the above code end up blocking ? But then if we remove the comment and place
close(p[1])
then why does the code end immediately ?
Once you create a pipe, it gets four ends:
A reading end p[0] in the parent process
A writing end p[1] in the parent process
A reading end p[0] in the child process
A writing end p[1] in the child process
UNIX will not deliver EOF to the reader unless both writing ends have been closed, because it knows that the pipe is still writeable.
When the child process exits, it closes both ends of the pipe on its side. However, the parent still has one writeable end open, so reading from the pipe blocks instead of delivering an EOF to the parent. That is why UNIX manual instructs to close the unused ends of the pipe right away:
An application that uses pipe(2) and fork(2) should use suitable close(2) calls to close unnecessary duplicate file descriptors; this ensures that end-of-file and SIGPIPE/EPIPE are delivered when appropriate.
Here is an example of how to make your program not block without closing p[1] on the parent side:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void* write_pipe(void* pp) {
int* p = (int*)pp;
char msg[] = "Hello from another thread!";
write(p[1], msg, sizeof(msg));
return NULL;
}
int main()
{
char buff[100];
int p[2];
pipe(p);
int pid=fork();
if(pid>0){
pthread_t thread1;
pthread_create (&thread1, NULL, &write_pipe, (void *)p);
read(p[0],buff, 100);
printf("%s\n", buff);
printf("parent exiting\n");
}
else
{
printf("child exiting\n");
}
return 0;
}
The code above writes to the writing end of the pipe from a thread within the parent process, instead of writing to it from the child process. This is a legitimate use of a pipe, too, illustrating why UNIX cannot deliver EOF unless the parent's writing end of the pipe is closed.
Read is a blocking call and it returns only when it receives EOF . If you wont close the write end of the pipe, read end wont get the EOF and hence,program will remain blocked
I want to learn how Linux pipes work! I wrote a small and easy program that use a pipe to communicate a string between parent and child process. However, the program results in a dead lock that I have not understood what is its cause.
Here is the code :
#include <sys/wait.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define SIZE 100
int
main(int argc, char *argv[])
{
int pfd[2];
int read_pipe=0, write_pipe=0;
pid_t cpid;
char buf[SIZE];
/* PIPE ***************************************
* pipe() creates a pair of file descriptors, *
* pointing to a pipe inode, and places them *
* in the array pointed to by filedes. *
* filedes[0] is for reading, *
* filedes[1] is for writing *
**********************************************/
if (pipe(pfd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
read_pipe=pfd[0];
write_pipe=pfd[1];
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */
char * hello = "I am a child process\n";
sleep(1);
// wait until there is some data in the pipe
while (read(read_pipe, buf, SIZE) > 0);
printf("Parent process has written : %s\n", buf);
write(write_pipe, hello, strlen(hello));
close(write_pipe);
close(read_pipe);
_exit(EXIT_SUCCESS);
} else { /* Parent writes argv[1] to pipe */
char * hello = "I am a parent process\n";
write(write_pipe, hello, strlen(hello));
while (read(read_pipe, buf, SIZE) > 0);
printf("Child process has written : %s\n", buf);
close(write_pipe);
close(read_pipe);
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}
In this link you'll find the proper mannipulation of PIPEs between parent and child. Your problem here is that the communication is not being correctly set-up.
The PIPE should be used to communicate in only one direction, so one process has to close the read descriptor and the other has to close the write descriptor. Otherwise what will happen is that the call to 'read'(both on the father and the son), since it can detect that there is another process with an open write descriptor on the PIPE, will block when it finds that the PIPE is empty (not return 0), until someone writes something in it. So, both your father and your son are getting blocked on their respective read.
There are two solutions to this:
.You create two PIPEs, one for the communication in each direction, and perform the initialization as explained in the link above. Here you have to remember to close the write descriptor when you are done sending the message, so the other process' read will return, or condition the loop to the count of bytes read (not to the return of read), so you won't perform another call when you read the whole message. For example:
int bread = 0;
while(bread < desired_count)
{
bread += read(read_pipe, buf + bread, SIZE - bread);
}
.You create one PIPE as you did, and modify the flags on the read descriptor, using fcntl to also have O_NONBLOCK, so the calls to read won't block when there's no information in the PIPE. Here you need to check on the return value of the read to know you received something, and go adding up until you get the full length of the message. Also you will have find a way to synchronize the two processes so they won't read messages that are not meant for them. I don't recommend you to use this option, but you can try it if you want using condition variables.
Maybe you can tell if you see any of yout printf() outputs?
Anyway, if you want to establish a two way communication between your paent and child, yout should use two pipes, one for writing data form parent to child an the other for writing from child to parent. Furthermore, your read loops may be dangerous: if the data comes in two or more chunks the second read() overwrites the first portion (I've never seen tha happen with local pipes, but for example with sockets). And of course, yout is not automatically null terminated after read(), so just printing int with "%s" may also cause problems.
I hope that gives you some ideas to try.
I'm trying to do a simple fork -> execute another program -> say "hello" to that child process -> read back something -> print what received.
The program used as child just waits for any line of input and prints something to the stdout like "hello there!"
This is my "host" program (that is not working):
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#define IN 0
#define OUT 1
#define CHILD 0
main ()
{
pid_t pid;
int pipefd[2];
FILE* output;
char buf[256];
pipe(pipefd);
pid = fork();
if (pid == CHILD)
{
printf("child\n");
dup2(pipefd[IN], IN);
dup2(pipefd[OUT], OUT);
execl("./test", "test", (char*) NULL);
}
else
{
sleep(1);
printf("parent\n");
write(pipefd[IN], "hello!", 10); // write message to the process
read(pipefd[OUT], buf, sizeof(buf));
printf("received: %s\n", buf);
}
}
I get this:
child
[.. waits 1 second ..]
parent
received:
What am I missing? Thanks!
EDIT (test.c):
By request, this is the child program:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int getln(char line[])
{
int nch = 0;
int c;
while((c = getchar()) != EOF)
{
if(c == '\n') break;
line[nch] = c;
nch++;
}
if(c == EOF && nch == 0) return EOF;
return nch;
}
main()
{
char line[20];
getln(line);
printf("hello there!", line);
fflush(stdout);
return 0;
}
You're always suppose to read from file-descriptor 0, and write to file-descriptor 1 with pipes ... you have this relationship reversed in the parent process. For what you're wanting to-do, you may end up needing two pipes for two-way communication between the parent and child that avoids situations where the parent ends up reading the contents it wrote to the pipe since process scheduling is non-deterministic (i.e., the child is not guaranteed to read what the parent wrote to the pipe if the parent is also reading from the same pipe since the parent could just end up writing and then reading with no interleaving of the child process to read what the parent wrote).
Change your code to the following:
main ()
{
pid_t pid;
int pipe_to_child[2];
int pipe_from_child[2];
FILE* output;
char buf[256];
pipe(pipe_to_child);
pipe(pipe_from_child);
pid = fork();
if (pid == CHILD)
{
printf("child\n");
//child process not using these ends of the pipe, so close them
close(pipe_to_child[1]);
close(pipe_from_child[0]);
dup2(pipe_to_child[0], fileno(stdin));
dup2(pipe_from_child[1], fileno(stdout));
execl("./test", "test", (char*) NULL);
}
else
{
sleep(1);
printf("parent\n");
write(pipe_to_child[1], "hello!\n", 10); // write message to the process
read(pipe_from_child[0], buf, sizeof(buf));
printf("received: %s\n", buf);
}
}
You need two pipes for this: one for the child process's stdin, and one for its stdout. You cannot reuse the two ends of a pipe as two pipes.
Also, this line of the parent program
write(pipefd[IN], "hello!", 10); // write message to the process
does not write a newline, so getln in the child will never return. (Furthermore, "hello!" has only six characters, but you are writing ten.)
You probably should use wait or waitpid.
It looks like you have your pipe descriptors mixed up. After calling pipe(), pipefd[0] is the read end of the pipe, and pipefd[1] is the write end of the pipe. You're writing to the read end, and reading from the write end.
Also, you're trying to use one pipe for both stdin and stdout of the child process. I don't think this is really what you want to do (you will need two pipes).
Looks like you have your IN/OUT backwards for the pipe -- pipefd[0] is the read end of the pipe, so writing to it (as the parent does) is nonsensical and will fail. Similarly pipefd[1] is the write end so reading from it (as the parent does) will also fail. You should ALWAYS check the return values of the read and write calls, to see if you're getting any errors
Others are saying that the pipe is mono-directional, which is what I thought at first. But actually that's not what my man page says:
A read from fildes[0] accesses the data written to fildes[1]
on a first-in-first-out (FIFO) basis and a read from
fildes[1] accesses the data written to fildes[0] also on a
FIFO basis.
However, this does mean that if the parent is writing to pipefd[0], then the child should read from pipefd[1], so you are associating the wrong side of the pipe with the child's stdin and stdout.
From the man page, it does seem like you can do this with one pipe. But it might be clearer code to use two.
It seems like you are thinking of each element of pipefd as a separate pipe, but that's not the case.