The logic you are using is wrong; to make it easier, let's break the code into multiple functions using the informal description you've made:
#include <iostream>
#include <unistd.h>
void
proc_filho(const char * nome) {
int num_segundos = 0;
while (true) {
sleep(1);
num_segundos ++;
cout << "Sou o processo " << nome << " (PID " << getpid() << "), estou rodando há " << num_segundos << " segundos." << endl;
}
}
int
main(int argc, char ** argv) {
int num_segundos = 0;
while (true) {
sleep(1);
num_segundos ++;
cout << "Sou o processo pai (PID " << getpid() << "), estou rodando há " << num_segundos << " segundos." << endl;
// aos 10 segundos, gera o filho 1.
if (num_segundos == 10 && ! fork()) proc_filho("filho 1");
// aos 20 segundos, gera o filho 2.
if (num_segundos == 20 && ! fork()) proc_filho("filho 2");
}
return 0;
}
The parent process is born in main)()
, sleeps a second, prints its ID, and tests if it has reached the point of ten seconds, then tests if it has reached the point of twenty seconds. In both if
s, since the &&
operator has the shortcut evaluation feature, the second operand ( ! fork()
) is not executed until the first ( num_segundos == 10
/ num_segundos == 20
) be true. So he goes on repeating.
When num_segundos
reaches ten, the first operand returns true and then it runs fork()
, creating the first child. In the case of the parent process, it will get the pid of the child process (which is greater than zero), which denied returns 0 (ie false). Then it does not enter the then . Ten seconds later, num_segundos
reaches twenty, the first operand of the second if returns true, and the second fork()
is evaluated. Again, a value greater than zero is returned, which denied becomes zero, then then is skipped.
After that, the parent only sleeps for a second, increments num_segundos
, and reports its state forever.
In the case of children 1 and 2, they are created in fork()
, which returns zero for them. Denied, turn one, then they enter their then , which invoke the proc_filho()
function. This function receives the name of the process (for the purpose of reporting the state), and does not return. Thus, we guarantee that the parent will always run in main()
, and children in proc_filho()
, avoiding having to test whether the parent or child is running.
Children have their own counter (% local% to num_segundos
), and report their own status normally. They do not have access to the father's count, although they could receive from the father the number of seconds they were created to simulate the parent's count by a simple sum.
Note that in this case we call proc_filho()
all iterations to get our own pid; we could call once before the loop , and save the number to a local variable, if you feel better. But since getpid()
is a quick and simple system call , I do not see much need.
EDIT: Two new phases: First, each child creates a grandchild (called grandchild 1 and grandchild 2) after 15 seconds.
#include <iostream>
#include <unistd.h>
void
proc_neto(const char * nome) {
int num_segundos = 0;
while (true) {
sleep(1);
num_segundos ++;
cout << "Sou o processo " << nome << " (PID " << getpid() << "), estou rodando há " << num_segundos << " segundos." << endl;
}
}
void
proc_filho(const char * nome) {
int num_segundos = 0;
char nome_filho[7] = "neto x";
// o dígito fica na 8ª posição do nome do filho, repete na 6ª do neto
nome_filho[5] = nome[7];
while (true) {
sleep(1);
num_segundos ++;
cout << "Sou o processo " << nome << " (PID " << getpid() << "), estou rodando há " << num_segundos << " segundos." << endl;
if (num_segundos == 15 && ! fork()) proc_neto(nome_filho);
}
}
int
main(int argc, char ** argv) {
int num_segundos = 0;
while (true) {
sleep(1);
num_segundos ++;
cout << "Sou o processo pai (PID " << getpid() << "), estou rodando há " << num_segundos << " segundos." << endl;
// aos 10 segundos, gera o filho 1.
if (num_segundos == 10 && ! fork()) proc_filho("filho 1");
// aos 20 segundos, gera o filho 2.
if (num_segundos == 20 && ! fork()) proc_filho("filho 2");
}
return 0;
}
Third and last phase: In the instant 50 seconds, Son1 kills Father. - In 55 seconds, Son1 kills his son and commits suicide two seconds after that. - In 60 seconds, Neto2 kills his father and commits suicide three seconds later, ending the family saga.
Here it is better to separate the procs of the two children and the two grandchildren, since their behavior begins to differ. We could also save these differences in some kind of data structure, but it's not worth it here. Let's assume that these instants refer to the parent's clock:
#include <iostream>
#include <unistd.h>
#include <signal.h>
void
proc_neto1(int dtnasc) {
int num_segundos = 0;
while (true) {
sleep(1);
num_segundos ++;
cout << "Sou o processo neto 1 (PID " << getpid() << "), estou rodando há " << num_segundos << " segundos." << endl;
}
}
void
proc_neto2(int dtnasc) {
int num_segundos = 0;
while (true) {
sleep(1);
num_segundos ++;
cout << "Sou o processo neto 2 (PID " << getpid() << "), estou rodando há " << num_segundos << " segundos." << endl;
if (dtnasc + num_segundos == 60) {
cout << "Neto 2 ficou louco! Matou seu pai (PID " << getppid() << ")" << endl;
kill(getppid(), SIGKILL);
}
if (dtnasc + num_segundos == 63) {
cout << "Não aguentando a pressão, neto 2 se suicida." << endl;
kill(getppid(), SIGKILL);
}
}
}
void
proc_filho1(int dtnasc) {
int num_segundos = 0;
int pid_filho = 0;
while (true) {
sleep(1);
num_segundos ++;
cout << "Sou o processo filho 1 (PID " << getpid() << "), estou rodando há " << num_segundos << " segundos." << endl;
if (num_segundos == 15 && ! (pid_filho = fork())) proc_neto1(dtnasc + num_segundos);
if (dtnasc + num_segundos == 50) {
cout << "Filho 1 ficou louco! Matou seu pai (PID = " << getppid() << ")" << endl;
kill(getppid(), SIGKILL);
}
if (dtnasc + num_segundos == 55) {
cout << "Filho 1 continua louco! Matou seu filho (PID " << pid_filho << ")" << endl;
kill(pid_filho, SIGKILL);
}
if (dtnasc + num_segundos == 57) {
cout << "Não aguentando a pressão, filho 1 se suicida." << endl;
kill(getpid(), SIGKILL);
}
}
}
void
proc_filho2(const char * nome) {
int num_segundos = 0;
while (true) {
sleep(1);
num_segundos ++;
cout << "Sou o processo " << nome << " (PID " << getpid() << "), estou rodando há " << num_segundos << " segundos." << endl;
if (num_segundos == 15 && ! fork()) proc_neto2(dtnasc + num_segundos);
}
}
int
main(int argc, char ** argv) {
int num_segundos = 0;
while (true) {
sleep(1);
num_segundos ++;
cout << "Sou o processo pai (PID " << getpid() << "), estou rodando há " << num_segundos << " segundos." << endl;
// aos 10 segundos, gera o filho 1.
if (num_segundos == 10 && ! fork()) proc_filho1(num_segundos);
// aos 20 segundos, gera o filho 2.
if (num_segundos == 20 && ! fork()) proc_filho2(num_segundos);
}
return 0;
}
As you can see, separating distinct features into distinct functions makes it easier to extend program behavior. In this last phase, for example, each process was running a proc different from the other, so it did not have to pass the name of the child anymore; on the other hand, I started to pass the "birth date" to be able to count the timing of the tragedy correctly.
Do this all from one sitting only in getpid()
is it possible? Technically yes, but the logic would be so complicated that it is very unlikely that you will get it right first (or tenth). Separating into functions makes it much easier to reason about each piece separately (here, the behavior of each actor separately).