Sending signal frequently with alarm() and pause() - c

My task is:
Write a program in which the parent process creates exactly 1 child. After creating the child, the behaviour of the parent process is as follows: it sends the signal SIGUSR1 to the child every 5 seconds. To implement this behaviour the parent process most use the following system calls: alarm() and pause(). After sending the signal SIGUSR1 three times, the fourth time the signal SIGUSR2 is sent to the child. After this, the parent waits for the child to finish.
The behaviour of the child is as follows: it waits until it is interrupted by any signal. If the signal received is SIGUSR1 it prints a message to the standard output. If the signal received is SIGUSR2 then it finishes. In addition, during the first 5 seconds of its execution, the signal SIGUSR2 should be blocked. Students are expected to check the behaviour of alarm() and pause() in the man pages.
My solution looks like this. I am trying to ignore the alarm() in the parent and by the alarm in the child I set the flag on true and change unblock SIGUSR2.
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdbool.h>
#include <sys/wait.h>
bool flag = false;
void alert_ignore(int signum) {
printf("catched alarm\n");
return;
}
void alert_setting_flag(int signum) {
flag = true;
return;
}
void sigusr1_handler(int signum) {
printf("Recieved SIGUSR1\n");
return;
}
void sigusr2_handler(int signum) {
printf("Recieved SIGUSR2\n");
exit(0);
}
int main(int argc, char *argv[]) {
sigset_t set;
if (sigemptyset(&set) == -1) {
perror("sigemptyset");
exit(1);
}
if (sigaddset(&set, SIGUSR2) == -1) {
perror("sigaddset");
exit(1);
}
if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
perror("sigprocmask");
exit(1);
}
struct sigaction alert_action;
alert_action.sa_handler = alert_ignore;
alert_action.sa_mask = set;
if (sigaction(SIGALRM, &alert_action, NULL) == -1) {
perror("sigaction");
exit(1);
}
struct sigaction sigusr1_action;
sigusr1_action.sa_handler = sigusr1_handler;
sigusr1_action.sa_mask = set;
if (sigaction(SIGUSR1, &sigusr1_action, NULL) == -1) {
perror("sigaction");
exit(1);
}
struct sigaction sigusr2_action;
sigusr2_action.sa_handler = sigusr2_handler;
if (sigaction(SIGUSR2, &sigusr2_action, NULL) == -1) {
perror("sigaction");
exit(1);
}
pid_t pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
if (pid == 0) {
/* Child process duties */
/* Setting alert handler to turn flag form false to true */
alert_action.sa_handler = alert_setting_flag;
if (sigaction(SIGALRM, &alert_action, NULL) == -1) {
perror("sigaction");
exit(1);
}
alarm(5);
int count = 0;
while (true) {
pause();
if (flag == true && !count) {
if (sigprocmask(SIG_UNBLOCK, &set, NULL) == -1) {
perror("sigprocmask");
exit(1);
}
printf("SIGUSR2 unblocked\n");
count++;
}
}
}
/* Parent Process duties */
for (int i = 0; i < 3; ++i) {
alarm(5);
pause();
kill(pid, SIGUSR1);
}
kill(pid, SIGUSR2);
wait(NULL);
return 0;
}
This works, but only sometimes and I get much random behavior, like the following, and I don't know why.
Case(0) desired behavior
catched alarm // after 5 seconds
SIGUSR2 unblocked
Recieved SIGUSR1
catched alarm // after 10 seconds
Recieved SIGUSR1
catched alarm // after 15 seconds
Recieved SIGUSR1
Recieved SIGUSR2
Case 1 (a lot):
catched alarm // after 5 seconds
Recieved SIGUSR1
SIGUSR2 unblocked
Alarm clock // after 10 seconds
Case 2 (very rare) needed to terminate it:
catched alarm // after 5 seconds
catched alarm // after 10 seconds
catched alarm // after 15 seconds
^Z
Case 3 (double printing SIGUSR2 unblocked):
catched alarm // after 5 seconds
SIGUSR2 unblocked
SIGUSR2 unblocked
Recieved SIGUSR1
catched alarm // after 10 seconds
Recieved SIGUSR1
catched alarm // after 15 seconds
Recieved SIGUSR1
Recieved SIGUSR2
Case 4:
catched alarm // after 5 seconds
Alarm clock
What is the reason for this behavior? (Most important part for me is, why the SIGALRM is not ignored as desired, I know that there are problems that I am not setting the flag in an atomic way, but that shouldn't influence my parent process, no?)

You have:
struct sigaction alert_action;
alert_action.sa_handler = alert_ignore;
alert_action.sa_mask = set;
if (sigaction(SIGALRM, &alert_action, NULL) == -1) {
Here, alert_action.sa_flags isn't initialized nor assigned to, so it will be a random integer. A number of bad things could happen. If the SA_RESETHAND flag is on, then the signal action will be reset to the default after the first SIGALRM is received, and the next SIGALRM will kill the process. That's why you're sometimes seeing the shell print out Alarm clock.
To fix this, either initialize the structure or have assignment statements for all of the necessary fields. For example:
struct sigaction alert_action = {
.sa_handler = alert_ignore,
.sa_mask = set,
.sa_flags = 0
};
You should do this for all your struct sigaction variables.

Related

Linux signal parent and child [c]

im new to Linux and im still learning my code job is simple it receives a signal from the parent and the child have to ignore the signal and print the number of the signal like [1,3,4,9,11], but my problem is the child does not print anything after the signal plus I want the child to ignore the signals especially like[sigquit] here is my code.
// C program to implement sighup(), sigint()
// and sigquit() signal functions
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
// function declaration
void sighup();
void sigint();
void sigquit();
void sigsegv();
// driver code
void main()
{
int pid;
/* get child process */
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0) { /* child */
signal(SIGHUP, sighup);
signal(SIGINT, sigint);
signal(SIGQUIT, sigquit);
signal(SIGSEGV, sigsegv);
for (;;)
; /* loop for ever */
}
else /* parent */
{ /* pid hold id of child */
printf("\nPARENT: sending SIGHUP\n\n");
kill(pid, SIGHUP);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGINT\n\n");
kill(pid, SIGINT);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGQUIT\n\n");
kill(pid, SIGQUIT);
sleep(3);
}
}
// sighup() function definition
void sighup()
{
signal(SIGHUP, sighup); /* reset signal */
printf("CHILD: 1 [sighub]\n");
}
// sigint() function definition
void sigint()
{
signal(SIGINT, sigint); /* reset signal */
printf("CHILD: 2 [sigint]\n");
}
// sigsegv() function definition
void sigsegv()
{
signal(SIGSEGV, sigsegv); /* reset signal */
printf("CHILD: 11 [sigsegv]\n");
}
// sigquit() function definition
void sigquit()
{
signal(SIGINT, sigquit); /* reset signal */
printf("3 [sigquit]\n");
}
Check signal.h in /usr/bin/include, signal handler
/* Type of a signal handler. */
typedef void (*__sighandler_t) (int);
so need to change both the forward declaration and function definition to match this prototype as
// C program to implement sighup(), sigint()
// and sigquit() signal functions
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
// function declaration
void sighup(int);
void sigint(int);
void sigquit(int );
void sigsegv(int );
// driver code
int main()
{
int pid;
/* get child process */
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0) { /* child */
signal(SIGHUP, sighup);
signal(SIGINT, sigint);
signal(SIGQUIT, sigquit);
signal(SIGSEGV, sigsegv);
for (;;)
; /* loop for ever */
}
else /* parent */
{ /* pid hold id of child */
printf("\nPARENT: sending SIGHUP\n\n");
kill(pid, SIGHUP);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGINT\n\n");
kill(pid, SIGINT);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGQUIT\n\n");
kill(pid, SIGQUIT);
sleep(3);
}
return 0 ;
}
// sighup() function definition
void sighup(int signo)
{
signal(SIGHUP, sighup); /* reset signal */
printf("CHILD: 1 [sighub]\n");
}
// sigint() function definition
void sigint(int signo)
{
signal(SIGINT, sigint); /* reset signal */
printf("CHILD: 2 [sigint]\n");
}
// sigsegv() function definition
void sigsegv(int signo)
{
signal(SIGSEGV, sigsegv); /* reset signal */
printf("CHILD: 11 [sigsegv]\n");
}
// sigquit() function definition
void sigquit(int signo)
{
signal(SIGINT, sigquit); /* reset signal */
printf("3 [sigquit]\n");
}
As mentioned in comments, stdio functions like printf() aren't safe to use in signal handlers. On Linux, you should also use sigaction() instead of signal() to install signal handlers, as that avoids some issues with an imprecise definition of how handlers work in the latter function (Which should only be used when targeting bare bones standard C, not POSIX, where what signal handlers can do is even more restricted than in POSIX).
However, when targeting Linux or Unix platforms, you don't need signal handlers at all for this task! Each process has a signal mask, which controls which signals are blocked from having the normal execution of a handler or default action go off. If a process blocking signal X gets that signal, it's considered pending, and there are other ways to receive it. One such way in Linux is to use a signalfd, a special file descriptor that can be read from to get information about pending signals. An example using it:
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/signalfd.h>
#include <sys/types.h>
#include <sys/wait.h>
int child_main(const sigset_t *, int);
void send_signals(pid_t, int *);
int main(void) {
// The signals we want to catch
int signals[] = {SIGHUP, SIGINT, SIGQUIT, SIGSEGV, -1};
// Set up the signal mask
sigset_t sigs, oldmask;
sigemptyset(&sigs);
for (int i = 0; signals[i] >= 0; i++) {
sigaddset(&sigs, signals[i]);
}
// To avoid a race condition where the parent starts sending signals
// before the child is ready for them, block the signals before
// forking the child
if (sigprocmask(SIG_BLOCK, &sigs, &oldmask) < 0) {
perror("sigprocmask");
return EXIT_FAILURE;
}
pid_t child = fork();
if (child < 0) {
perror("fork");
return EXIT_FAILURE;
} else if (child == 0) {
// In the child process
return child_main(&sigs, (sizeof signals / sizeof signals[0]) - 1);
} else {
// Parent process. Restore the original signal mask and send child signals
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
perror("parent sigprocmask");
kill(child, SIGKILL);
return EXIT_FAILURE;
}
send_signals(child, signals);
// Wait for the child to finish
if (waitpid(child, NULL, 0) < 0) {
perror("parent waitpid");
return EXIT_FAILURE;
}
}
return 0;
}
void send_signals(pid_t proc, int *signals) {
for (int i = 0; signals[i] >= 0; i++) {
printf("Sending process %d signal %s (%d)\n", (int)proc,
strsignal(signals[i]), signals[i]);
if (kill(proc, signals[i]) < 0) {
printf("Failed: %s\n", strerror(errno));
}
}
}
int child_main(const sigset_t *sigs, int nsigs) {
// Create a signalfd that monitors the given signals
int fd = signalfd(-1, sigs, 0);
if (fd < 0) {
perror("child signalfd");
return EXIT_FAILURE;
}
struct signalfd_siginfo s;
// Loop up to nsigs times reading from the signal fd
int count = 0;
while (++count <= nsigs && read(fd, &s, sizeof s) == sizeof s) {
printf("Child received signal %s (%d)\n", strsignal(s.ssi_signo),
s.ssi_signo);
}
if (count <= nsigs && errno != EINTR) {
perror("child read");
close(fd);
return EXIT_FAILURE;
}
close(fd);
return 0;
}
Example output:
Sending process 17248 signal Hangup (1)
Sending process 17248 signal Interrupt (2)
Sending process 17248 signal Quit (3)
Sending process 17248 signal Segmentation fault (11)
Child received signal Hangup (1)
Child received signal Segmentation fault (11)
Child received signal Interrupt (2)
Child received signal Quit (3)
Your program is running perfectly in my machine, so I don't understand why do you say the child doesn't print anything.
Anyway, you have to be careful, as the child is printing the messages of the received signals, but running forever, this means that when the parent is done and exit()s you leave a child running... forever? (and you are running the child without stopping or blocking, so consuming all the available cpu)
You must devise a method to kill such a process, because if you don't you will end with lots of processes running in your behalf, doing nothing but ignoring the kill()s you do to them.
For that, there are some signals that are not ignorable, the SIGKILL is one, you cannot install a signal handler to it, for security reasons... :)
Also, when using signal(2) system call, the handler is executed on the reception of the first signal, and switches to the default behaviour (which depends on the signal) when the signal handler is done.
If you want it to be permanent, you need to reinstall the signal handler (this has a race condition, in case you receive a second signal while you are executing the signal handler and have not had time to install it again) or call sigaction(2) instead, that allows you to permanently (while the process is running, or up to the next call you do to sigaction) install a signal handler that doesn't need to be renewed. Look at the manual page of sigaction, as you need to learn how to use it.

simple synchronization with signals

The program is intended to signal transaction permanently. SIGUSR1 is caught by the parent and SIGUSR2 caught by the child. They play with only the flag when they catch their own signals. I let first the parent to run, that is, at first the parent sends signal. The child waits by pause() its process until it runs its catcher on the fly. I thought I apply a simple synchronization, but seemingly not. However, if I comment in the usleep(1000), the code works. Like
initial value, flag = -99
child process, flag = 0
parent process, flag = 1
child process, flag = 0
parent process, flag = 1
child process, flag = 0
.
.
.
child process, flag = 0
parent process, flag = 1
child process, flag = 0
parent process, flag = 1
child process, flag = 0
.
.
.
but without sleep, I can't get what I want. I want to get my intend without sleep. Wrong output is,
initial value, flag = -99
parent process, flag = -99
waits forever..................
How can it be run as intended? However, what's the reason of the behaviour? By the way, I have to apply the synchronization with only signals without semaphores, mutex etc. All posix signal features, except for sleep, nanosleep or pause and busy waiting, can be used like sigaction, sigsuspend etc.
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
volatile sig_atomic_t flag = -99; // child = 0, parent = 1;
void catcher(int sig) {
switch (sig) {
case SIGUSR1 : flag = 1; break;
case SIGUSR2 : flag = 0; break;
}
}
int safeBlockParent(int signum) {
sigset_t maskall, maskmost, maskold;
sigfillset(&maskall);
sigfillset(&maskmost);
sigdelset(&maskmost, signum);
sigprocmask(SIG_SETMASK, &maskall, &maskold);
if (flag == 0)
sigsuspend(&maskmost);
sigprocmask(SIG_SETMASK, &maskold, NULL);
}
int safeBlockChild(int signum) {
sigset_t maskall, maskmost, maskold;
sigfillset(&maskall);
sigfillset(&maskmost);
sigdelset(&maskmost, signum);
sigprocmask(SIG_SETMASK, &maskall, &maskold);
if (flag == 1)
sigsuspend(&maskmost);
sigprocmask(SIG_SETMASK, &maskold, NULL);
}
void ChildProcess() {
while(1) {
safeBlockChild(SIGUSR2);
fprintf(stderr, "child process, flag = %d\n", flag);
kill( getppid(), SIGUSR1 );
}
}
void ParentProcess(pid_t childPid) {
flag = 1;
while(1) {
//usleep(1000);
fprintf(stderr, "parent process, flag = %d\n", flag);
kill( childPid, SIGUSR2 );
safeBlockParent(SIGUSR1);
}
}
int main() {
pid_t pid;
struct sigaction sact = { 0 };
fprintf(stderr, "initial value, flag = %d\n", flag);
sigemptyset( &sact.sa_mask );
sact.sa_flags = 0;
sact.sa_handler = catcher;
if (sigaction (SIGUSR1, &sact, NULL) < 0) {
perror("sigaction sigusr1 error");
exit(1);
}
if (sigaction (SIGUSR2, &sact, NULL) < 0) {
perror("sigaction sigusr2 error");
exit(2);
}
pid = fork();
if (pid < 0) { perror("fork problem"); exit(3); }
if (pid == 0) {
//kill(getppid(), SIGUSR1);
ChildProcess();
}
else {
ParentProcess(pid);
//wait(NULL);
}
return 0;
}
The code stucks sometimes, sometimes runs.
You have two race conditions:
The parent process could send a signal before the child has had a chance to register a signal handler for SIGUSR2.
One process could send a signal while the other is outside pause.
The latter can happen the first time round, when the child process has yet to reach pause, but the parent has sent SIGUSR2 anyway. This causes the effect you're seeing.

Ignore SIGQUIT once

I am trying to write a function to install a signal handler on a process to ignore a SIGQUIT signal the first time it is called and then exit on the second SIGQUIT signal. I am intend to use a global variable for the counter to count the number of times SIGQUIT signal is sent.
However I am having issues implementing this. I have found SIG_IGN which would ignore the first SIGQUIT signal, but how would I then detect the first signal to increment the counter (and maybe change the signal() to call the signal handler)?
My code is as below:
// global var counter
int signal_counter = 0;
int main (int argc, const char * argv[]) {
pid_t child_pid = 0;
int child_status = 0;
/* fork a child process... */
child_pid = fork();
if(child_pid < 0 ) { /* fork() and check if there were errors */
perror("fork"); /* print a system-defined error message */
exit(EXIT_FAILURE);
}
else if(child_pid == 0) { /* Child code */
install_handler();
for(; ;sleep(1)); /*loop forever*/
}
else { /* Parent code */
printf("Parent processing starts\n");
printf("\nPARENT: sending first SIGQUIT\n\n");
kill(child_pid,SIGQUIT);
printf("\nPARENT: doing something\n\n");
sleep(3);
printf("\nPARENT: sending SIGQUIT again\n\n");
kill(child_pid,SIGQUIT);
}
return EXIT_SUCCESS;
}
void sigquit() {
printf("SIGQUIT\n");
sleep(2);
exit(0);
}
void install_handler(){
if (signal_counter == 0){
signal(SIGQUIT, sigquit);
signal_counter+=1;
}
else{
signal(SIGQUIT, sigquit);
}
}
Any help would be appreciated.
A way to solve it is to use the posix standard. According to it, a signal installed is one shot.
The first time that the signal will arrive, it will start the method specified. Therefore, if another signal arrives, then it will be executed normally (i.e. it will quit your application for a sigquit).

sending signal from parent to child

I am using this tutorial from website http://www.code2learn.com/2011/01/signal-program-using-parent-child.html and trying to understand why signal is not recieved by child?
here is the code:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
void sighup(); /* routines child will call upon sigtrap */
void sigint();
void sigquit();
void main()
{ int pid;
/* get child process */
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0)
{ /* child */
signal(SIGHUP,sighup); /* set function calls */
signal(SIGINT,sigint);
signal(SIGQUIT, sigquit);
for(;;); /* loop for ever */
}
else /* parent */
{ /* pid hold id of child */
printf("\nPARENT: sending SIGHUP\n\n");
kill(pid,SIGHUP);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGINT\n\n");
kill(pid,SIGINT);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGQUIT\n\n");
kill(pid,SIGQUIT);
sleep(3);
}
}
void sighup()
{ signal(SIGHUP,sighup); /* reset signal */
printf("CHILD: I have received a SIGHUP\n");
}
void sigint()
{ signal(SIGINT,sigint); /* reset signal */
printf("CHILD: I have received a SIGINT\n");
}
void sigquit()
{ printf("My DADDY has Killed me!!!\n");
exit(0);
}
output:
It's a race condition. Your code assumes that the child runs first and is not preempted by the parent until it installed all signal handlers and starts looping forever.
When that is not the case, the parent may send a signal to the child before the child had the chance to catch the signal. As such, the child process is killed, since the default action for SIGHUP, SIGINT and SIGQUIT is to terminate.
In your specific case, you never see any output from the child. This means that the parent sent SIGHUP to the child, and SIGHUP was delivered before the child changed the default behavior. So the child was killed.
Actually, if you did some error checking on the returning value of kill(2) - which you should - you would see ESRCH in the parent upon trying to send SIGINT and SIGQUIT, because the child is already gone (assuming no other process in the system was started and got assigned the same PID in the meantime).
So, how do you fix it? Either use some form of synchronization to force the child to run first and only let the parent execute after all signal handlers are installed, or set up the signal handlers before forking, and then unset them in the parent. The code below uses the latter approach:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
void sighup(int); /* routines child will call upon sigtrap */
void sigint(int);
void sigquit(int);
int main(void) {
int pid;
signal(SIGHUP,sighup); /* set function calls */
signal(SIGINT,sigint);
signal(SIGQUIT, sigquit);
/* get child process */
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0) {
/* child */
for(;;); /* loop for ever */
} else {
signal(SIGHUP, SIG_DFL);
signal(SIGINT, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
/* parent */
/* pid hold id of child */
printf("\nPARENT: sending SIGHUP\n\n");
kill(pid,SIGHUP);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGINT\n\n");
kill(pid,SIGINT);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGQUIT\n\n");
kill(pid,SIGQUIT);
sleep(3);
}
return 0;
}
void sighup(int signo) {
signal(SIGHUP,sighup); /* reset signal */
printf("CHILD: I have received a SIGHUP\n");
}
void sigint(int signo) {
signal(SIGINT,sigint); /* reset signal */
printf("CHILD: I have received a SIGINT\n");
}
void sigquit(int signo) {
printf("My DADDY has Killed me!!!\n");
exit(0);
}
Also, you shouldn't be using signal(2): it is unreliable in many ways, and its exact semantics are platform dependent. To ensure maximum portability, you should use sigaction(2). Refer to the manpages to learn more. Here's the same code using sigaction(2) instead:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
void sighup(int); /* routines child will call upon sigtrap */
void sigint(int);
void sigquit(int);
int main(void) {
struct sigaction sigact;
sigact.sa_flags = 0;
sigemptyset(&sigact.sa_mask);
sigact.sa_handler = sighup;
if (sigaction(SIGHUP, &sigact, NULL) < 0) {
perror("sigaction()");
exit(1);
}
sigact.sa_handler = sigint;
if (sigaction(SIGINT, &sigact, NULL) < 0) {
perror("sigaction()");
exit(1);
}
sigact.sa_handler = sigquit;
if (sigaction(SIGQUIT, &sigact, NULL) < 0) {
perror("sigaction()");
exit(1);
}
pid_t pid;
/* get child process */
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0) {
/* child */
for(;;); /* loop for ever */
} else {
sigact.sa_handler = SIG_DFL;
sigaction(SIGHUP, &sigact, NULL);
sigaction(SIGINT, &sigact, NULL);
sigaction(SIGQUIT, &sigact, NULL);
/* parent */
/* pid hold id of child */
printf("\nPARENT: sending SIGHUP\n\n");
kill(pid,SIGHUP);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGINT\n\n");
kill(pid,SIGINT);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGQUIT\n\n");
kill(pid,SIGQUIT);
sleep(3);
}
return 0;
}
void sighup(int signo) {
signal(SIGHUP,sighup); /* reset signal */
printf("CHILD: I have received a SIGHUP\n");
}
void sigint(int signo) {
signal(SIGINT,sigint); /* reset signal */
printf("CHILD: I have received a SIGINT\n");
}
void sigquit(int signo) {
printf("My DADDY has Killed me!!!\n");
exit(0);
}
Last, but not least, it is important to mention that you should always compile with -Wall. Your program has some mistakes:
The return type of main() should be int.
Signal handlers receive the signal number as an argument, please use the right prototype and declaration.
fork(2) returns a pid_t, not an int, please use the correct type.
You need to include unistd.h to get the right prototype for fork(2).
printf(3) is not async-signal safe and as such you shouldn't call it inside a signal handler. It's ok in this toy program to see how signals work together, but keep in mind that you should never do it in the real world. To see a list of async-signal safe functions, as well as the default actions for each signal, see man 7 signal.
Word of advice: stop learning from that website. If you want to learn this kind of stuff, read Advanced Programming in the UNIX Environment. Go straight to chaper 10 to learn why exactly signal(2) is considered unreliable and obsolescent. It's a big book, but it's well worth investing your time on it.

How does processes synchronization using signals work?

I've recently finished Section 10 (Signals) of "Advanced Programming in the Unix Environment" (3rd edition) and I've come across a piece of code I don't entirely understand:
#include "apue.h"
static volatile sig_atomic_t sigflag; /* set nonzero by sig handler */
static sigset_t newmask, oldmask, zeromask;
static void
sig_usr(int signo) /* one signal handler for SIGUSR1 and SIGUSR2 */
{
sigflag = 1;
}
void
TELL_WAIT(void)
{
if (signal(SIGUSR1, sig_usr) == SIG_ERR)
err_sys("signal(SIGUSR1) error");
if (signal(SIGUSR2, sig_usr) == SIG_ERR)
err_sys("signal(SIGUSR2) error");
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGUSR1);
sigaddset(&newmask, SIGUSR2);
/* Block SIGUSR1 and SIGUSR2, and save current signal mask */
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
err_sys("SIG_BLOCK error");
}
void
TELL_PARENT(pid_t pid)
{
kill(pid, SIGUSR2); /* tell parent we're done */
}
void
WAIT_PARENT(void)
{
while (sigflag == 0)
sigsuspend(&zeromask); /* and wait for parent */
sigflag = 0;
/* Reset signal mask to original value */
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error");
}
void
TELL_CHILD(pid_t pid)
{
kill(pid, SIGUSR1); /* tell child we're done */
}
void
WAIT_CHILD(void)
{
while (sigflag == 0)
sigsuspend(&zeromask); /* and wait for child */
sigflag = 0;
/* Reset signal mask to original value */
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error");
}
The routines above are used (as you certainly know) to synchronize processes using signals. Although I understand every single line on its own, I can't see (understand) the big picture. The code itself is used it the following scenario: to avoid a race condition in our program, after we fork(), we make the child process TELL_PARENT and WAIT_PARENT, and then we do the same to the parent with TELL_CHILD and WAIT_CHILD. My questions are:
1.) How can a child communicate with its parent through a variable while both of them work with their own set (copy) of variables? Is it because the child doesn't modify sigflag directly but through a signal handler (the same goes for the parent)?
2.) Why do we need to block SIGUSR1 and SIGUSR2 and then unblock it with sigprocmask?
A program that uses three of those routines could be (taken from the book):
#include "apue.h"
static void charatatime(char *);
int
main(void)
{
pid_t pid;
TELL_WAIT();
if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid == 0) {
WAIT_PARENT(); /* parent goes first */
charatatime("output from child\n");
} else {
charatatime("output from parent\n");
TELL_CHILD(pid);
}
exit(0);
}
static void
charatatime(char *str)
{
char *ptr;
int c;
setbuf(stdout, NULL); /* set unbuffered */
for (ptr = str; (c = *ptr++) != 0; )
putc(c, stdout);
}
Cheers,
1) They are not communicating through "variable" - the sole communication facility used here is kill function. We "tell" things by invoking kill, we "wait" to be told with sigsuspend. sig_flag is not shared, it's a local state of each process, and it says whether this particular process has been "told" by the other.
2) Were the signals not blocked prior to fork, the parent process could send the signal to the child before the child has started waiting for it. That is, the timeline could be like that:
fork
parent gets the time slice, sends signal to the child with kill
child gets the time slice, and waits for the signal
But this signal has already been delivered, and so waits indefinitely. Therefore, we must ensure the signal is not delivered to the child process before it starts the waiting loop. To this end, we block it before fork, and atomically unblock it and start waiting for it. Atomicity is the key; required invariant cannot be achieved with this operation performed as two independent steps, as the signal could be delivered inbetween.

Resources