Unix Processes PIPE - c

I have the following assignment to do :
Ping-pong. Two processes will play the ping-pong game.
The first process will generate a random number between 5000 and 15000 that will be send to the other process.
This process will subtract a random value (between 50 and 1000) and will send the number back,
The chat between the processes will be implemented using pipe channels.
The game ends when the value is below zero.
Each process will print the received value.
So i wrote the following code :
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int p[2];
int a, n;
pipe(p);
int pid = fork();
if (pid == 0)
{
int r = 0;
srand(time(NULL));
while ( r < 50 || r > 1000)
{
r = rand();
}
if ((a=read(p[0], &n, sizeof(int)))==-1)
perror("Error read pipe:");
printf("Process 2 recived %d\n", a);
a = a - r;
close(p[0]); close(p[1]);
}
else if (pid > 0)
{
srand(time(NULL));
while ( n < 5000 || n > 15000) {
n = rand();
}
while (n > 0)
{
printf("Process 1 recived %d\n", n);
if (write(p[1], &n, sizeof(int))==-1)
perror("Error write pipe:");
wait(0);
}
}
return 0;
}
When it is executed it goes in a infinite loop which prints "Process 1 received 4" and i don't know why.
I created another pipe and now it prints correctly the first received value but from the second process it happens the same thing
Process 1 recived 9083
Process 2 recived 4
Process 1 recived 4
and infinite loop

I give you a corrected program with the explanation in comments:
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int p[2];
int p2[2];
int a, n;
pipe(p);
pipe(p2);
int pid = fork();
if (pid == 0)
{
close(p[1]); // not writing in p, so closing p[1] immediately
close(p2[0]); // not reading in p2, so closing p2[0] immediately
srand(time(NULL));
do {
if ((a = read(p[0], &n, sizeof(int)))==-1)
perror("Error read pipe:");
if (a == 0) // nothing read means all processes have closed p[1]
n = 0;
else
printf("Process 2 recived %d\n", n);
int r = 50 + rand() % 950; // Only need one random number, and need one different each time
n -= r;
if (n > 0)
{
if(write(p2[1], &n, sizeof(int))==-1)
perror("Error write pipe:");
}
} while (n > 0);
close(p[0]); close(p2[1]);
exit(0); // or return(0) - as pointed out by Stian
}
else if (pid > 0)
{
close(p[0]); // not reading in p, so closing p[0] immediately
close(p2[1]); // not writing in p2, so closing p2[1] immediately
srand(time(NULL) + pid); // Adding pid so that processes each use a different seed
n = rand() % 10000 + 5000; // Only need one random number
while (n > 0)
{
if (write(p[1], &n, sizeof(int))==-1)
perror("Error write pipe:");
// wait(0); Wrong use of wait()
if ((a = read(p2[0], &n, sizeof(int)))==-1)
perror("Error read pipe:");
if (a == 0) // nothing read means all processes have closed p2[1]
n = 0;
else
printf("Process 1 recived %d\n", n);
int r = 50 + rand() % 950;
n -= r;
}
close(p[1]); close(p2[0]);
}
wait(NULL); // Better use of wait(). Still not perfect, you need to check return value.
return 0;
}

You need two pipes. One for each direction

Related

Child processes running in random order and before parent finishes loop?

I created a for loop in C that is responsible for creating a given amount of processes. There is a section in the for loop (pid > 0) for the parent, and a section for the child (pid == 0). The wait() function call is after the loop finishes, but I am having a problem where some of the child processes start executing their fprintf statements while the parent is still running the for loop. How can I make sure all of the children do NOT start running until the parent has gone through the for loop and finished all fprintf statements in order?
> ./a.out 5 process 1
*[EXPECTED]*
Process 0 creating process 1...
Process 0 creating process 2...
Process 0 creating process 3...
Process 0 creating process 4...
Process 0 creating process 5...
Process 1 beginning...
*(SLEEP RANDOM AMOUNT OF TIME)*
Process 1 ending...
Process 2 beginning...
*(SLEEP RANDOM AMOUNT OF TIME)*
Process 2 ending...
Process 3 beginning...
*(SLEEP RANDOM AMOUNT OF TIME)*
Process 3 ending...
Process 4 beginning...
*(SLEEP RANDOM AMOUNT OF TIME)*
Process 4 ending...
Process 5 beginning...
*(SLEEP RANDOM AMOUNT OF TIME)*
Process 5 ending...```
```*[ACTUAL RESULT]*
Process 0 creating process 1...
Process 1 beginning...
Process 0 creating process 2...
Process 2 beginning...
Process 0 creating process 3...
Process 0 creating process 4...
Process 0 creating process 5...
Process 5 beginning...
Process 3 beginning...
Process 4 beginning...
*(SLEEP RANDOM AMOUNT OF TIME)*
Process 1 ending...
Process 2 ending...
Process 5 ending...
Process 3 ending...
Process 4 ending...`
The output is never not the same, and the child processes often begin in a random order in between each parent process loop iteration.
Here is my code:
main.c:
/*
* Write a program that takes three arguments:
* the number of “things” (between 1 and 256),
* a word: either “thread” or “process, and
* a “pattern number” (1-2 only).
*/
#include "functions.c"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
// USE STDERR FOR PRINTING
// fprintf(stderr, "This is process %d \n", getpid());
// extern set_pattern
typedef struct {
int number;
pid_t pid;
} process;
typedef struct {
int number;
pthread_t tid;
} thread;
int main(int argc, char *argv[]) {
setbuf(stdout, NULL);
// Check for correct amount of paramters
if (argc != 4) {
help_and_exit(argv[0]);
}
// Parse parameters and check for errors
// Set size
int size;
set_size(argv, &size);
// Set type (thread or process)
exec_t type;
set_choice(argv, &type);
// Set pattern
int pattern;
set_pattern(argv, &pattern);
// printf("Size is %d, choice is %d, and pattern is %d\n", size, type,
// pattern);
if (pattern == 1) {
if (type == Process) {
// process processes[size];
pid_t alpha_process = getpid();
int process_number = 0;
pid_t pids[size];
int n = size;
int flag = 0;
for (int i = 0; i < n; i++) {
fflush(stdout);
if ((pids[i] = fork()) < 0) {
perror("fork");
} else if (pids[i] == 0) {
fprintf(stderr, "Process %d beginning...\n", i + 1);
sleep(rand() % 5);
fflush(stdout);
fprintf(stderr, "Process %d ending...\n", i + 1);
exit(0);
} else if (pids[i] > 0) {
fprintf(stderr, "Process %d creating process %d...\n", i - i, i + 1);
}
}
int counter = 0;
while (counter < n) {
wait(NULL);
counter++;
}
}
}
return 0;
}
functions.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
typedef enum { Thread, Process } exec_t;
void help_and_exit(char *name) {
fprintf(stderr,
"usage: %s <number of things> {thread | process} <pattern number>\n",
name);
exit(-1);
}
void set_size(char *argv[], int *size) {
if (sscanf(argv[1], "%d", size) == 0) {
fprintf(stderr, "First parameter must be an integer. Recieved: '%s'\n",
argv[1]);
help_and_exit(argv[0]);
}
if (*size > 256 || *size < 1) {
fprintf(stderr, "Number of things must be between 1-256!\n");
help_and_exit(argv[0]);
}
}
void set_choice(char *argv[], exec_t *type) {
char choice[8];
if (sscanf(argv[2], "%s", choice) == 0) {
fprintf(stderr, "Second parameter must be a string. Recieved: '%s'\n",
argv[2]);
help_and_exit(argv[0]);
}
if (strcmp(choice, "thread") != 0 && strcmp(choice, "process") != 0) {
fprintf(stderr, "Second parameter must be either 'thread' or 'process'\n");
help_and_exit(argv[0]);
} else if (strcmp(choice, "thread") == 0) {
*type = Thread;
} else {
*type = Process;
}
}
void set_pattern(char *argv[], int *pattern) {
if (sscanf(argv[3], "%d", pattern) == 0) {
fprintf(stderr, "Third parameter must be an integer. Recieved: '%s'\n",
argv[3]);
help_and_exit(argv[0]);
}
if (*pattern != 1 && *pattern != 2) {
fprintf(stderr, "Pattern number must be either 1 or 2!\n");
help_and_exit(argv[0]);
}
}
Put each child on pause() and have the parent wake them up after the loop by sending a signal. (Credit to DYZ for the answer)
#include "functions.c"
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
//snip-snip...
int cont;
void handleContinueSignal(int sig) { cont = 1; }
int main(int argc, char *argv[]) {
// snip-snip...
if (pattern == 1) {
if (type == Process) {
// snip-snip...
for (int i = 0; i < n; i++) {
fflush(stdout);
if ((pids[i] = fork()) < 0) {
perror("fork");
} else if (pids[i] == 0) {
signal(SIGCONT, handleContinueSignal);
pause();
fprintf(stderr, "Process %d beginning...\n", i + 1);
sleep(rand() % 5);
fflush(stdout);
fprintf(stderr, "Process %d ending...\n", i + 1);
exit(0);
} else if (pids[i] > 0) {
fprintf(stderr, "Process %d creating process %d...\n", i - i, i + 1);
}
}
int counter = 0;
while (counter < n) {
kill(pids[counter], SIGCONT);
wait(NULL);
counter++;
}
}
}
return 0;
}

Checking if a number is prime using fork and pipe

So I have to do the next program:
create a child process,
the parent reads numbers from the keyboard (until 0) and sends them to the child,
the child receives numbers from the parent and prints those that are prime,
when the child receives 0 from the parent, it will terminate.
And I did it the next way:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
int a=1;
int isprime=0;
int p[2];
pipe(p);
int pid = fork();
if(pid < 0)
exit(1);
if(pid == 0)
{
close(p[1]);
read(p[0],&a,sizeof(int));
isprime = 1;
for(int i=2;i*i<=a;i++)
if(a%i==0)
isprime = 0;
if(isprime == 1)
printf("%d is prime",a);
close(p[0]);
}
else
{
close(p[0]);
while(a!=0){
printf("a=");
scanf("%d",&a);
write(p[1],&a,sizeof(int));
}
wait(0);
close(p[1]);
}
return 0;
}
But it doesn't work properly, it stops after the first reading of a. Can somebody help me, please?

Processes in C for Linux(Ubuntu)

Here is what I am trying to do:
Write a C program that takes an integer command line argument n,
spawns n processes that will each generate a random numbers between
-100 and 100, and then computes and prints out the sum of these random numbers. Each process needs to print out the random number it
generates.
This is what I have so far:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
int command,processCheck; // processCheck: to check if fork was successful or not and to
char * strNumProcess = NULL;// check the status of child process
while((command = getopt(argc, argv, "n:"))!=-1){
if(command == 'n'){
strNumProcess = optarg;
break;
}
}
int numProcess = atoi(strNumProcess);
int pipes[numProcess][2];
int randomNum; // Variable to store the random number
int randomNumSum=0; // Initialized variable to store the sum of random number
/** A loop that creates specified number of processes**/
for(int i=0; i<numProcess; i++){
processCheck = fork(); // creates a child process. Usually fork() = 2^n processes
if(processCheck < 0){ // Checks for the error in fork()
printf("Error");
exit(1); // Terminates with error
}
else if(processCheck == 0){
close(pipes[i][0]);
/** Child process**/
srand(time(NULL)+getpid()); // sets the randomness of the number associted with process id
randomNum = rand()% 201 + (-100); // sets the range of random number from -100 to 100 and stores the random number in randomNum
printf("%d\n" , randomNum); // Prints out the random number
write(pipes[i][1], &randomNum, sizeof randomNum);
close(pipes[i][1]);
exit(0);// Terminates successfully
}
else{
if(wait(NULL)){ // Waits for the child process to end and directs to parent process
int v;
if(read(pipes[i][0], &v, sizeof v)==sizeof(v)){
randomNumSum+=v;
close(pipes[i][0]);
}
}
}
close(pipes[i][1]);
}
printf("%d\n", randomNumSum); // Prints the sum of the random number
return 0;
}
The program goes in infinite loop after second process.
edit
The OP has made significant changes to the question, it's not the same question as it was yesterday. This answer might henceforth make no sense any more.
end edit
The reason for this is that fork() creates a new independent process with its
own virtual memory. It only inherits the values from the parent, the forked process do not share variables
with the parents. So randomNumSum is for every child a unique variable and
changing it does not affect the randomNumSum of the parent.
You need to use for example pipes for communication between parents and
children, the children write the results in the pipe, the parent reads from the
children.
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char **argv)
{
if(argc != 2)
{
fprintf(stderr, "usage: %s num_of_children\n", argv[0]);
return 0;
}
int noc = atoi(argv[1]);
if(noc <= 0)
{
fprintf(stderr, "Invalid number of children\n");
return 1;
}
int pipes[noc][2];
pid_t pids[noc];
for(size_t i = 0; i < noc; ++i)
{
if(pipe(pipes[i]) == -1)
{
perror("pipe");
pids[i] = -2; // used later for error checking
continue;
}
pids[i] = fork();
if(pids[i] == -1)
{
perror("fork");
continue;
}
if(pids[i] == 0)
{
// CHILD
// closing reading end
close(pipes[i][0]);
srand(time(NULL)+getpid());
int r = rand()% 201 + (-100);
printf("Child %zu: r = %d\n", i, r);
// sending value to parent
write(pipes[i][1], &r, sizeof r);
close(pipes[i][1]);
return 0;
}
// closing writing end
close(pipes[i][1]);
}
int sum = 0;
for(size_t i = 0; i < noc; ++i)
{
if(pids[i] == -2)
{
fprintf(stderr, "Pipe could not be created for child %zu\n", i);
continue;
}
if(pids[i] == -1)
{
fprintf(stderr, "Child %zu was not started\n", i);
close(pipes[i][0]);
continue;
}
int status;
if(waitpid(pids[i], &status, 0) == -1)
{
fprintf(stderr, "Could not wait for child %zu\n", i);
close(pipes[i][0]);
continue;
}
if(WIFEXITED(status) && WEXITSTATUS(status) == 0)
{
int v;
if(read(pipes[i][0], &v, sizeof v) != sizeof(v))
{
fprintf(stderr, "Could not read from child %zu\n", i);
close(pipes[i][0]);
continue;
}
sum += v;
close(pipes[i][0]);
} else
printf("Child %zu did not exit normally\n", i);
}
printf("The sum is: %d\n", sum);
return 0;
}
Gives me the output:
Child 0: r = -6
Child 1: r = 63
Child 3: r = 78
Child 2: r = 77
Child 4: r = -47
The sum is: 165
So the technique here is the creation of the pipes with the pipe. A pipe
is a unidirectional data channel that can be used for interprocess communicationcite.
With a pipe two processes can communicate with each other, but the pipe has only
one direction. In this example the child process will write into the pipe and
the parent will read from the pipe.
That's why before doing the fork, the parent creates the pipe, does the fork
and then closes the it's writing end of the pipe. The child closes it's reading
end of the pipe. Then the child calculates the value and writes into the pipe
the value it calculated and exists with the status 0.
After creating the children the parent waits for the children to terminate. If
the children terminate normally and with exit status 0, the parent reads from
the pipe and gets the calculated value of the child.
Btw, as David C. Rankin points out in the comments, your method of getting
a random value in the range [-100, 100] is incorrect. rand()% 201 + (-100)
would give you values between -100 and 100, because rand()%201 gives you a
value between 0 and 200.
edit2
OP asked in the comments
based on my understanding can I just return randonNum instead of exit(0) and do the computation where I calling wait(NULL) and call wait(randomNum)?
Yes, you can use the exit status of a process to send information back to the
parent without the need of creating a pipe. But I think this is not a particular
good solution for these reasons:
the exit status in Unix/POSIX is a unsigned 8-bit value, meaning the exit
codes are in the range [0, 255]. So if your random value is let's say -1, the
parent process will see 255. In your case that wouldn't be such a problem,
because you for values greater than 127, you can subtract 256 to get the
negative value.
You can only return an (unsigned) 8-bit value. If your child process has to
send something more "complex" like a 16-bit value, a float, double, or a
struct, you cannot use the exit status, so you
are limiting what you can return to the parent. When you want to return
something more "complex" than a 8-bit value, then a pipe is perfect tool for that.
I consider it as a hack to use the exit status to send other information
that is not an error value. The purpose of the exit status is that a process
can tell it's parent that it exited without an error by returning 0, or that it
exited with an error and the exit status has the error code. That's why I
consider it a hack, for me it's like using a screwdriver instead of a hammer for
nailing nails.
Your wait call would be invalid though, because wait expects a pointer to
int and you would need to use the macros WIFEXITED and WEXITSTATUS to get
the exit status. But the problem of using wait in this case is that wait
returns -1 on error and you wouldn't be able to tell for which child it returned
-1 and how many waits you have to
call to wait for the rest of the children. The children don't end in the same order as you
forked them, so you would need to keep track which child has been wait()ed.
It's much more simpler to use waitpid. With waitpid you can wait for a
particular child. I personally prefer waitpid here.
So, changing the code to do the same without pipes and using the exit status:
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char **argv)
{
if(argc != 2)
{
fprintf(stderr, "usage: %s num_of_children\n", argv[0]);
return 0;
}
int noc = atoi(argv[1]);
if(noc <= 0)
{
fprintf(stderr, "Invalid number of children\n");
return 1;
}
pid_t pids[noc];
for(size_t i = 0; i < noc; ++i)
{
pids[i] = fork();
if(pids[i] == -1)
{
perror("fork");
continue;
}
if(pids[i] == 0)
{
// CHILD
srand(time(NULL)+getpid());
int r = rand()% 201 + (-100);
printf("Child %zu: r = %d\n", i, r);
exit(r);
}
}
int sum = 0;
for(size_t i = 0; i < noc; ++i)
{
if(pids[i] == -1)
{
fprintf(stderr, "Child %zu was not started\n", i);
continue;
}
int status;
if(waitpid(pids[i], &status, 0) == -1)
{
fprintf(stderr, "Could not wait for child %zu\n", i);
continue;
}
if(WIFEXITED(status))
{
int v = WEXITSTATUS(status);
// checking if the child wrote a 8-bit negative value
// in 2-complement format
if(v > 127)
v -= 256;
printf("Parent: child %zu returned %d\n", i, v);
sum += v;
} else
fprintf(stderr, "Child %zu did exit abnormally, ignoring\n", i);
}
printf("The sum is: %d\n", sum);
return 0;
}
Gives me the output for 10 children:
Child 0: r = -59
Child 1: r = 73
Child 2: r = 61
Child 3: r = 98
Child 4: r = 18
Child 6: r = 31
Child 5: r = -88
Parent: child 0 returned -59
Parent: child 1 returned 73
Parent: child 2 returned 61
Child 8: r = 58
Parent: child 3 returned 98
Parent: child 4 returned 18
Parent: child 5 returned -88
Child 7: r = 53
Parent: child 6 returned 31
Child 9: r = -43
Parent: child 7 returned 53
Parent: child 8 returned 58
Parent: child 9 returned -43
The sum is: 202

Why the child process is not executed?

I have the following piece of code:
int main() {
int n = 1;
if(fork() == 0) {
printf("child");
n = n + 1;
exit(0);
}
n = n + 2;
printf("%d: %d\n", getpid(), n);
wait(0);
return 0;
}
The problem is that I don't understand why the child process is not executing.
The child process is executing only if i set sleep(1) in the parent process
Thanks in advance.
It is getting executed and it should be outputting the text. No newlines should be necessary:
https://ideone.com/a1tznH
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
int n = 1;
if(fork() == 0) {
printf("child");
n = n + 1;
exit(0);
}
n = n + 2;
printf("%ld: %d\n", (long)getpid(), n); //this is how you should print pids
wait(0);
return 0;
}
Example output:
child23891: 3
Perhaps you didn't notice the child text was at the beginning of your next prompt:
18188: 3
child[21:17] pskocik#laptop: $
The child is executed but two processes are trying to write on the same FD - STDOUT (File Descriptor).
If you want to see the result, put "\n" in printf of the child.
int main() {
int n = 1;
if(fork() == 0)
{
printf("child\n");
n = n + 1;
exit(0);
}
n = n + 2;
printf("%d: %d\n", getpid(), n);
wait(0);
return 0;
}
Try
pid_t pid;
pid = fork();
if(pid < 0)
{
printf("fail to fork");
}
else if (pid == 0)
{
printf("running child");
exit(0);
}
else
{
print("running parent");
wait(0);
print("child done");
}
return 0;
This is the basic structure of a program I wrote recently which works. Not totally sure why yours didn't work though.

read from a pipe is blocked after close writer

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char **argv) {
int childs[3];
for (int i = 0; i < 3; ++i) {
int p[2];
if (pipe(p) == -1) { perror("pipe"); exit(1); }
pid_t pid = fork();
if (pid) {
close(p[0]);
childs[i] = p[1];
}
else {
close(p[1]);
printf("child %d start\n", i + 1);
char buf[10];
buf[0] = 0;
int r;
if ((r = read(p[0], buf, 9)) == -1) { ... }
printf("child %d read %s (%d), finish\n", i + 1, buf, r);
sleep(2);
exit(0);
}
}
for (int i = 0; i < 3; ++i) {
// if (argc > 1) {
// write(childs[i], "42", 2);
// }
// ============== HERE >>>
close(childs[i]);
}
pid_t pid;
while ((pid = waitpid(-1, NULL, 0)) > 0) {
printf("child %d exited\n", pid);
}
return 0;
}
Output with comment:
child 1 start
child 2 start
child 3 start
child 3 read (0), finish
The next line is displayed after 2 seconds
child 2 read (0), finish
The next line is displayed after 2 seconds
child 1 read (0), finish
I do not write to the channel in the parent. Closing it, I want to give a signal to the child that will be waiting in the read.
It seems that there is a following. Сhild N expected finishes reading from the result 0, it's ok. Children 2 (N-1) and 1 are locked in a read to a child 3 is completed. Then the child 1 is similar will wait.
Why lock occur?
Child processes inherit open file descriptors from their parent. Your main process opens file descriptors in a loop (using pipe, keeping only the write ends). Child 1 inherits no descriptors (except for stdin/stdout/stderr); child 2 inherits childs[0] (the descriptor going to child 1); child 3 inherits childs[0] and childs[1] (the descriptors going to child 1 and 2).
read on a pipe blocks as long as any write descriptor is still open (because it could be used to send more data). So child 1 waits (because child 2 and child 3 still have an open write descriptor) and child 2 waits (because child 3 still has an open write descriptor); only child 3 sleeps and exits. This causes its file descriptors to close, which wakes up child 2. Then child 2 sleeps and exits, closing its file descriptors, which finally wakes up child 1.
If you want to avoid this behavior, you have to close the open file descriptors in each child:
else {
for (int j = 0; j < i; j++) {
close(childs[j]);
}
close(p[1]);
printf("child %d start\n", i + 1);
The write ends of the pipes are getting inherited by the children.
Since filedescriptor are ref-counted, the write end is only considered closed if all references to it are closed.
Below is your code, slightly refactored, with a fix added:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char **argv) {
int children_w[3];
for (int i = 0; i < 3; ++i) {
int p[2];
if (0>pipe(p))
{ perror("pipe"); exit(1); }
pid_t pid;
if(0> (pid= fork()))
{ perror("fork"); exit(1); }
if(pid==0) {
/* Fix -- close the leaked write ends */
int j;
for(j=0; j<i; j++)
close(children_w[j]);
/* end fix*/
close(p[1]);
printf("child %d start\n", i + 1);
char buf[10];
buf[0] = 0;
int r;
if ((r = read(p[0], buf, 9)) == -1) { perror("read");/*...*/ }
printf("child %d read %s (%d), finish\n", i + 1, buf, r);
sleep(2);
exit(0);
}
children_w[i] = p[1];
close(p[0]);
}
for (int i = 0; i < 3; ++i) {
// if (argc > 1) {
// write(childs[i], "42", 2);
// }
// ============== HERE >>>
close(children_w[i]);
}
pid_t pid;
while ((pid = waitpid(-1, NULL, 0)) > 0) {
printf("child %d exited\n", pid);
}
return 0;
}

Resources