0

I was given an assignment to practice working with signals in Linux, the course uses C++, for reasons I wont get into. The goal of the program is to fork 3 processes that each run a separate program (in my case, the separate program runs a countdown loop). The secondary program is then started when it receives a signal from the first, via a kill call for SIGUSR1, and is stopped when it receives SIGUSR2. The secondary program is also meant to receive SIGINT and SIGTSTP, output "discarded" and continue.

The problem occurs when a user tries to use SIGINT or SIGTSTP to interrupt the program while running. It catches the signal, and displays "DISCARDED"...and then gives me a new terminal prompt, then continues running, and then fails to respond to ctrl-c signals or any signal after that (after shortening the timer down, it appears to not even retrieve signals from the parent anymore past that).

and so I'm confused, because my code was implemented almost 1:1 from what my prof's example had been, and they are just as confused as I've been.

The first program (named forker.cpp)

#include <fcntl.h>
#include <fstream>
#include <cstring>
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
//Program creates 2 processes
//Then those processes call a program "countdown"
//countdown each counts from 10-0, and handles signal interrupts
using namespace std;
int signalThrower();
pid_t childProc[3];
bool isParent = true;

int main(){
    cout << "Parent Start" << endl;

    for (int i = 0; i < 3; i++){
        childProc[i] = fork();
            if (childProc[i] == 0){
                //if proc is zero, then its the child process
                cout << "Child[" << i << "] Process: " << getpid() << endl;
                isParent = false;
                execlp("./countdown", "./countdown", NULL);
                cout << "nothing here" << endl;
            }
    }
    if (isParent){
        sleep(5);
        signalThrower();
    }

    cout << "Parent - End - Reached the end of program" << endl;
    return 0;
}

int signalThrower(){
    int status= -1;
    pid_t proc = 0;
    for (int i = 0; i < 3; i++){
        cout << "Signal thrown to: " << childProc[i] << endl;
        kill(childProc[i], SIGUSR1);
    }

    sleep(5);
    for(int i=0; i<3; i++){
        cout << "Signal thrown to: " << childProc[i] << endl;
        kill(childProc[i], SIGUSR2);
    }
    while(proc>=0){
        proc=wait(&status);
        cout << "Returned: " << proc << endl;
    }
    return 0;
}

The second program (named countdown.cpp)

#include <fcntl.h>
#include <cstring>
#include <fstream>
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

using namespace std;
bool sigrecv = false;
static void sig_handler(int);
int main(){
    struct sigaction sigcontrol;
    

    
    sigcontrol.sa_handler = sig_handler;
    sigemptyset(&sigcontrol.sa_mask);
    sigcontrol.sa_flags = 0;
    sigaction(SIGUSR1, &sigcontrol, NULL);
    sigaction(SIGUSR2, &sigcontrol, NULL);
    sigaction(SIGINT, &sigcontrol, NULL);
    sigaction(SIGTSTP, &sigcontrol, NULL);
    cout << "COUNTDOWN ON STANDBY" << endl;
    while(!sigrecv){
        sleep(0.1);
    }
    int counter = 30;
    while(sigrecv){
        cout << "Proc: " << getpid() << " - count: " << counter << endl;
        counter--;
        sleep(1);
    }


}


static void sig_handler(int sig){
    switch(sig){
        case SIGUSR1:
            sigrecv = true;
            cout << "Spinning up" << endl;
            break;
        case SIGINT:
            sigrecv = true;
            cout <<"ctrl-C discarded" << endl;
            break;
        case SIGTSTP:
            sigrecv = true;
            cout << "ctrl-Z discarded" << endl;
            break;
        case SIGUSR2:
            sigrecv = false;
            cout << "shutting down" << endl;
            break;
        default:
            cout << "Undefined Signal" << endl;
    }
    

}

as the program runs, it catches the SIGUSR1 startup signal, then CTRL-C is detected 3 times across each process, and then the program continues running, no longer catching any signals

Both me and my prof are stuck on this one, as are multiple other students who I've shown this same issue to. Aside from the obvious problem of outputting stuff in a signal handler, what is preventing my program from catching any signals after this point?

MonocleHat
  • 43
  • 6
  • 2
    When you type Ctl-C, it sends the signal to all the processes. Since the parent doesn't have a signal handler, it's killed by this. – Barmar Feb 25 '22 at 22:06
  • When the parent process is killed, the shell gives you a new prompt. But the child processes keep running because they caught the signal. – Barmar Feb 25 '22 at 22:07
  • @Barmar I added a signal handler to the parent process with the same results, the program catches the ctrl-c, then continues running in an infinite countdown state. What you've said makes sense but I still can't grasp why it would just fail to catch **any** signals past SIGUSR1 and SIGINT – MonocleHat Feb 25 '22 at 22:20
  • That's because of job control. Keyboard signals are only sent to the foreground process group. When the parent process exits, the child processes continue running in the background, so they don't receive keyboard signals. – Barmar Feb 25 '22 at 22:27
  • @Barmar so what would be the proper way to send signals to terminate the children then, given that the handler in the parent process didnt seem as effective? – MonocleHat Feb 25 '22 at 22:46
  • See [how to make child process die when parent exits](https://stackoverflow.com/questions/284325/how-to-make-child-process-die-after-parent-exits) – Barmar Feb 25 '22 at 22:47

0 Answers0