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.
Related
My program creates multiple processes, the parent process will send SIGUSR1 to all child processes with the following function:
void sendtochild (struct system_ system, int sig) {
pid_t selfpid = getpid();
for (int i = 0; i < system.children; i++) {
if (system.pids[i] != selfpid && system.pids[i] != system.parent) {
kill(system.pids[i], sig);
}
}
}
The signal gets awaited in the following way:
sigset_t set, oset;
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
if (sigprocmask(SIG_BLOCK, &set, &oset) < 0) {
perror("sigprocmask");
exit(EXIT_FAILURE);
}
printf("%d ", getpid());
fflush(stdout);
sigsuspend(&oset);
for some reason when looking at top the children are zombies instead of sleeping until the signal is delivered which is not the behavior I'm looking for.
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.
Hi i've done this code but there are somethings i need to add like.
ignore signal SIGINT
restore the signal handler for SIGINT to the default one
catch signal SIGINT and prints out the numerical value of the signal
so far this is the coding i have
void sig_handler(int signo)
{
// body of signal handler
}
int main()
{
struct sigaction act;
act.sa_flags = 0;
act.sa_handler = sig_handler;
sigfillset( & (act.sa_mask) );
if (sigaction(sig, &act, NULL) != 0)
{
perror("sigaction"); exit(1);
}
}
Something like this will get you started.
#include <stdio.h>
#include <signal.h>
void handle_signal(int signum)
{
printf("\nRecived signal: %d\n", signum);
switch(signum)
{
case SIGINT:
printf("I won't respond to ctrl-c!\n");
//restore to default
signal(SIGINT, SIG_DFL);
break;
case SIGWINCH:
//windows size change
break;
case SIGCONT:
//continue process 'fg' in terminal
break;
case SIGTSTP:
//ctrl-z
break;
default:
//others
break;
}
}
void listen_to_signals(void)
{
int i;
i = 0;
while (i < 32)
{
if (i != SIGKILL && i != SIGSTOP && i != SIGCHLD && i != SIGCONT &&
i != SIGURG && i != SIGIO)
signal(i, &handle_signal);
i++;
}
}
int main()
{
listen_to_signals();
while(1)
{}
}
Note, I'm listening to all signals I can: there are some signals that I can't listen to (like kill).
Note: use 'kill -9 pidof your_program' to stop it
Note: the first time SIGINT will be ignored, but the second time, it won't (because I'm restoring it to its default behavior)
So, my task is to sync parent and his 2 children in this way:
one child sends SIGUSR2 signal to parent and then blocks waiting parent msg.
The sync is implemented by global flags, so the parent waits for any of the flag_ch become 1 (it happens when child sends SIGUSR2) and then sends signal SIGUSR1 to this child, and child resumes (cause global flag_p becomes 1)
the trouble is that parent receives signals only from one child, and then blocks waiting for second child signals, but they don't appear
.
any idea?..
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/signalfd.h>
#define LPC 10
pid_t ch[2];
sig_atomic_t flag_ch[2] = {0, 0};
sig_atomic_t flag_p = 0;
int get_ind(pid_t ch_pid) {
int i;
for (i = 0; i < 2; ++i) {
if (ch_pid == ch[i])
return i;
}
return -1;
}
void usr_handler(int signo, siginfo_t* si, void* unused) {
int ch_index;
switch(signo) {
case SIGUSR2:
ch_index = get_ind(si->si_pid);
if (ch_index >= 0)
flag_ch[ch_index] = 1;
else
fprintf(stderr, "signal handled not from child pid %d\n", si->si_pid);
break;
case SIGUSR1:
flag_p = 1;
break;
}
}
void set_usr_handler(void) {
struct sigaction sa;
sa.sa_sigaction = usr_handler;
sa.sa_flags = SA_SIGINFO;
sa.sa_restorer = NULL;
sigemptyset(&sa.sa_mask);
if (0 != sigaction(SIGUSR1, &sa, NULL))
abort_prg("signal [SIGUSR1] error");
if (0 != sigaction(SIGUSR2, &sa, NULL))
abort_prg("signal [SIGUSR2] error");
}
void child_proc(void) {
int i;
for (i = 0; i < LPC; ++i) {
if (0 != kill(getppid(), SIGUSR2))
exit(1);
while (0 == flag_p) { };
flag_p = 0;
}
}
int wait_child(void) {
while (0 == flag_ch[0] && 0 == flag_ch[1]) { };
if (1 == flag_ch[0]) {
flag_ch[0] = 0;
return ch[0];
}
flag_ch[1] = 0;
return ch[1];
}
void parent_proc(void) {
int i;
pid_t ch_pid;
for (i = 0; i < LPC * 2; ++i) {
ch_pid = wait_child();
printf("Parent: Received from pid [%d]\n", ch_pid);
if (0 != kill(ch_pid, SIGUSR1))
exit(1);
}
}
int main(int argc, char* argv[]) {
set_usr_handler();
int i;
for (i = 0; i < 2; ++i) {
pid_t child = fork();
if (0 > child)
exit(1);
if (0 == child) {
child_proc();
return 0;
}
ch[i] = child;
}
parent_proc();
return 0;
}
My guess is that it's missing volatile in a few global variable declarations. For example, flag_p not being volatile means that the loop
while (flag_p == 0) { }
can run forever: GCC probably compiles it to load the global variable only once into a register, and then loop until this register is non-zero (which never occurs).
A conservative approximation is that you should make volatile all mutable variables that are read from or written to in a signal handler.
EDIT: another source of problem I can think of is that signals are not cumulative: either the parent process has no SIGUSR2 pending, or it has one. If both children send it to the parent process at the same time, only one might be delivered, as far as I know.
EDIT: I think a "better" solution (more flexible and more portable) would be along the lines of: do not use signals at all, but use pipes. You make one pipe between the parent and each of the children, and the children send a character 'X' over the pipes when they are done. The parent waits with select(); or if it just wants to wait until both children are ready, it can read the 'X' character from one pipe and then the other, blocking in both cases (the order doesn't matter).
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.