I found this problem in a past exam from CMU and I can't get how the outputs were possible.
basically, the idea behind it that there is a parent process that blocks a user-defined signal, then the parent forks a child. and based on which process that runs first (aka: wins the race) a different output is possible. Here is the question that is being asked in the exam (please read it)
and here is the code from the exam:
int i = 1;
void handler (int sig) {
i++;
}
int main() {
pid_t pid;
sigset_t s;
sigemptyset(&s);
sigaddset(&s, SIGUSR1);
signal(SIGUSR1, handler);
sigprocmask(SIG_BLOCK, &s, 0);
pid = fork();
<LINE A>
if (pid != 0) {
i = 2;
<LINE B>
} else {
i = 3;
<LINE C>
}
sigprocmask(SIG_UNBLOCK, &s, 0);
pause(); /* pause to allow all signals to arrive */
printf("%d\n", i);
exit(0);
}
There are 3 cases that needs to be tested since we need to put the function:
kill(pid,USRSIG1);
either in LINE A or LINE B or LINE C and find the possible output.
Now here is what i did, i placed the function in LINE A.
Let's say that we run the program, then the parent will create an empty set s, add the signal SIGSUR1 to it, then it will assign a custom handler for the SIGUSR1 signal, and it blocks the signals in the set s. Which are these lines
sigset_t s;
sigemptyset(&s);
sigaddset(&s, SIGUSR1);
signal(SIGUSR1, handler);
sigprocmask(SIG_BLOCK, &s, 0);
Then the parent will run the line
pid = fork();
which will create a new child from the process.
Now there are 2 cases that will determine the output. The Operating system schedules the parent or the child to run first.
Let's say that the parent runs first. Then it will execute LINE A (which is the kill function)
and since it's the parent the pid value will be the process id of the child. So it will send the USRSIG1 to the child but since it's blocked it will do nothing
The if statement assigns a value for the global variable i. if the process is a parent then i = 2, else then i = 3. So in our parent process, we will have i = 2.
if (pid != 0) { //if i am a parent then i = 2
i = 2;
<LINE B>
} else { //if i am a child then i = 3
i = 3;
<LINE C>
}
the next line will be executed in the parent and it will unblock the SIGUSR1 signal
sigprocmask(SIG_UNBLOCK, &s, 0);
and the parent process will pause until it receives a signal
now the child will run and it will send a kill(0,SIGUSR1) signal to all the process in the process group including itself. But since it's blocked in the child nothing will happen. The parent will receive the signal and it will increment me by 1 (So now i = 3 in parent). And it (the parent) will be resumed from the function pause to print the value of I (which is 3) and exit.
the child now resumes from the kill function and since it's a child the if statement will not be true (so the value of i in the child = 3). The child unblocks the signals from the set and pause().
Since there is no other process to send a signal to the child it will stay paused forever and the output was 3 by the parent only. And if we go the other way (the child runs before the parent) then the output will be 4 only.
What is confusing me that the solution from the exam says that there are 2 outputs per run? I don't get how is this possible since one of the process will stay in pause().
The solution key says that the possible output for LINE A is:
3 4, 4 3, 3 5, or 5 3
that is all that I could understand from the question. Any help or hint would be appreciated.