Asynchronous signals from children to parent and sigaction() - c

Important pre-knowledge: I have two children that were forked by the original process. The children simply send one of the two user-defined signals (SIGUSR1 or SIGUSR2) to the parent.
The code is constructed as follows:
if (pid == 0 || second_child == 0) {
/* Just some pseudocode for basic logic */
while (1) {
sleep(2);
kill(parent_pid, SIGUSR1) or kill(parent_pid, SIGUSR2);
}
} else {
while (1) {
struct sigaction psa;
psa.sa_handler = sigHandler;
/* three signals that need to be handled */
sigaction(SIGUSR1, &psa, NULL);
sigaction(SIGUSR2, &psa, NULL);
sigaction(SIGINT, &psa, NULL);
pause();
}
}
sigHandler method:
void sigHandler (int sigNum) {
switch(sigNum) {
case SIGUSR1:
//print something using write()
break;
case SIGUSR2:
//print something using write()
break;
default:
exit(0);
}
}
The calls to sigHandler() do not occur as they should. The print statements that occur in sigHandler() eventually just stop and there is no output. I believe the problem deals with a signal being sent to the parent while the sigHandler is being executed, but I am not certain.

Related

UNIX signal handling. Wait in SIGCHLD handler. C

I've a parent and a child processes. In the parent I established a signal handler for a SIGCHLD. I send SIGTSTP signal to the child, that trigers SIGCHLD and in SIGCHLD siganl handler in parent I call wait function to get a status of the stopped child. But instead of returning immediately it blocks. Then I send a SIGCONT signal to the child and wait returns with errno set to Interuppted system call. I can't understand what I'm missing.
pid_t pid;
static void sig_chld(int signo);
int main() {
struct sigaction act, savechld;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_handler = sig_chld;
if (sigaction(SIGCHLD, &act, &savechld) < 0){
return errno;
}
pid = fork();
switch (pid){
case -1:{
perror("fork failed");
return errno;
}
case 0:{ //child
if (sigaction(SIGCHLD, &savechld, NULL) < 0)
return errno;
execlp(path, name_of_executable, (char*)NULL);
return errno;
}
default:{
for (;;)
pause();
}
}
return 0;
}
void sig_chld(int signo) {
int statol;
if (wait(&statol) < 0){
perror("waitt error");
exit(errno);
}
if (WIFSTOPPED(statol)){
puts("Child is stopped");
} else if (WIFEXITED(statol)){
puts("Child exited");
} else if (WIFCONTINUED(statol)){
puts("Child continued");
} else if (WIFSIGNALED(statol)){
puts("Child is signaled");
int sig = WTERMSIG(statol);
psignal(sig, NULL);
}
}
You have to use waitpid() instead of wait(), and you need to specify the option WUNTRACED to also have stopped children reported with waitpid(), like this:
if (waitpid(-1, &statol, WUNTRACED) < 0) {
Now waitpid() should return immediately and your macro WIFSTOPPED(&statol) should be true.

How to implement producer-consumer using processes?

I'm trying to implement a producer-consumer application using 1 parent process and 1 child process. The program should work like this:
1 - The parent process is the producer and the child process is the consumer.
2 - The producer creates a file, the consumer removes the file.
3 - After the file has been created, the parent process sends a SIGUSR1 signal to the child process which then removes the file and sends a SIGUSR2 signal to the parent, signaling it that the file can be created again.
I've tried implementing this problem but I keep getting this error:
User defined signal 1: 30.
I don't really understand what could be the problem. I've just started learning about process and signals and maybe I'm missing something. Any help would be appreciated. Here's my implementation:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
pid_t child, parent;
void producer()
{
system("touch file");
printf("File was created.\n");
}
void consumer()
{
system("rm file");
printf("File was deleted.\n");
kill(parent, SIGUSR2); // signal -> file can created by parent
}
int main(void)
{
system("touch file");
pid_t pid = fork();
for(int i = 0; i < 10; ++i)
{
if(pid < 0) // error fork()
{
perror("fork()");
return -1;
}
else if(pid == 0) // child proces - consumer
{
child = getpid();
signal(SIGUSR1, consumer);
pause();
}
else // parent process - producer
{
parent = getpid();
signal(SIGUSR2, producer);
// signal -> file can be deleted by child
kill(child, SIGUSR1);
}
}
return 0;
}
Edit: I forgot to mention that there can only be one file at a time.
...Any help would be appreciated.
Regarding the Error: User defined signal 1: 30, it is possible that the speed of execution is precipitating a race condition, causing termination before your handler functions are registered. Keep in mind, each signal has a default disposition (or action). For SIGUSR1 and SIGUSR2S the disposition is term, (from table in signal(7) page linked below)
SIGUSR1 30,10,16 Term User-defined signal 1
SIGUSR2 31,12,17 Term User-defined signal 2
(Note the value 30 listed by SIGUSR1 matches the exit condition you cite.)
The implication here would be that your handler functions had not registered before the first encounter with SIGUSR1, causing the default action of terminating your application and throwing the signal related error.
The relationship between synchronization and timing come to mind as something to look at. I found several things written on synchronization, and linked one below.
Timing may be implicitly addressed with an adequate approach to synchronization, negating the need for any explicit execution flow control functions. However, if help is needed, experiment with the sleep family of functions.
Here are a couple of other general suggestions:
1) printf (and family) should really not be used in a signal handler.
2) But, if used, a newline ( \n ) is a good idea (which you have), or use fflush to force a write.
3) Add a strace() call to check if any system call traffic is occurring.
Another code example of Synchronizing using signal().
Take a look at the signal(7) page.. (which is a lot of information, but implies why using printf or fprintf inside a signal handler in the first place may not be a good idea.)
Another collection of detailed information on Signal Handling.
Apart from what #ryyker mentioned, another problem is that by the time your parent process tries to signal the child using global variable child, the child has not got a chance to run and collect the pid. So the parent will send signal to a junk pid. A better approach is to use the pid variable in the parent and getppid() in the child. Here is the code which seems to give desired output
void producer()
{
system("touch file");
printf("File was created.\n");
}
void consumer()
{
system("rm file");
printf("File was deleted.\n");
kill(getppid(), SIGUSR2); // signal -> file can created by parent
}
int main(void)
{
system("touch file");
pid_t pid = fork();
if(pid < 0) // error fork()
{
perror("fork()");
return -1;
}
if(pid > 0) { //parent
signal(SIGUSR2, producer);
}
else { //child
signal(SIGUSR1, consumer);
}
for(int i = 0; i < 10; ++i)
{
if(pid == 0) {// child proces - consumer
pause();
}
else // parent process - producer
{
printf("Iter %d\n",i);
kill(pid, SIGUSR1);
pause();
}
}
return 0;
}
Try using semaphores in c++ instead of signals.
Signals truly serve special purposes in OS whereas semaphores serve process synchronization.
Posix named semaphores in c++ can be used across processes.
The following pseudocode will help.
Semaphore Full,Empty;
------
Producer() //producer
{
waitfor(Empty);//wait for an empty slot
system("touch file");
printf("File was created.\n");
Signal(Full); //Signal one slot is full
}
Consumer() //Consumer
{
WaitFor(Full); //wait for producer to produce
system("rm file");
printf("File was deleted.\n");
Signal(Empty);//Signal that it has consumed, so one empty slot created
}
After a lot of research and reading all of the suggestions I finally managed to make the program work. Here is my implementation. If you see any mistakes or perhaps something could have been done better, then feel free to correct my code. I'm open to suggestions.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void signal_handler(int signal_number)
{
sigset_t mask;
if(sigemptyset(&mask) == -1 || sigfillset(&mask) == -1)
{// initialize signal set || block all signals
perror("Failed to initialize the signal mask.");
return;
}
switch(signal_number)
{
case SIGUSR1:
{
if(sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
{ // entering critical zone
perror("sigprocmask(1)");
return;
} //---------------------
sleep(1);
system("rm file"); /* critical zone */
puts("File was removed.");
//--------------------
if(sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
{// exiting critical zone
perror("1 : sigprocmask()");
return;
}
break;
}
case SIGUSR2:
{
if(sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
{// entering critical zone
perror("2 : sigprocmask()");
return;
} //---------------------
sleep(1);
system("touch file");
puts("File was created."); /* critical zone */
// --------------------
if(sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
{// exiting critical zone
perror("sigprocmask(2)");
return;
}
break;
}
}
}
int main(void)
{
pid_t pid = fork();
struct sigaction sa;
sa.sa_handler = &signal_handler; // handler function
sa.sa_flags = SA_RESTART;
sigaction(SIGUSR1, &sa, NULL);
sigaction(SIGUSR2, &sa, NULL);
if(pid < 0)
{
perror("fork()");
return -1;
}
for(int i = 0; i < 10; ++i)
{
if(pid > 0) // parent - producer
{
sleep(2);
// signal -> file was created
kill(pid, SIGUSR1);
pause();
}
else // child - consumer
{
pause();
// signal -> file was removed
kill(getppid(), SIGUSR2);
}
}
return 0;
}

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).

SIGTERM to all children processes but not parent

I have a C program that operates by responding to signals. Some signals cause the parent to fork. This allows other processing while the parent continues to respond to signals.
When the parent is sent a SIGTERM, I want the forked children to receive a SIGTERM as well. It isn't critical that the children finish handling the SIGTERM before the parent exits.
However, with the below code, the children do not receive a SIGTERM when I call kill(0, SIGTERM) from the parent. From the kill manpage, it looks like all of the children should get this SIGTERM.
I have a signal handler setup for the parent.
static volatile sig_atomic_t done = 0;
const int handled_signals[] = {SIGINT, SIGTERM, 0};
static void set_flag(int signum) {
switch (signum) {
/* Intentionally exclude SIGQUIT/SIGABRT/etc. as we want to exit
* without cleaning up to help with debugging */
case SIGTERM:
case SIGINT:
done = 1;
break;
default:
/* Should be unreachable, but just in case */
if (signal(signum, SIG_DFL) != SIG_ERR) {
raise(signum);
}
}
}
static int setup_handlers() {
struct sigaction sa;
sigset_t block_all;
int i;
/* Block all other signals while handling a signal. This is okay as
* our handler is very brief */
sigfillset(&block_all);
sa.sa_mask = block_all;
sa.sa_handler = set_flag;
for (i = 0; handled_signals[i] != 0; i++) {
if (sigaction(handled_signals[i], &sa, NULL)) {
err_log("Unable to set sigaction");
return 1;
}
}
/* Ignore SIGCHLD as we don't keep track of child success */
sa.sa_handler = SIG_IGN;
if (sigaction(SIGCHLD, &sa, NULL)) {
err_log("Unable to ignore SIGCHLD");
return 1;
}
return 0;
}
int main() {
int i;
sigset_t block_mask, orig_mask;
setup_handlers();
/* Block all of our handled signals as we will be using
* sigsuspend in the loop below */
sigemptyset(&block_mask);
for (i = 0; handled_signals[i] != 0; i++) {
sigaddset(&block_mask, handled_signals[i]);
}
if (sigprocmask(SIG_BLOCK, &block_mask, &orig_mask)) {
err_log("Error blocking signals");
}
while (!done) {
if (sigsuspend(&orig_mask) && errno != EINTR) {
err_log("sigsuspend");
}
}
/* Kill all children */
if (kill(0, SIGTERM)) {
err_log("kill(0, SIGTERM))");
}
}
After getting a signal that requires a fork, I do the following
static int unregister_handlers() {
struct sigaction sa;
int i;
sa.sa_handler = SIG_DFL;
for (i = 0; handled_signals[i] != 0; i++) {
if (sigaction(handled_signals[i], &sa, NULL)) {
err_log("sigaction unregister");
return 1;
}
}
if (sigaction(SIGCHLD, &sa, NULL)) {
err_log("sigaction SIGCHLD unregister");
return 1;
}
return 0;
}
void do_fork() {
switch(fork()) {
/* Error */
case -1:
err_log("fork");
break;
/* Child */
case 0:
if (unregister_handlers()) {
_exit(1);
}
do_fork_stuff();
_exit(0);
break;
/* Parent */
default:
break;
}
}
In do_fork_stuff, the child sleeps for 30 seconds. I then call kill(0, SIGTERM) from the parent. The children do not terminate.
What's the reason the children aren't getting the SIGTERM?
Ah, a little help from /proc/[PID]/status solved this.
$ cat /proc/31171/status
Name: myprog
SigQ: 2/16382
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000004203
SigIgn: 0000000000000000
SigCgt: 0000000180000000
The blocked signals (SigBlk) were the issue here. While the handlers were unregistered, the children were blocking SIGTERM. Removing the blocked signals resolved the issue.

Hanging loop with sleep()

I'm trying to learn how to handle signals. In my program I have an array of pids of earlier created subprocesess. No I want to every couple seconds send a sigtstp signal to one of them. He just have to send sigchld to parent process and exit. Parent process should print an exit code of exited process and create next one in the place of exit one. Everything works fine in first loop but it hangs in second. So on output get:
loop
slept
forking
in to array
loop
Zakonczyl sie potomek 3934 z kodem 0.
So it's seems that sleep works in first loop but not in second. Or just main process didn't get back control after handling signal but this should't happen. So I have no idea whats may be wrong here.
while(1) {
printf("loop\n");
sleep(5);
printf("slept\n");
int r = rand() % n;
if(kill(process_tab[r],SIGTSTP) < 0) {
printf("Error while sending sigtstp signal.\n");
} else {
printf("forking\n");
if((child = fork()) < 0) {
printf("Fork failed.\n");
} else if(child == 0) {//to sie dzieje w procesie
if(signal(SIGTSTP,&catch_sigtstp)) {
printf("Error while setting signal handler.\n");
_exit(EXIT_FAILURE);
}
while(1) {
}
} else { //to sie dzieje w parencie
process_tab[r] = child;
printf("in to array\n");
}
}
}
And here are handlers.
void catch_sigtstp(int signal) {
kill(ppid,SIGCHLD);
_exit(EXIT_SUCCESS);
}
void catch_sigchld (int signal) {
int status;
pid_t child = wait(&status);
printf("Zakonczyl sie potomek %d z kodem %d.\n",child,status);
}
Add fflush after printf.
printf("Something\n");
fflush(stdout);
Otherwise you may not get the output as stdio is buffered by default.
Edit: Issues of handler
It is pretty unsafe to use printf function in signal handler, as it is not reentrant. Also, the catch_sigchild function can be modified:
void catch_sigchld (int signal) {
int status;
pid_t child;
while ((child = waitpid(-1, &status, WNOHANG)) > 0)
{
// may be something else?
// ...printf("Zakonczyl sie potomek %d z kodem %d.\n",child,status);
}
}
The reason is that one signal can be delivered for multiple dead children.
Edit: blocking signal when printing.
To avoid deadlock inside stdio, you should block the signal:
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGCHILD);
...
sigprocmask(SIG_BLOCK, &set, NULL);
printf("my output");
sigprocmask(SIG_UNBLOCK, &set, NULL);
...
Edit: as #Barmar has pointed, you parent process will receive SIGCHILD signal twice: once from your child'd signal handler, and one from OS.
To fix, it might be sufficient to remove your own signal source:
void catch_sigtstp(int signal) {
// kill(ppid,SIGCHLD); //< This one causes two signals per one child
_exit(EXIT_SUCCESS);
}

Resources