How to implement pipes for multiple processes? - c

I am creating multiple processes and I need to create two unnamed pipes for each process.
For each child, one pipe will be used to get int value from parent; one for sending to int arrays to parent. Parent will do some things while getting new data from childs.
The base code:
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // for reaching unix operations
int main(int argc, char *argv[]){
pid_t main = getpid();
int N = 30;
int i;
pid_t* children = (pid_t*) malloc(sizeof(pid_t) * N);
for(i = 0; i < N; i++){
pid_t child = fork();
if ( child == 0){
pid_t me = getpid();
printf("I'm a child and my pid is: %d\n", me);
sleep(1);
// exit(4);
return me * 2;
} else if ( child < 0){
// printf("Could not create child\n");
} else {
children[i] = child;
// printf("I have created a child and its pid %d\n", child);
}
}
// The child never reaches here
for(i = 0; i < N; i++){
int status;
waitpid(children[i], &status, 0);
printf("Process %d exited with return code %d\n", children[i], WEXITSTATUS(status));
}
return 0;
}
I tried many things with no success and I'm lost. Can you help me to continue?
Any help is appreciated! Thank you.

Here's how to set up one pipe for each child process so that each child writes to the parent:
Since you need two file descriptors for each child, declare:
int fd[2 * N];
Initialize them appropriately:
for (int i = 0; i < N; i++) {
pipe(&fd[2*i]);
}
Inside the i-th child process, use:
write(fd[2*i + 1], write_buffer, SIZE)
to write to the parent, and in the parent use:
read(fd[2*i], read_buffer, SIZE)
to read from the i-th child.
To close the pipes:
Inside the i-th child, you can use
close(fd[2*i])
right away, seeing as you're only writing. After you're done writing call
close(fd[2*i + 1])
to close the write end of the pipe.
The situation is parallel in the parent: when reading from the i-th child you can
close(fd[2*i + 1])
right away, since you're not writing, and after you're done reading call
close(fd[2*i])
to close the read end of the pipe.
Since you need two pipes per child process, create two arrays - one containing pipes for the children writing to the parent, and one containing pipes for the parent writing to the children.

Related

Creating multiple processes via "fork" for one parent process

I would like to create a small program that will accept user input from stdin for the number of processes they would like, and then, my program will fork the n number of processes as specified by the user. Later on, I would like to pipe data from the child to the parent.
However, I want only one parent process. I have been trying to figure out the algorithm for this, and perhaps I am overcomplicating it, but I am stuck.
Do note I can only use the fork and pipe features in C (so nothing too crazy!)
Here is my algorithm.
Loop only if I am a parent process, and do not loop if I am a child process.
If I am a parent process entering the loop, then I will call fork(). Otherwise, I am a child, and I will do some child-related tasks (which I may then pipe back to the parent later on). The child should not re-enter the loop as to avoid creating children-of-children processes.
Does that make any sense?
What would you advise me to do?
Let's say n is the number of children you get as input. Let's see what you could do, if you use one pipe for each child.
In the parent process:
pid_t pid;
int fd[n][2];
for(i = 0; i < n; i++) {
pipe(fd[i]);
pid = fork();
if (pid < 0) {
perror("whatever");
exit(1);
}
else if (pid == 0) {
for(j = 0; j < i; j++) {
if (close(fd[j][0]) < 0) {
perror("closing fd[0]");
exit(1);
}
if (close(fd[j][1]) < 0) {
perror("closing fd[1]");
exit(1);
}
}
func(fd[i]);
}
}
// other parent stuff next && close file discriptors not needed
And your func() should be what the children have to do. It takes as arguments the 2 file descriptors of the child's pipe. Note that in the end of func you should exit().
A solution making a pipe for each child would be a little better but a little more complex than that (hint: you may pass fd's as arguments, also close all fd's with caution!)
Also, you may keep each child's pid by defining pid_t pid[n]; instead of pid, and refer to each pid as pid[i].
Don't forget to wait for every child to die!
If it were me, I would move all of the fork() and pipe() stuff into its own subroutine, with clear semantics, and call that subroutine from a loop in main().
In the example below, spawn() forks, invokes the work function in child, ensures that the child exits appropriately, and returns in the parent.
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
/* Launch a child. This routine exit()s in the child and
* return()s in the parent */
void spawn(void (*fn)(int), int *fd) {
int pipefd[2];
int pid;
if(pipe(pipefd) < 0) {
perror("pipe");
exit(1);
}
switch(pid = fork()) {
case -1: /* Error */
perror("fork");
exit(1);
break;
case 0: /* Child */
close(pipefd[0]); /* Kids only talk */
fn(pipefd[1]); /* Put the kid to work */
exit(0); /* Kill the kid */
break;
default: /* Parent */
close(pipefd[1]); /* Parents only listen */
*fd = pipefd[0];
printf("Spawning PID=%d, FD=%d\n", pid, *fd);
break;
}
}
int
get_number_of_children() {
/* TODO: Do stdin-reading here and return a good number */
return 3;
}
void do_work(int fd) {
/* TODO: Whatever work the children might do */
/* For example: */
write(fd, "hello", 5);
}
int main (int ac, char **av) {
int nkids = get_number_of_children();
int fd_array[nkids];
int pid;
/* Birth the children */
for(int i = 0; i < nkids; i++) {
spawn(do_work, &fd_array[i]);
}
/* TODO: Read the data from the file descriptors in fd_array */
/* Finally, wait for all children to die */
while((pid = wait(0)) != -1) {
printf("Waited PID=%d\n", pid);
}
}

How to differentiate child processes?

Say I fork N children. I want to create pipes between 1 and 2, 2 and 3, 4 and 5, ... and so on. So I need some way to figure out which child is which. The code below is what I currently have. I just need some way to tell that child number n, is child number n.
int fd[5][2];
int i;
for(i=0; i<5; i++)
{
pipe(fd[i]);
}
int pid = fork();
if(pid == 0)
{
}
The following code will create a pipe for each child, fork the process as many times as it is needed and send from the parent to each child an int value (the id we want to give to the child), finally the children will read the value and terminate.
Note: since you are forking, the i variable will contain the iteration number, if the iteration number is the child id, then you do not need to use pipe.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int count = 3;
int fd[count][2];
int pid[count];
// create pipe descriptors
for (int i = 0; i < count; i++) {
pipe(fd[i]);
// fork() returns 0 for child process, child-pid for parent process.
pid[i] = fork();
if (pid[i] != 0) {
// parent: writing only, so close read-descriptor.
close(fd[i][0]);
// send the childID on the write-descriptor.
write(fd[i][1], &i, sizeof(i));
printf("Parent(%d) send childID: %d\n", getpid(), i);
// close the write descriptor
close(fd[i][1]);
} else {
// child: reading only, so close the write-descriptor
close(fd[i][1]);
// now read the data (will block)
int id;
read(fd[i][0], &id, sizeof(id));
// in case the id is just the iterator value, we can use that instead of reading data from the pipe
printf("%d Child(%d) received childID: %d\n", i, getpid(), id);
// close the read-descriptor
close(fd[i][0]);
//TODO cleanup fd that are not needed
break;
}
}
return 0;
}

Synchronization of parent and child with SIGUSR signal in C. Make parent and child read one after the other

I have created a two way communication between parent and child processes using two pipes. Parent and child write data and I was able to make them read the data from each other. Parent writes numbers 1 to 5, and child writes numbers from 6 to 10. But I want parent to start reading data the first, and then reading continues in this order switching from parent to child until all the data are read: 6,1,7,2,8,3,9,4,10,5. I have tried to synchronize the reading with SIGUSR1 but when the parent is reading for the second time the program stops. I have searched a lot to find where the problem can be, and tried some tips and alike working examples, but nothing seems to help. Here is my code:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
void paction(int dummy)
{
printf("P*************************************************\n");
}
void caction(int dummy)
{
printf("C*************************************************\n");
}
int main()
{
int pfd[2];
int pfd2[2];
pid_t cfork, pfork;
if (pipe(pfd) == -1 || pipe(pfd2) == -1) {
fprintf(stderr,"Pipe failed");
exit(1);
}
cfork = fork();
signal(SIGUSR1, paction);
if (cfork == -1) {
printf("Fork Failed\n");
exit(1);
}
else if (cfork > 0) { /*parent process*/
int numbers[] = {1, 2,3, 4, 5};
int numbers2[] = { 6, 7,8, 9, 10 };
close(pfd[0]); /*close read end, write and then close write end*/
/*write part*/
int limit = 5;
int i;
for (i = 0; i < limit; i++) {
printf("Parent sends: %d\n", numbers[i]);
write(pfd[1], &numbers[i], sizeof(numbers[i]));
printf("Child sends: %d\n", numbers2[i]);
write(pfd2[1], &numbers2[i], sizeof(numbers2[i]));
}
printf("***************************************************\n");
close(pfd[1]);
close(pfd2[1]);
/*read part/////////////////////////////////////////*/
int temp;
int reads = 5;
int j;
for (j = 0; j < reads; j++) {
sleep(1);
read(pfd2[0], &temp, sizeof(temp));
printf("Parent gets: %d\n", temp);
kill(cfork, SIGUSR1);
pause();
}
/*printf("***************************************************\n");*/
kill( cfork, SIGUSR1 );
close(pfd2[0]);
}
else { /*child process*/
signal(SIGUSR1, caction);
close(pfd[1]);
int temp;
int reads = 5;
int j;
pfork = getppid();
for (j = 0; j < reads; j++) {
sleep(1);
read(pfd[0], &temp, sizeof(temp));
printf("Child gets: %d\n", temp);
kill(getppid(), SIGUSR1);
pause();
}
/*printf("***************************************************\n");*/
close(pfd[0]);
close(pfd2[0]);
}
return 0;
}
My output looks like this:
> Parent sends:1
> Child sends:6
> Parent sends:2
> Child sends:7
> Parent sends:3
> Child sends:8
> Parent sends:4
> Child sends:9
> Parent sends:5
> Child sends:10
> **************************************************************
Parent gets:6
> C************************************************************
> Child gets:1
> P*************************************************************
> Parent gets:7
And here is when it stops.
If someone can help me I would really appreciate it because I really want to know where the problem is, and since I am a beginner in C programming and processes!
Thank you in advance
printf() is not an async-safe function. Calling printf() in both normal code and a signal handler will cause undefined behavior. In particular, printf() may need to take a lock on the output-stream, while taking locks in signal-handlers is very inadvisable (risk of self-deadlock).
Maybe it is a bad idea to use signals, but I had a task in which it was assigned to use SIGUSR1. I solved the issue by adding:
static struct sigaction pact, cact;
/* set SIGUSR1 action for parent */;
pact.sa_handler = p_action;
sigaction(SIGUSR1, &pact, NULL);
After the parent was assigned the first action, it worked fine.
Thank you:)

how to create two processes from a single Parent

I know I'm going to need to use fork(), but this just creates a single child process. Do i simply call fork again from within the child process? Also, I need them to communicate through a signal or pipe, which is easier to implement and what do i need to know for doing that (functions, etc..)
To create a second process, call fork() again - either within the parent or the child (but not both!). Which you choose depends on whether you want this process to be a child of the original parent or a child of the first child process (it is usual for it to be a child of the original parent).
Communicating through a pipe is much simpler and more reliable than using signals. pipe(), close(), read(), write() and select() are the key functions here.
For example, to have the parent create two child processes, you would do something like:
pid_t child_a, child_b;
child_a = fork();
if (child_a == 0) {
/* Child A code */
} else {
child_b = fork();
if (child_b == 0) {
/* Child B code */
} else {
/* Parent Code */
}
}
Another fancy code using && operator:
pid_t c1_pid, c2_pid;
(c1_pid = fork()) && (c2_pid = fork()); // Creates two children
if (c1_pid == 0) {
/* Child 1 code goes here */
} else if (c2_pid == 0) {
/* Child 2 code goes here */
} else {
/* Parent code goes here */
}
#include <stdio.h>
#include <unistd.h>
void main(){
int pi_d ;
int pid ;
pi_d = fork();
if(pi_d == 0){
printf("Child Process B:\npid :%d\nppid:%d\n",getpid(),getppid());
}
if(pi_d > 0){
pid = fork();
if(pid > 0){
printf("\nParent Process:\npid:%d\nppid :%d\n",getpid(),getppid());
}
else if(pid == 0){
printf("Child Process A:\npid :%d\nppid:%d\n",getpid(),getppid());
}
}
}
output :
Parent Process:
pid:3648
ppid :2379
Child Process B:
pid :3649
ppid:3648
Child Process A:
pid :3650
ppid:3648
You can put the fork in a loop and generate as many child processes as you need.
I did that on a project recently.
for(nSon=0; nSon < nSonsAsked; nSon++) {
Log_Print("Setup son #%.2u ", nSon+1);
if((pid = fork()) == 0) {
/* Do child stuff init, like connect the pipes, close shared handles */
return iTMInChild(...); /* A specific function of the child work */
/* The life of the child should not go beyond that point, i.e. the loop is over
or else the child will spawn even more processes. */
}
else if(pid > 0) {
/* Father process stuff. Here I initialise an array with the pid of the forked */
/* processes, this way I can index with the number of processes.*/
pid[nSon] = pid;
}
else
return Err_Print(ERR_FORK_FAILED, "fork failed. errno=%d \"%s\"\n", errno, strerror(errno));
}
Log_Print() and Err_Print() are internal functions but quite obvious so I let them like they are.
There is one aspect with the variables that has to be explained. nSon and nSonAsked should be declared as globals not as stack variables. This way, their value persists in the forked process. This means that the nSon variable will have a different value in each of the children. This allows it to have a simpler numbering scheme than the ownpid() number.
To get it completely right, there are a lot of details to get right. You will have to set signal handlers in the father process to detect the death of a child, likewise the other way round (only possible on Linux, other Unix (at least Solaris) do not support parent death signals).
You have to be aware that open file descriptors in the father process will be also open in the child after fork and it will be the same one. This opens a lot of concurrency problems if you're not aware of it (the solution is using dup() and close() in the right places).
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
system ("clear");
int i ;
pid_t childa,childb,childa1,childa2,childb1,childb2;
printf("\n \t \t I am the parent process with ID %d \n",getpid());
childa=fork();
if (childa == 0 )
{
printf("\nI am a child A with PID %d and my parent ID is %d\n",getpid(),getppid());
}
else
{
childb = fork();
if (childb == 0)
{
printf("\nI am Child B with ID %d and my parent ID is %d\n",getpid(),getppid());
}
else
{
sleep(1);
}
}
}
In this example they are just sleeping for a few random sec. It also has all the pid, so we can send SIGNAL to communicate... Most of the #includes are commented cause they were useless where I compiled.
#include <stdlib.h> // exit() ...
#include <stdio.h> // printf() ...
// Compile with -lrt -> cc file_name.c -lrt
//#include <fcntl.h>
//#include <sys/stat.h>
//#include <sys/types.h>
//#include <sys/wait.h> // may need this for wait()
//#include <time.h>
//#include <unistd.h> // and this one for fork()
// In the start function you can do whatever you want.
void start (const int azon) {
// For children processes
srand( time(NULL) );
unsigned t = rand()%5; // printf("%d\n", t);
sleep(t);
printf("%d. process reached the end.\n", azon);
exit(0);
}
int main() {
const int N = 5;
pid_t pids[N];
int i;
// The 'for' loop make 'N' process with 'fork()'.
// The children processes will call the start function.
// Since after fork() you will get 2 process. One Parent, and One Child
// The returning value from fork() is saved in "pids" which is an
// integer AND it is (<0) IF something went wrong.
// it is (>0) IF 'we are' in the Parent process,
// because this number is the Child process' ID (pid).
// and Last it is (==0) IF 'we are' in the Child process.
for (i = 0; i < N; i++) {
pids[i] = fork();
sleep(1);
if (pids[i] == 0) start(i+1); // ... OR you can make a switch(..)
}
// This 'for' loop in the wait(NULL) statement ONLY move on when a
// process ended, so it waits until 'N' proc ends.
for (i = 0; i < N; i++)
wait(NULL);
printf("Partent process reached the end\n");
return 0;
}
Just a little contribution, if you want to create 2 childs from the same parent you could use this code below. In which one father create 2 child processes (lazy and active).
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main (){
pid_t lazy_child;
lazy_child = fork();
if(lazy_child == 0){ // This is the lazy child process.
printf("LAZY CHILD:%d\n", getpid());
}
else if(lazy_child > 0){ // This is the father process.
pid_t active_child = fork();
if(active_child == 0){ // This is the active child process.
printf("ACTIVE CHILD:%d\n", getpid());
}
else if(active_child > 0){ // This is the father process.
printf("FATHER:%d\n", getpid());
}
else{ // Fork doesnt work.
printf("fork error\n");
exit(1);
}
}
else{ // Fork doesnt work.
printf("fork error\n");
exit(1);
}
return 0;
}
If you run this code, you should get a similar output:
$ ./a.out
FATHER:14501
ACTIVE CHILD:14503
LAZY CHILD:14502
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t AliceID, BobID;
double n=0;
int i1 =0;
/* fork a child process */
AliceID = fork();
if (AliceID < 0) { /* error occurred */
fprintf(stderr, "Fork Failed");
return 1;
}
else if (AliceID == 0) { /* child Alice code */
for(int i=1; i<11; i++)
{n = n+i;
i1++; }
double avg1 = n/i1;
printf("From Alice: the average of 1,2, …, 10 is the-average-she-calculated");
printf(" sum = %.2f and avg = %.2f \n",n, avg1);
}
else {
BobID = fork();
if (BobID == 0) { /* Child Bob code */
printf("From Bob: I am born to print this and then die.\n");
} else { /* Parent Code */
/* parent will wait for the child to complete */
wait(NULL);
printf("From parent: AliceID is %d \n", AliceID);
printf("From parent: Bob is %d \n", BobID);
printf("Parent ID %d \n", getpid());
}
}
return 0;
}

Reaping zombie process - child

I am taking command line arguments to main from parent to child and counting them and printing. My question is that i am not sure that i am reaping the child? dont i just need an exit 0
or do i need to call fork again?
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[])
{
int length = 0;
int i, n;
int fdest[2]; // for pipe
pid_t pid; //process IDs
char buffer[BUFSIZ];
if ((pid = fork()) < 0) /* attempt to create child / parent process */
{
printf("fork error");
}
if (pipe(fdest) < 0) /* attempt to create pipe */
printf("pipe error");
/* parent process */
else if (pid > 0) {
close(fdest[0]);
for(i = 1; i < argc; i++) /* write to pipe */
{
write(fdest[1], argv[i], strlen(argv[1]));
}
} else {
/* child Process */
close(fdest[1]);
for(i = 1; i < argc; i++)
{
length +=( strlen(argv[i])); /* get length of arguments */
}
n = read(fdest[0], buffer, length);
printf("\nchild: counted %d characters\n", n);
}
exit(0);
}
No, you are not reaping the child correctly. In your case, if the child process finishes before the parent process exits, the child will become a zombie. Then, when the parent process finishes, the child will be reparented to init (whether it has finished and is a zombie, or is still running). init is then reaping the child for you.
To reap the child, add a call to wait() before exit.
By the way, you have another bug - you are creating the pipe after the fork, so the parent and child each create a (different) pipe - they're not connected. Move the if (pipe(... up before the fork().

Resources