Autumn 2021
Jerry Cain
PDF
process control related
timing related
static void handleSIGSEGV(int sig) {
assert(sig == SIGSEGV);
cout << "There's no recovering from this." << endl;
exit(139); // as per https://www.geeksforgeeks.org/exit-codes-in-c-c-with-examples
} // SIGSEGV handlers should still end the program.
int main(int argc, char *argv[]) {
signal(SIGSEGV, handleSIGSEGV);
*(int *)NULL = 110;
return 0;
}
static void handleSIGINT(int sig) {
assert(sig == SIGINT);
cout << "I'm ignoring you." << endl;
} // By the way, Doris is over Thor. She likes Brutus now.
int main(int argc, char *argv[]) {
signal(SIGINT, handleSIGINT);
cout << "Just try to interrupt me, Thor!" << endl;
for (size_t i = 0; i < 50; i++) { sleep(1); }
return 0;
}
static const size_t kNumChildren = 5; // constant
static size_t numDone = 0; // global variable!
int main(int argc, char *argv[]) {
cout << "Let my five children play while I take a nap." << endl;
signal(SIGCHLD, reapChildProcesses);
for (size_t kid = 1; kid <= kNumChildren; kid++) {
pid_t pid = fork();
if (pid == 0) {
sleep(3 * kid); // sleep emulates "play" time
cout << "Child " << kid << " tires... returns to dad." << endl;
return 0;
}
}
while (numDone < kNumChildren) {
cout << "At least one child still playing, so dad nods off." << endl;
snooze(5); // signal-safe version of sleep
cout << "Dad wakes up! ";
}
cout << "All children accounted for. Good job, dad!" << endl;
return 0;
}
static void reapChildProcesses(int unused) {
waitpid(-1, NULL, 0);
numDone++;
}
poohbear@myth63:$ ./five-children
Let my five children play while I take a nap.
At least one child still playing, so dad nods off.
Child 1 tires... returns to dad.
Dad wakes up! At least one child still playing, so dad nods off.
Child 2 tires... returns to dad.
Child 3 tires... returns to dad.
Dad wakes up! At least one child still playing, so dad nods off.
Child 4 tires... returns to dad.
Child 5 tires... returns to dad.
Dad wakes up! At least one child still playing, so dad nods off.
Dad wakes up! All children accounted for. Good job, dad!
poohbear@myth63:$
static void reapChildProcesses(int unused) {
while (true) {
pid_t pid = waitpid(-1, NULL, 0);
if (pid < 0) break;
numDone++;
}
}
static void reapChildProcesses(int unused) {
while (true) {
pid_t pid = waitpid(-1, NULL, WNOHANG); // check out the WNOHANG!!!
if (pid <= 0) { // note the < is now a <=
assert(pid == 0 || errno == ECHILD); // pid could be 0 now
break;
}
numDone++;
}
}
int kill(pid_t pid, int sig);
int raise(int sig);
Signal handlers are difficult to use properly, and the consequences can be severe. Many regard signals to be one of the worst parts of Unix’s design.
This installment of Ghosts of Unix Past explains why asynchronous signal handling can be such a headache.
The article's primary point: The trouble with signal handlers is that they can be invoked at a really, really bad time (e.g. while the main execution flow is in the middle of a malloc call, or accessing a complex data structure).
This slide was written by Ryan Eberhardt and edited by Jerry.
static vector<string> strings;
void handleSIGINT(int sig) {
stringsToProcess.clear();
}
int main(int argc, char *argv[]) {
buildStringVector(strings); // assume anything reasonable
signal(SIGINT, handleSIGINT);
for (string &s: strings) processString(s);
return 0;
}