In this program 2 children process are forked, then one send string to another through pipe. When communication finished, the parent get stuck in waiting the exit of the child that reads from the pipe (waitpid(read_child, NULL, 0);). It works fine without any waitpid (both children processes exit) or just wait for the write_child. Why is that?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int pipe_fd[2];
if (pipe(pipe_fd) == -1)
{
// fail to build pipe
perror("pipe");
exit(EXIT_FAILURE);
}
int read_child = fork();
if (read_child == 0)
{
sleep(0.5);
close(pipe_fd[1]);
printf("child %d read from pipe:\n", (int)getpid());
char buffer;
while (read(pipe_fd[0], &buffer, 1) > 0)
{
write(STDOUT_FILENO, &buffer, 1);
}
write(STDOUT_FILENO, "\n", 1);
close(pipe_fd[0]);
printf("read child exits\n");
exit(0);
}
int write_child = fork();
if (write_child == 0)
{
sleep(0.5);
close(pipe_fd[0]);
printf("child %d writes to pipe\n", (int)getpid());
char message[100];
sprintf(message, "greeting from brother %d", (int)getpid());
write(pipe_fd[1], message, strlen(message));
close(pipe_fd[1]);
printf("write child exits\n");
exit(0);
}
waitpid(read_child, NULL, 0);
// waitpid(write_child, NULL, 0);
return 0;
}
TL;DR
add close(pipe_fd[1]); just before parent process's waitpid(read_child, NULL, 0); will solve the problem.
The problem here is that, parent process also holds a reference to the two pipe fds.
The read blocks until some data available or, when the pipe identified by the fd is closed and the read returns immediately with 0 byte.
Initially the refcount of the write pipe fd is 2, from the writer child and the parent. The parent block waiting for writer, and then the writer exits, refcount decreases to 1. As the writer has exited, the parent's blocking wait returns, and the parent also exit. Refcount decreases to 0, so the write side of pipe is closed, so the reader's blocking read returns with 0 byte, then the reader exits.
However if the parent wait for the reader without first releasing its reference to write side of the pipe fd, the pipe will not be closed even if the writer has exited, due to the final reference from the parent. This means, the read of the reader child shall block forever...
Related
There are already multiple answers to this question but none of them have been able to help me solve my problem. I am trying to understand IPC using an anonymous pipe in C.
From my understanding of pipes, they are a one way communication channel with one read end and one write end.
Assuming we have two c files one named parent.c and the other child.c. What I am trying to achieve is to be able to create 5 or more child processes. After this the parent and the child should communicate with the child processes through standard input and standard output, but since I want to be able to print what the parent receives from the child I'll instead tie the pipes to standard error output using dup2.
In summary
1. Run a parent program which spins up 5 or more child processes and runs them.
2. The child process waits for an input from the parent using scanf.
3. The parent sends a message to the child process.
4. The child process receives the message and sends a reply to the parent and exits.
5. The parent process prints the received message and prints it then exits.
parent.c
// Parentc
#include <stdio.h>
#include <stdlib.h>
#include <uinstd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, const char *argv[]){
// File descriptors for the pipes
int read_pipe[2]; // From child to parent
int write_pipe[2]; // From parent to child
pid_t process_id;
int exit_status;
// Try to fork 5 child processes
for(int i = 0; i < 5; i++){
if(pipe(write_pipe) == -1 || pipe(read_pipe) == -1){
perror("Pipe");
exit(1);
}
// Spin a child process
process_id = fork();
if(process_id == -1){
perror("Fork");
exit(1);
} else if(processId == 0) {
// The child process
// I don't know what to do here, The idea is to close the
// unneeded end of the pipes and wait for input from the parent
// process
// Start the ./child
execl("./child", "");
} else {
// The parent process
char recieved_data[1024];
// Send data to child since stderr is duplicated in the pipe
// It sends the pid of the child
fprintf(stderr, "Test data to %d ", process_id);
// Wait to recieve data from child
// Don't know how to do that
// Print the recieved data
printf("Parent recieved: \"%s\"\n", recieved_data);
wait(&exit_status); // Will wait till all children exit before exiting
}
}
return 0;
}
The child.c is a simple program as shown below
child.c
#include <stdio.h>
int main(int argc, const char *argv[]){
char data_buffer[1024];
// Wait for input from parent
scanf("%s", data_buffer);
// Send data back to parent
printf("Child process: %s", data_buffer);
return 0;
}
Expected output
$ ./parent
parent recived: "Child process: Test data to 12345"
parent recived: "Child process: Test data to 12346"
parent recived: "Child process: Test data to 12347"
parent recived: "Child process: Test data to 12348"
parent recived: "Child process: Test data to 12349"
Where 12345, 12346....12349 is the process id of the child process
Here you have a code i did, and i will use to explain to you:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main() {
char buff[1024];
int aux, i, count;
int fds[2], fdss[2];
pipe(fds); //Here we initialize the file descriptors
pipe(fdss);
mode_t fd_mode = S_IRWXU;
for (i = 0; i < 3; i++) {
aux = fork();
if (aux == 0)
break;
}
switch (i) {
case 0:
printf("Write something:\n");
scanf("%s[^\n]", buff);
i = 0;
count = 0;
while(buff[i] != '\0') {
count++;
i++;
}
dup2(fds[1], 1);
close(fds[1]);
close(fds[0]);
close(fdss[0]);
close(fdss[1]);
write (1, buff, sizeof(buff));
break;
case 1:
dup2(fds[0], 0);
dup2(fdss[1], 1);
close(fds[0]);
close(fds[1]);
close(fdss[0]);
close(fdss[1]);
//
if (execl("/bin/grep", "grep", "example", NULL) == -1) {
printf("Error\n");
exit (1);
}
break;
case 2:
aux = open("result.txt", O_RDWR | O_CREAT , S_IRWXU);
dup2(fdss[0], 0);
dup2(aux, 1);
close(fds[0]);
close(fds[1]);
close(fdss[0]);
close(fdss[1]);
close(aux);
if (execl("/usr/bin/wc", "wc", "-l", NULL) == -1) {
printf("Error \n");
exit (1);
}
}
close(fds[0]);
close(fds[1]);
close(fdss[0]);
close(fdss[1]);
for (i = 0; i < 3; i++) wait(NULL);
return 0;
}
Ok, let's start:
We create and initialize pipes with pipe()
Then we write our code and before execl() we change the file descriptors, in order to pass the text we will write in the console, through processes and finally write in a file called result.txt the result of the "grep example" command applied to the text we have written.
The function dup2(new_descriptor, old_descriptor) is copying the new descriptor into the old descriptor and closes the old descriptor. For example:
Before dup2(fds[1], 1) we have:
0 STDIN
1 STDOUT
2 STDERR
After dup2(fds[1], 1) we have:
0 STDIN
1 fds[1]
2 STDERR
NOTE: If you don't want to use 1, yo can simply write STDOUT_FILENO
So now we are able to write through processes and in my example to a file too
Here is the code, where parent process writes a string input in pipe and children processes read this from pipe. If child process reads from pipe the word "end", then i want to terminate all the processes and then terminate itself, and if reads the word "finish" i want to raise a signal to father for killing all the processes and then exit. I run the code and i had segmentation fault. Why it is wrong?
#define _POSIX_SOURCE
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
void measure_time(int sig)
{
printf("child [%d] received signal %d\n", getpid(), sig);
}
int main(int argc, char *argv[])
{
int n_task = 4;
pid_t pid;
pid_t pid_array[n_task];
int fd[2];
for (int i = 0; i < n_task; i++)
{
pid = fork();
if (pipe(fd) == -1)
{
perror(" pipe ");
exit(1);
}
if (pid < 0)
{
perror("fork");
exit(1);
}
if (pid == 0) //child
{
char *buf;
close(fd[1]);
read(fd[0], buf, 10);
printf("I read: %s", buf);
if (strcmp(buf, "end") == 0)
{
for (int i = 0; i < n_task; i++)
kill(pid_array[i], SIGUSR1);
}else if(strcmp(buf,"finish") == 0){
/*Here i want father to kill all children and then exit.*/
}
exit(0);
}
close(fd[0]);
char *buf;
printf("Give the input string: \n");
scanf("%s", buf);
write(fd[1], buf, strlen(buf));
close(fd[1]);
pid_array[i] = pid;
}
sleep(1);
for (int i = 0; i < n_task; i++)
wait(NULL);
return (0);
}
Besides the issue of uninitialized buf identified by #G. Sliepen, the pipe() need be called before fork() as file descriptors are kept open when forking child process(s). This is also how pipe works.
You can try to change your code snippet to put pipe() before fork().
...
if (pipe(fd) == -1)
{
perror(" pipe ");
exit(1);
}
pid = fork();
if (pid < 0)
{
perror("fork");
exit(1);
}
...
Please read the manual page of pipe(2) in which an example presented.
SO has this post fork() and pipes() in c explained this as well.
Update for terminating process(s)
This child process has no knowledge about existence of its siblings, but its parent process has. If not explicitly required, you can let the parent to do so, i.e. to "end" all child processes.
BTW, instead of sending signal SIGUSR1 it is better to send SIGTERM signal. Although SIGUSSR1 can cause the target process be terminated by default (see signal(7)).
To "finish", i.e. to kill (or terminate) all the child processes as well as parent process, you can simplly kill the parent. All its descendants got killed as well. Or, you can send signal to the same process group. See kill(2).
You are declaring a pointer buf, but did not initialize it. Subsequent calls to read() and scanf() will fail because the pointer is invalid.
You need to make sure buf is initialized and pointing to valid memory. A simple way to fix your code is to do:
char buf[10];
read(fd[0], buf, 10);
If you enable compiler warnings with -Wall, then the compiler will warn you about initialized variables.
Be aware of potential buffer overflows: if you declare char buf[10], make sure you will never write more than ten bytes into it. Also, check the return value of functions like read(), write(), scanf() to ensure no errors were encountered, otherwise the contents of the buffers or output files might not be as expected.
I have a very simple basic program that has two process first one is parent and second one is child.
Child process should write some stuff to the FIFO. After all writing jobs finished(after the child is terminated).
Then parent process should read all the FIFO file and print to the stdout.
So I think, I need a wait(NULL); for parent. So the parent will wait until the child is terminated. But child is also blocked because of the writing and blocked for reading this writes. So both process wait each other and I think,there occur an deadlock.
My program is this:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
int writeSomeStuffToFifo ();
void printAllFifo ();
char * myfifo = "myfifo";
int main(int argc, char **argv) {
int pid=0;
int childPid=-1;
int status;
pid=fork();
if ((pid = fork()) < 0){
perror("fork() error");
}
else if (pid == 0) {
writeSomeStuffToFifo ();
exit(1);
}
else do {
if ((pid = waitpid(pid, &status, WNOHANG)) == -1)
perror("wait() error");
else if (pid == 0) {
//child running
printf("child running\n");
}
else {
if (WIFEXITED(status)){
printf("child is terminated\n");
printAllFifo();
}
else{
printf("child did not exit successfully\n");
}
}
} while (pid == 0);
return 0;
}
int writeSomeStuffToFifo (){ //child process will run this function
int fd;
mkfifo(myfifo, 0666);
fd = open(myfifo, O_WRONLY);
write(fd,"foo1\n",strlen("foo1\n"));
close(fd);
fd = open(myfifo, O_WRONLY);
write(fd,"foo2\n",strlen("foo2\n"));
close(fd);
fd = open(myfifo, O_WRONLY);
write(fd,"foo3\n",strlen("foo3\n"));
close(fd);
}
void printAllFifo (){ //parent process will run this function
int fd=open(myfifo, O_RDONLY);
char* readBuffer=(char*)malloc((strlen("foo1\n")+strlen("foo2\n")+strlen("foo3\n"))*sizeof(char));
read(fd, readBuffer, strlen("foo1\n")+strlen("foo2\n")+strlen("foo3\n"));
printf("%s\n",readBuffer );
close(fd);
}
mkfifo() creates a pipe of limited size. You should not wait in the parent process until the child has finished in order to read, you should read constantly in the parent process while checking if the child has terminated already.
You can use ulimit -p in order to read the default size of pipes in your linux system. The number is multiplications of 512, so a value of 8 means 4096 bytes.
Using pipe() is more suited to the task than mkfifo() because you do not actually need a named pipe. this will provide you with 2 fds, one for read and one for write. In the parent code you close the write fd, in the child code you close the read fd, then you can start reading from the pipe in the parent code until it returns a value <= 0. This would mean that the child process has terminated (and the pipe was closed for writing). then you only need to call waitpid() from the parent code to collect the terminated child process.
on receiving a SIGUSR1 signal, I want to display the value read by the child from the pipe.
Having a little issue. It is always displaying 0 despite getppid() was written to pipe by parent process. Any solution?
`
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
char bufP[10], bufC[10];
int gpid;
void handler(int signum){
signal(SIGUSR1, handler);
if (signum == SIGUSR1){
printf("SIGUSR1 received\n");
gpid = atoi(bufC);
printf("Grandparent: %d\n", gpid);
exit(0);
}
}
int main(void){
int pid, fd[2];
pipe(fd);
pid = fork();
signal(SIGUSR1, handler);
if (pid == 0){
//child
close(fd[1]);
read(fd[0], bufC, sizeof(bufC));
close(fd[0]);
}else{
//parent
close(fd[0]);
sprintf(bufP, "%d", getppid());
write(fd[1], bufP, sizeof(bufP));
kill(pid, SIGUSR1);
close(fd[1]);
}
}
`
Thanks for your response.
You seem to assume that the signal will always be handled after the read() has completed, which is not the case. Signals are asynchronous by nature and can arrive anytime (even halfway through the read()!). Basically you are building your program upon a so-called race condition, which you should really avoid.
While there certainly is a race condition here, it's not what causing trouble. The problem is that your signal is triggered while the read() call in the child process is blocked. You can add a sufficiently long pause in the parent process to let child's read() complete:
if (pid == 0){
//child
close(fd[1]);
read(fd[0], bufC, sizeof(bufC));
close(fd[0]);
sleep(10); // wait for the signal to arrive
}else{
//parent
close(fd[0]);
sprintf(bufP, "%d", getppid());
write(fd[1], bufP, sizeof(bufP));
close(fd[1]); // close right away to be sure the buffers are flushed
sleep(1); // make sure the child has finished reading the buffer
kill(pid, SIGUSR1);
}
Of course, remarks about race conditions and the fact that you should avoid them are still true. This code is not "production quality", it will fail if the load on your system is so heavy that 1 second is not enough to schedule the child process and finish that read() call.
// This code is pasted from
// http://linux.die.net/man/2/pipe
#include <sys/wait.h>
#include <unistd.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 */ <----- Point A
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 */ <----- Point B
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);
}
}
As what I understood,
if (...)
............; ---+
else |---> " Only ONE of them can be reached! "
............; ---+
So, how can the child process read from the pipe AND the parent process write to the pipe in this code?
The result of fork() is that one process becomes two (by asexual reproduction). So while it is still the case that exactly one branch of the if/else block will be taken in a process, there are two processes, and one path will be taken by each.
More specifically, look at what fork() returns: a PID to the parent, and 0 to the new child. Apart from that the two processes are almost identical. So the if (cpid == 0) check is a common pattern after fork() so that you can proceed with distinct logic in each process. In your case, that's reading in one process and writing in the other.
The system call fork() returns twice. Both in the parent process and the child process. The moment you call fork(), two exact copies of your program are running. The SINGLE difference is the return value of fork().
So your "if else only one" rule is still valid when you consider each process in isolation.
Check this resource for a description of the fork call return value:
On success, the PID of the child process is returned in
the parent, and 0 is returned in the child. On failure, -1 is returned
in the parent, no child process is created, and errno is set
appropriately.
So the line that contains cpid = fork(); is executed by both process after the fork, where the parent receives the new process' PID and the child receives 0 as PID. Hence the distinction between parent and child.