So I have a project to do but I am completely stumped. I have spent ten hours and have gotten nowhere. I don't specifically want the code to the answer, but some pseudocode and good hints in the right direction would help a heap!!
It forks a number of processes, k - a command-line argument, connected by pipes - each process is connected to the next, and the last process is connected to the first. Process number k sends its message on to process number (k+1)%n.
Process 0 reads a line from stdin. It then transmits it to process 1. Each other process reads the line, increments the first byte of the string by 1, and then relays the line to the next process. As it relays, it prints a status message (shown below).
When the message gets back to process 0, it is output to the standard output as well. When a process receives EOF (either from pipe, if its a process other than 0, or from stdin, for process 0), it prints the final string. This will close all pipes.
The expected output is:
$ ./ring 4
hello
process #0 (32768) sending message: hello
process #1 (32769) relaying message: iello
process #2 (32770) relaying message: jello
process #3 (32767) relaying message: kello
I hear kello
^C
$
What I have written so far:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define BUFFER_SIZE 80
#define READ_END 0
#define WRITE_END 1
int main(int argc, char *argv[])
{
char readmsg[BUFFER_SIZE], readmsg2[BUFFER_SIZE], final[BUFFER_SIZE];
int pid, process;
int parent_child[2], child_parent[2];
process = 0;
if (pipe(child_parent) == -1) {
fprintf(stderr, "Pipe failed");
return 1;
}
if (pipe(parent_child) == -1) {
fprintf(stderr, "Pipe failed");
return 1;
}
pid = fork();
if (pid < 0) {
fprintf(stderr, "Fork failed");
return 1;
} else if (pid > 0) {
/* PARENT */
read(0, &readmsg, BUFFER_SIZE);
printf("process #%d (%d) sending message: %s",
0, getpid(), readmsg);
write(parent_child[WRITE_END], &readmsg, BUFFER_SIZE);
close(parent_child[WRITE_END]);
} else {
/* CHILD */
read(parent_child[READ_END], &readmsg2, BUFFER_SIZE);
readmsg2[0] += 1;
printf("process #%d (%d) relaying message: %s",
1, getpid(), readmsg2);
process += 1;
write(child_parent[WRITE_END], &readmsg2, BUFFER_SIZE);
}
read(child_parent[READ_END], &final, BUFFER_SIZE);
printf("I hear %d %s", pid - getpid(), final);
return 0;
}
What it does currently is read in a string from stdin, pass it to the first process and print process 0 (can't actually get the 0 though, simply printing 0), it then pipes the string to process 1 which distorts byte 1 and then writes to a pipe again and then outside of the pipes, the string is read and outputs the distorted string.
$ ./ring
hello
process #0 (6677) sending message: hello
process #1 (6678) relaying message: iello
I hear -6678 iello
^C
$
I don't know where to go from here. Thank you in advance, anything will help!!
Given some help this is what I have now:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define BUFFER_SIZE 80
#define READ_END 0
#define WRITE_END 1
int main(int argc, char **argv) {
char buf[BUFFER_SIZE];
int process, rings, pid, pid_n, pid_n1, pid_1, i;
int Pn[2]; //Pipe for process n -> 0
int Pn_1[2]; //Pipe for process n-1 -> n
int Pn_2[2]; //Pipe for process n-2 -> n-1
int P_0[2]; //Pipe for process 0 -> 1
process = 0;
if (argc == 2) {
rings = atoi(argv[1]);
} else {
fprintf(stderr, "Usage: %s n, where n is number of rings\n", argv[0]);
exit(EXIT_FAILURE);
}
if ((pid = fork()) < 0) {
fprintf(stderr, "Fork failed");
return 1;
} else if (pid == 0) {
if ((pid_n = fork()) < 0) {
fprintf(stderr, "Fork failed");
return 1;
} else if (pid_n == 0) {
/* CHILD */
close(Pn[WRITE_END]);
close(Pn_1[READ_END]);
} else {
/* PARENT */
close(Pn[READ_END]);
close(Pn_1[WRITE_END]);
}
for (i = 0; i < rings; i++) {
if ((pid_n1 = fork()) < 0) {
fprintf(stderr, "Fork failed");
return 1;
} else if (pid_n1 == 0) {
/* CHILD */
close(Pn_1[WRITE_END]);
close(Pn_2[READ_END]);
} else {
/* PARENT */
close(Pn_1[READ_END]);
close(Pn_2[WRITE_END]);
}
}
/* Not sure about these last ones */
if ((pid_1 = fork()) < 0) {
fprintf(stderr, "Fork failed");
return 1;
} else if (pid_1 == 0) {
/* CHILD */
close(P_n2[WRITE_END]);
close(P_0[READ_END]);
} else {
/* PARENT */
close(P_n2[READ_END]);
close(P_0[WRITE_END]);
}
} else {
/* PARENT */
read(0, &buf, BUFFER_SIZE);
buf[BUFFER_SIZE - 1] = '\0';
printf("process first # (%d) sending message: %s", getpid(), buf);
write(P_0[WRITE_END], &buf, BUFFER_SIZE);
read(Pn[READ_END], &buf, BUFFER_SIZE);
buf[BUFFER_SIZE - 1] = '\0';
printf("I hear %s", buf);
}
return 0;
}
This is a diagram I drew for myself showing how the processes are to be interconnected:
p4
C5 <--------- C4
/ \
p5 / p3 \
/ \
o----> C0 ---->o C3
\ /
p0 \ p2 /
\ /
C1 ---------> C2
p1
The Cn represent the processes; C0 is the parent process. The pn represent the pipes; the other two lines are standard input and standard output. Each child has a simple task, as befits children. The parent has a more complex task, mainly ensuring that exactly the right number of file descriptors are closed. In fact, the close() is so important that I created a debugging function, fd_close(), to conditionally report on file descriptors being closed. I used that too when I had silly mistakes in the code.
The err_*() functions are simplified versions of code I use in most of my programs. They make error reporting less onerous by converting most error reports into a one-line statement, rather than requiring multiple lines. (These functions are normally in 'stderr.c' and 'stderr.h', but those files are 750 lines of code and comment and are more comprehensive. The production code has an option to support prefixing each message with a PID, which is also important with multi-process systems like this.)
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
enum { BUFFER_SIZE = 1024 };
typedef int Pipe[2];
static int debug = 0;
static void fd_close(int fd);
/* These functions normally declared in stderr.h */
static void err_setarg0(const char *argv0);
static void err_sysexit(char const *fmt, ...);
static void err_usage(char const *usestr);
static void err_remark(char const *fmt, ...);
static void be_childish(Pipe in, Pipe out)
{
/* Close irrelevant ends of relevant pipes */
fd_close(in[1]);
fd_close(out[0]);
char buffer[BUFFER_SIZE];
ssize_t nbytes;
while ((nbytes = read(in[0], buffer, sizeof(buffer))) > 0)
{
buffer[0]++;
if (write(out[1], buffer, nbytes) != nbytes)
err_sysexit("%d: failed to write to pipe", (int)getpid());
}
fd_close(in[0]);
fd_close(out[1]);
exit(0);
}
int main(int argc, char **argv)
{
err_setarg0(argv[0]);
int nkids;
if (argc != 2 || (nkids = atoi(argv[1])) <= 1 || nkids >= 10)
err_usage("n # for n in 2..9");
err_remark("Parent has PID %d\n", (int)getpid());
Pipe pipelist[nkids];
if (pipe(pipelist[0]) != 0)
err_sysexit("Failed to create pipe #%d", 0);
if (debug)
err_remark("p[0][0] = %d; p[0][1] = %d\n", pipelist[0][0], pipelist[0][1]);
for (int i = 1; i < nkids; i++)
{
pid_t pid;
if (pipe(pipelist[i]) != 0)
err_sysexit("Failed to create pipe #%d", i);
if (debug)
err_remark("p[%d][0] = %d; p[%d][1] = %d\n", i, pipelist[i][0], i, pipelist[i][1]);
if ((pid = fork()) < 0)
err_sysexit("Failed to create child #%d", i);
if (pid == 0)
{
/* Close irrelevant pipes */
for (int j = 0; j < i-1; j++)
{
fd_close(pipelist[j][0]);
fd_close(pipelist[j][1]);
}
be_childish(pipelist[i-1], pipelist[i]);
/* NOTREACHED */
}
err_remark("Child %d has PID %d\n", i, (int)pid);
}
/* Close irrelevant pipes */
for (int j = 1; j < nkids-1; j++)
{
fd_close(pipelist[j][0]);
fd_close(pipelist[j][1]);
}
/* Close irrelevant ends of relevant pipes */
fd_close(pipelist[0][0]);
fd_close(pipelist[nkids-1][1]);
int w_fd = pipelist[0][1];
int r_fd = pipelist[nkids-1][0];
/* Main loop */
char buffer[BUFFER_SIZE];
while (printf("Input: ") > 0 && fgets(buffer, sizeof(buffer), stdin) != 0)
{
int len = strlen(buffer);
if (write(w_fd, buffer, len) != len)
err_sysexit("Failed to write to children");
if (read(r_fd, buffer, len) != len)
err_sysexit("Failed to read from children");
printf("Output: %.*s", len, buffer);
}
fd_close(w_fd);
fd_close(r_fd);
putchar('\n');
int status;
int corpse;
while ((corpse = wait(&status)) > 0)
err_remark("%d exited with status 0x%.4X\n", corpse, status);
return 0;
}
static void fd_close(int fd)
{
if (debug)
err_remark("%d: close(%d)\n", (int)getpid(), fd);
if (close(fd) != 0)
err_sysexit("%d: Failed to close %d\n", (int)getpid(), fd);
}
/* Normally in stderr.c */
static const char *arg0 = "<undefined>";
static void err_setarg0(const char *argv0)
{
arg0 = argv0;
}
static void err_usage(char const *usestr)
{
fprintf(stderr, "Usage: %s %s\n", arg0, usestr);
exit(1);
}
static void err_vsyswarn(char const *fmt, va_list args)
{
int errnum = errno;
fprintf(stderr, "%s:%d: ", arg0, (int)getpid());
vfprintf(stderr, fmt, args);
if (errnum != 0)
fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
putc('\n', stderr);
}
static void err_sysexit(char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
err_vsyswarn(fmt, args);
va_end(args);
exit(1);
}
static void err_remark(char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
Example output:
$ ./pipecircle 9
Parent has PID 34473
Child 1 has PID 34474
Child 2 has PID 34475
Child 3 has PID 34476
Child 4 has PID 34477
Child 5 has PID 34478
Child 6 has PID 34479
Child 7 has PID 34480
Child 8 has PID 34481
Input: Hello
Output: Pello
Input: Bye
Output: Jye
Input: ^D
34474 exited with status 0x0000
34477 exited with status 0x0000
34479 exited with status 0x0000
34476 exited with status 0x0000
34475 exited with status 0x0000
34478 exited with status 0x0000
34480 exited with status 0x0000
34481 exited with status 0x0000
$
It seems to me that you are pretty close, as this works for two processes. What you need to do now is loop to create more processes from the parent.
(k=N+1 processes: proc0 = parent, proc1, ..., procN)
Create a pipe Pn, that will be for procN->proc0
Create a pipe Pn-1, that will be for procN-1->procN
Create relaying fork procN
fork closes Pn output and Pn-1 input
parent closes Pn input and Pn-1 output
(loop here)
Create a pipe Pi-2, that will be for procI-2->procI-1
Create relaying fork procI-1
fork closes Pi-1 output and Pi-2 input
parent closes Pi-1 input and Pi-2 output
...
Create a pipe P0 that will be for proc0->proc1
Create relaying fork proc1
fork closes P1 output and P0 input
parent closes P1 input and P0 output
(end loop)
(parent final code:)
Read from stdin
Write on P0
Read on Pn
Write on stdout
Once created with fork(), the child processes (i.e. apart from proc0) close the input of the pipe (the output of the other is already closed!), read the message on one, write on the other and exit.
Some remarks on your current code:
The child shouldn't execute that list bit, when you read on child_parent.
You don't need that many buffers (you only need one, that will turn into one per process after the fork).
Put some terminating null bytes before printing :)
It's good practice to close the ends that you're not going to need
I am new to pipes but how do I redirect the output from child_1 to the input for child_2?
I am trying to pass the value from the parent to child_1, adds 1 to the value, print the value, then use that output and pass it into child_2, add 1 again, and finally print the value.
The code below has the right output value for child_1, but not for child_2, how do I redirect the output from child_1 to the input for child_2?
Here is my code so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char * argv[]) {
int fd[2];
int PID;
pipe(fd); //fd1[0] = read | fd1[1] = write
PID = fork(); // spawn child_1
if (PID < 0){ // failed to fork
perror("Unable to fork child");
exit(1);
}
if (PID > 0) { // parent
int value = 100;
// since parent is only writing, close the reading end of pipe
close(fd[0]);
// write the data to the write end of the pipe
write(fd[1], &value, sizeof(int));
// then close the writing end of the pipe (parent)
close(fd[1]);
/**********************************************************/
} else { // child 1
int val = 0;
// read from the parent pipe
read(fd[0], &val, sizeof(int));
val += 1;
// is this how to redirect from one pipe to another?
dup2(fd[0], fd[1]);
// this prints the right value for val (val [101] = value [100] + 1)
printf("Child [%d] read value %d\n", getpid(), val);
// close the reading end of the pipe for child_1
close(fd[0]);
int PID2 = fork(); // make child 2
if(PID2 == 0) { // child 2
int val2 = 0;
close(0); // close stdin since we are trying to take the value from child_1
// read input from child_1 pipe (NOT WORKING?)
read(fd[0], &val2, sizeof(int));
val2 += 1;
printf("Child [%d] out %d\n", getpid(), val2);
close(fd[0]);
}
}
return EXIT_SUCCESS;
}
The way you have things set up, there's no need to use dup2() or any other I/O redirection.
Add #include <unistd.h> to the list of include files (and remove #include <string.h> — it seems to be unused)
Delete: dup2(fd[0], fd[1]);
Delete: close(fd[0]);
Delete: close(0);
Before the second fork(), add write(fd[1], &val, sizeof(val));
When you have close(fd[0]) in the first child, you effectively close fd[0] for the second child too.
You should check the status of the read and write operations before using the results.
Those changes lead to:
/* SO 7383-1815 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
int fd[2];
int PID;
pipe(fd);
PID = fork();
if (PID < 0)
{
perror("Unable to fork child");
exit(EXIT_FAILURE);
}
if (PID > 0)
{
int value = 100;
close(fd[0]);
write(fd[1], &value, sizeof(int));
close(fd[1]);
}
else
{
int val = 0;
if (read(fd[0], &val, sizeof(val)) != sizeof(val))
{
perror("read() failed in child 1");
exit(EXIT_FAILURE);
}
val += 1;
printf("Child [%d] read value %d\n", getpid(), val);
if (write(fd[1], &val, sizeof(val)) != sizeof(val))
{
perror("write() failed in child 1");
exit(EXIT_FAILURE);
}
int PID2 = fork();
if (PID2 == 0)
{
int val2 = 0;
if (read(fd[0], &val2, sizeof(val2)) != sizeof(val2))
{
perror("read() failed in child 2");
exit(EXIT_FAILURE);
}
val2 += 1;
printf("Child [%d] out %d\n", getpid(), val2);
close(fd[0]);
close(fd[1]);
}
}
return EXIT_SUCCESS;
}
When compiled (cleanly with options set fussy), it produces output such as:
Child [34520] read value 101
Child [34521] out 102
I believe this is what was wanted.
Is it possible in C to have multiple concurrent 1-to-1 pipes?
My example use case is the following:
The parent creates x children processes using fork()
The parent creates x pipes, one for each child
Each child write to its respective pipe, then closes it.
The parent reads from the pipes then closes them.
My current draft attempt is as follows however I have doubts about its functionality:
// Create x pipes
int fd[2*x];
pipe(fd);
// In child i do (i from 0 to x-1 inclusive):
close(fd[2*i]);
write(fd[2*i +1], .....);
close(fd[2*i +1]);
// In parent do:
wait() // wait for children to finish
// while any pipe has content do:
// For each i from 0 to x-1 inclusive:
close(fd[2*i +1]);
read(fd[2*i], .....);
close(fd[2*i]);
I would really appreciate it if someone could show me a simple example of this concept at work. The end goal here is one way conversations between children to parent with multiple values that the parent will store into a single array.
This code more or less does what you want. It uses error reporting code that is available in my SOQ (Stack Overflow Questions) repository on GitHub as files stderr.c and stderr.h in the src/libsoq sub-directory.
#include "stderr.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
enum { NUM_CHILDREN = 5 };
enum { MSG_BUFFSIZE = 64 };
static void be_childish(int kid, int *fd);
int main(int argc, char **argv)
{
int fd[2 * NUM_CHILDREN];
int pid[NUM_CHILDREN];
err_setarg0(argv[0]);
err_setlogopts(ERR_PID);
if (argc != 1)
err_usage("");
for (int i = 0; i < NUM_CHILDREN; i++)
{
if (pipe(&fd[2 * i]) != 0)
err_syserr("failed to create pipe for child %d: ", i);
}
for (int i = 0; i < NUM_CHILDREN; i++)
{
if ((pid[i] = fork()) < 0)
err_syserr("failed to fork child %d: ", i);
else if (pid[i] == 0)
be_childish(i, fd);
else
{
printf("Child %d has PID %d\n", i, pid[i]);
fflush(stdout);
}
}
char buffer[MSG_BUFFSIZE];
for (int i = 0; i < NUM_CHILDREN; i++)
{
close(fd[2 * i + 1]);
int pipe_in = fd[2 * i + 0];
int nbytes = read(pipe_in, buffer, sizeof(buffer));
if (nbytes < 0)
err_syserr("failed to read from FD %2d: ", pipe_in);
printf("Got %2d bytes [%.*s] from FD %2d, PID %d\n",
nbytes, nbytes, buffer, pipe_in, pid[i]);
close(pipe_in);
}
for (int i = 0; i < NUM_CHILDREN; i++)
{
int status;
int corpse = wait(&status);
if (corpse > 0)
printf("Child with PID %d exited with status 0x%.4X\n", corpse, status);
else
err_syserr("Failed to wait for dead children: ");
}
return 0;
}
static void be_childish(int kid, int *fd)
{
for (int i = 0; i < kid; i++)
{
close(fd[2 * i + 0]);
close(fd[2 * i + 1]);
}
close(fd[2 * kid + 0]);
int estat = kid + 32;
char buffer[MSG_BUFFSIZE];
int nbytes = snprintf(buffer, sizeof(buffer),
"Child %d (PID %d) exiting with status %d",
kid, (int)getpid(), estat);
int pipe_out = fd[2 * kid + 1];
if (write(pipe_out, buffer, nbytes) != nbytes)
err_syserr("failed to write to parent: ");
close(pipe_out);
exit(estat);
}
Sample run:
Child 0 has PID 36957
Child 1 has PID 36958
Child 2 has PID 36959
Child 3 has PID 36960
Child 4 has PID 36961
Got 42 bytes [Child 0 (PID 36957) exiting with status 32] from FD 3, PID 36957
Got 42 bytes [Child 1 (PID 36958) exiting with status 33] from FD 5, PID 36958
Got 42 bytes [Child 2 (PID 36959) exiting with status 34] from FD 7, PID 36959
Got 42 bytes [Child 3 (PID 36960) exiting with status 35] from FD 9, PID 36960
Got 42 bytes [Child 4 (PID 36961) exiting with status 36] from FD 11, PID 36961
Child with PID 36960 exited with status 0x2300
Child with PID 36959 exited with status 0x2200
Child with PID 36958 exited with status 0x2100
Child with PID 36957 exited with status 0x2000
Child with PID 36961 exited with status 0x2400
So i just want to create a simple forking program that forks 5 children at the rate of 1 per every half second and then displays the date and time when each fork is complete.. so this is the jist of the code
int count = 1;
while(count <= 5){
int kid = fork();
if(kid == -1){
perror("error in fork");
exit(0);
} else if(!kid){
numbytes = read(sockfd, buf, sizeof(buf)-1);
buf[numbytes] = '\0';
printf("%s\n",buf);
}
count++;
usleep(500000); //create per every half second, 500000 = 0.5sec
close(sockfd);
}
return 0;
}
which I thought should be simple enough, but instead of forking 5 times it doubles after each fork.. so it forks 1 time, then 2, then 4, 8.. etc.
Help?
A fork is generally of this form.
int pid = fork();
if( pid == -1 ) { /* error */
fprintf(stderr, "Error forking: %s", strerror(errno));
exit(1);
}
else if( pid == 0 ) { /* child */
puts("Child");
exit(0);
}
/* Parent */
printf("Forked %d\n", pid);
Note that the child has to exit else it will continue executing the rest of the program. Usually you have the child process run a function and exit.
The other part is the main program should wait until all child processes are complete, else you get zombies. Usually a loop calling wait() until there's no more children.
int wpid;
int wstatus;
while( (wpid = wait(&wstatus)) != -1 ) {
printf("Child %d exited with status %d\n", wpid, wstatus);
}
Put it all together and here's how you fork and wait for 5 child processes.
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
int main() {
int max_children = 5;
for( int i = 0; i < max_children; i++ ) {
int pid = fork();
if( pid == -1 ) { /* error */
fprintf(stderr, "Error forking: %s", strerror(errno));
}
else if( pid == 0 ) { /* child */
puts("Child");
exit(0);
}
/* Parent */
printf("Forked %d\n", pid);
}
int wpid;
int wstatus;
while( (wpid = wait(&wstatus)) != -1 ) {
printf("Child %d exited with status %d\n", wpid, wstatus);
}
}
Can someone explain why my calls to fork below are not doing anything? They are never equal to zero, or negative, they are only positive. So I never get to go into/create any child processes.
I can't create a child B, C, or D since the fork calls don't do anything.
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
//Creates Three Processes B,C,D which communicate data through pipes.
int main() {
int fd[2], nbytes;
pid_t childpidB, childpidC, childpidD;
pipe(fd);
if((childpidB = fork()) < 0)
{
perror("fork B error.\n");
exit(1);
}
if(childpidB == 0) //
{
close(fd[0]);
char writeparam[100];
sprintf(writeparam, "%d", fd[0]);
char *arg[3];
arg[0] = "PipeW1";
arg[1] = writeparam[100];
arg[2] = NULL;
execv("PipeW1",arg);
close(fd[1]);
}
if((childpidC = fork()) < 0)
{
perror("fork C error.\n");
exit(1);
}
if(childpidC == 0)
{
close(fd[0]);
char writeparam[100];
sprintf(writeparam, "%d", fd[0]);
char *arg[3];
arg[0] = "PipeW2";
arg[1] = writeparam[100];
arg[2] = NULL;
execv("PipeW2",arg);
close(fd[1]);
}
if((childpidD = fork()) < 0)
{
perror("fork D error\n");
exit(1);
}
if(childpidD == 0)
{
close(fd[1]);
char readparam[100];
sprintf(readparam, "%d", fd[1]);
char *arg[3];
arg[0] = "PipeR";
arg[1] = readparam[100];
arg[2] = NULL;
execv("PipeR",arg);
close(fd[0]);
}
return 0;
}
This is really killing me because it just makes no sense. Could it be something wrong with my debugger? To add more detail, I have tried adding printf("GOT HERE"); statements after each if xxx == 0, and nothing gets printed out ever.
Sorry for not adding much more detail, I don't know what more to say. It's really perplexing to be honest. The forks just aren't working the way they... normally should?
Adding printfs after the fork calls to print the resulting pids:
./fk
childpidB = 3311
childpidB = 0
childpidC = 3312
childpidC = 0
childpidD = 3313
childpidD = 3314
childpidC = 3316
childpidD = 3317
childpidD = 0
childpidD = 0
childpidD = 0
childpidC = 0
childpidD = 331a
childpidD = 0
So, the pids are not only equal to zero, additional forks are being done by the children because the execv() calls are broken. Your debugger doesn't seem to be tracking children processes. If you're using gdb see:
https://sourceware.org/gdb/current/onlinedocs/gdb/Forks.html#Forks