Our task is to develop an application that creates a child process. The parent process then waits 3 seconds (we are not allowed to use sleep in the parent process) and sends a SIGUSR2 signal followed by a SIGUSR1 signal. After that, the parent sends the SIGUSR1 signal periodically (3 seconds) to the child until it terminates. Upon child termination it prints Parent done and exits. We should implement this behaviour using alarm() and pause().
The child process should block the signal SIGUSR2 for 13 seconds. Upon receiving SIGUSR1 it should print Received SIGUSR1. Upon receiving SIGUSR2 it prints Child done and exits. My approach to implement this is the following:
Please note: the SIGUSR2 signal is only sent once, until now I did not implement that and it is sent each time along with the SIGUSR1 signal.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <stdarg.h>
static pid_t chld;
void init_signal(struct sigaction* action, void (*handler)(int));
void init_sigset(sigset_t* set, int signum);
void bind_signal(struct sigaction* action, int n, ...);
void par_signal(int signum);
void chld_signal(int signum);
int main(){
chld = fork();
struct sigaction action;
if (chld == -1){
fprintf(stderr, "Failed to create child process.\n");
return EXIT_FAILURE;
} else if (chld == 0){
init_signal(&action, &chld_signal);
bind_signal(&action, 3, SIGUSR1, SIGUSR2, SIGALRM);
sigset_t sig;
init_sigset(&sig, SIGUSR2);
sigprocmask(SIG_BLOCK, &sig, NULL);
alarm(13);
pause();
}
init_signal(&action, &par_signal);
bind_signal(&action, 2, SIGALRM, SIGCHLD);
alarm(3);
pause();
}
void init_signal(struct sigaction* action, void (*handler)(int)){
action->sa_flags = 0;
action->sa_handler = handler;
sigemptyset(&action->sa_mask);
}
void init_sigset(sigset_t* set, int signum){
sigemptyset(set);
sigaddset(set, signum);
}
void bind_signal(struct sigaction* action, int n, ...){
va_list args;
va_start(args, n);
for (int i = 0; i < n; i++){
int signum = va_arg(args, int);
sigaction(signum, action, NULL);
}
va_end(args);
}
void par_signal(int signum){
if (signum == SIGALRM){
kill(chld, SIGUSR2);
kill(chld, SIGUSR1);
alarm(3);
pause();
} else if (signum == SIGCHLD){
printf("Parent done\n");
exit(EXIT_SUCCESS);
}
}
void chld_signal(int signum){
if (signum == SIGALRM){
sigset_t sig;
init_sigset(&sig, SIGUSR2);
sigprocmask(SIG_UNBLOCK, &sig, NULL);
pause();
} else if (signum == SIGUSR1){
printf("Received SIGUSR1\n");
pause();
} else if (signum == SIGUSR2){
printf("Child done\n");
exit(EXIT_SUCCESS);
}
}
It works apart from the fact, that only the first signal of the parent process is received by the child. I figure this has something to do with my usage of pause respectively alarm. Do you have any idea what I am doing wrong?
The link to the exercise tasksheet can be found here (Task 3).
Calling pause in the signal handler is problematic because in this situation the signal that just triggered the signal handler is blocked until you return from the signal handler. That means the process will no longer react to this signal. The signal handler can only be triggered by other signals.
See below a system call trace with explanations. I replaced the parent's PID with #PARENT# and the child's PID with #CHILD## for clarity.
$ strace -f -r -e trace=signal,process ./proc
0.000000 execve("./proc", ["./proc"], 0x7ffc146ba360 /* 74 vars */) = 0
0.001428 arch_prctl(0x3001 /* ARCH_??? */, 0x7ffd3c958fc0) = -1 EINVAL (Invalid argument)
0.002302 arch_prctl(ARCH_SET_FS, 0x7f264e40a540) = 0
# clone is fork()
0.001086 clone(strace: Process #CHILD## attached
child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f264e40a810) = #CHILD##
# parent establishes SIGALRM handler
[pid #PARENT#] 0.000791 rt_sigaction(SIGALRM, {sa_handler=0x564709a5859c, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f264e25e470}, <unfinished ...>
# child establishes SIGUSR1 handler
[pid #CHILD##] 0.000087 rt_sigaction(SIGUSR1, {sa_handler=0x564709a58605, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f264e25e470}, <unfinished ...>
[pid #PARENT#] 0.000132 <... rt_sigaction resumed> NULL, 8) = 0
[pid #CHILD##] 0.000056 <... rt_sigaction resumed> NULL, 8) = 0
# child establishes SIGUSR2 handler
[pid #CHILD##] 0.000072 rt_sigaction(SIGUSR2, {sa_handler=0x564709a58605, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f264e25e470}, NULL, 8) = 0
# child establishes SIGALRM handler
[pid #CHILD##] 0.000141 rt_sigaction(SIGALRM, {sa_handler=0x564709a58605, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f264e25e470}, NULL, 8) = 0
# parent establishes SIGCHLD handler
[pid #PARENT#] 0.000389 rt_sigaction(SIGCHLD, {sa_handler=0x564709a5859c, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f264e25e470}, <unfinished ...>
# child blocks SIGUSR2
[pid #CHILD##] 0.000132 rt_sigprocmask(SIG_BLOCK, [USR2], NULL, 8) = 0
# child waits for signal
[pid #CHILD##] 0.000204 pause( <unfinished ...>
[pid #PARENT#] 0.000837 <... rt_sigaction resumed> NULL, 8) = 0
# parent waits for signal
[pid #PARENT#] 0.000133 pause() = ? ERESTARTNOHAND (To be restarted if no handler)
# parent receives SIGALRM
[pid #PARENT#] 3.000317 --- SIGALRM {si_signo=SIGALRM, si_code=SI_KERNEL} ---
# parent sends SIGUSR2 and SIGUSR1 to child
[pid #PARENT#] 0.000106 kill(#CHILD##, SIGUSR2) = 0
[pid #PARENT#] 0.000116 kill(#CHILD##, SIGUSR1) = 0
[pid #CHILD##] 0.000144 <... pause resumed> ) = ? ERESTARTNOHAND (To be restarted if no handler)
# child receives SIGUSR1 (SIGUSR2 is blocked)
[pid #CHILD##] 0.000166 --- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_USER, si_pid=#PARENT#, si_uid=1000} ---
# parent waits for signal again. As this "pause" was called from a signal handler triggered by SIGALRM, this signal is now blocked.
[pid #PARENT#] 0.000227 pause( <unfinished ...>
# child waits for signal. As this "pause" was called from a signal handler triggered by SIGUSR1, this signal is now blocked.
[pid #CHILD##] 0.000566 pause() = ? ERESTARTNOHAND (To be restarted if no handler)
# child receives SIGALRM
[pid #CHILD##] 9.997742 --- SIGALRM {si_signo=SIGALRM, si_code=SI_KERNEL} ---
# child unblocks SIGUSR2
[pid #CHILD##] 0.000102 rt_sigprocmask(SIG_UNBLOCK, [USR2], NULL, 8) = 0
# child now receives SIGUSR2 which was already sent by the parent
[pid #CHILD##] 0.000122 --- SIGUSR2 {si_signo=SIGUSR2, si_code=SI_USER, si_pid=#PARENT#, si_uid=1000} ---
# child exits
[pid #CHILD##] 0.000183 exit_group(0) = ?
[pid #CHILD##] 0.000204 +++ exited with 0 +++
0.000081 <... pause resumed> ) = ? ERESTARTNOHAND (To be restarted if no handler)
# parent receives SIGCHLD from child
0.000056 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=#CHILD##, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
Parent done
# parent exits
0.000563 exit_group(0) = ?
0.000301 +++ exited with 0 +++
I suggest to set flags from the signal handlers and do the decisions and waiting in the main function. Something like this
static volatile sig_atomic_t childSigUsr1 = 0;
static volatile sig_atomic_t childSigUsr2 = 0;
static volatile sig_atomic_t childSigAlrm = 0;
void chld_signal(int signum) {
if (signum == SIGALRM) {
childSigAlrm = 1;
} else if (signum == SIGUSR1) {
childSigUsr1 = 1;
} else if (signum == SIGUSR2) {
childSigUsr2 = 1;
}
}
int main() {
chld = fork();
struct sigaction action;
if (chld == -1) {
fprintf(stderr, "Failed to create child process.\n");
return EXIT_FAILURE;
} else if (chld == 0) {
init_signal(&action, &chld_signal);
bind_signal(&action, 3, SIGUSR1, SIGUSR2, SIGALRM);
sigset_t sig;
init_sigset(&sig, SIGUSR2);
sigprocmask(SIG_BLOCK, &sig, NULL);
alarm(13);
while(1) {
pause();
if(childSigUsr1) {
childSigUsr1 = 0;
printf("Received SIGUSR1\n");
}
if(childSigUsr2) {
childSigUsr2 = 0;
printf("Child done\n");
exit(EXIT_SUCCESS);
}
if(childSigAlrm) {
sigset_t sig;
init_sigset(&sig, SIGUSR2);
sigprocmask(SIG_UNBLOCK, &sig, NULL);
}
}
}
init_signal(&action, &par_signal);
bind_signal(&action, 2, SIGALRM, SIGCHLD);
while(1) {
alarm(3);
pause();
/* handle and reset signal flags similar to child */
}
}
Related
In the following code, I try to send SIGINT, SIGHUP, SIGQUIT signal to child process.
void sighup(int sig);
void sigint(int sig);
void sigquit(int sig);
These are my signal handler.
the issue is signal handler never invoked.
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
void sighup(int sig);
void sigint(int sig);
void sigquit(int sig);
int main()
{
int pid, i, j, k;
if ((pid = fork()) < 0)
{
perror("fork");
exit(1);
}
if(pid == 0)
{
signal(SIGHUP, sighup);
signal(SIGINT, sigint);
signal(SIGQUIT, sigquit);
}
else
{
j = 0;
for (i = 1; i <= 5; i++)
{
j++;
if (i % 2 == 0)
{
printf("PARENT: sending SIGHUP Signal : %d\n", j);
kill(pid, SIGHUP);
sleep(3);
}
else
{
printf("PARENT: sending SIGINT signal : %d\n", j);
kill(pid, SIGINT);
sleep(3);
}
}
printf("Parent sending SIGQUIT\n");
kill(pid, SIGQUIT);
}
}
void sighup(int sig)
{
signal(SIGHUP, sighup);
printf("Child: I have received sighup\n");
}
void sigint(int sig)
{
signal(SIGINT, sigint);
printf("Child: I have received sighINT\n");
}
void sigquit(int sig)
{
printf("My daddy has killed me\n");
exit(0);
}
Below lines never printed on screen
Child: I have received sighup
Child: I have received sighINT
My daddy has killed me
Output
PARENT: sending SIGINT signal : 1
PARENT: sending SIGHUP Signal : 2
PARENT: sending SIGINT signal : 3
PARENT: sending SIGHUP Signal : 4
PARENT: sending SIGINT signal : 5
Parent sending SIGQUIT
You have two problems here.
First, after the child process sets up its signal handlers, it exits right away. So the parent might get to send the first signal depending on timing, but not any others.
Put the child in a pause loop to have it wait for signals.
The other problem is that it's possible that the parent might send the first signal to the child before it can set up its signal handlers. So put a short delay in the parent to allow that to happen.
if(pid == 0)
{
signal(SIGHUP, sighup);
signal(SIGINT, sigint);
signal(SIGQUIT, sigquit);
while (1) pause();
}
else
{
sleep(1);
...
Also, calling printf and exit from a signal handler are not considered safe. It's better to have the signal handlers set a global variable and have the main part of the code check for that.
int gotsig = 0;
void sighup(int sig)
{
signal(SIGHUP, sighup);
gotsig = sig;
}
void sigint(int sig)
{
signal(SIGINT, sigint);
gotsig = sig;
}
void sigquit(int sig)
{
gotsig = sig;
}
...
while (1) {
pause();
if (gotsig == SIGHUP) {
printf("Child: I have received sighup\n");
} else if (gotsig == SIGINT) {
printf("Child: I have received sighINT\n");
} else if (gotsig == SIGQUIT) {
printf("My daddy has killed me\n");
exit(0);
}
gotsig = 0;
}
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<signal.h>
void signint(int sig);
void sighup(int sig);
void sigquit(int sig);
int gotsig;
int main()
{
int pid,i;
pid = fork();
if(pid < 0)
{
perror("fork");
exit(0);
}
if(pid == 0)
{
signal(SIGHUP,sighup);
signal(SIGINT,signint);
signal(SIGQUIT,sigquit);
pause();
while(1)
{
if(gotsig == SIGINT)
{
printf("Child : Child process recieved SIGINT signal\n");
gotsig = -1;
}else if(gotsig == SIGHUP)
{
printf("Child : Child process recieved SIGHUP signal\n");
gotsig = -1;
}else if(gotsig == SIGQUIT)
{
printf("Dady killed me....!\n");
exit(0);
}
}
}else
{
sleep(1);
for(i = 1; i <=5 ; i++)
{
if(i % 2 == 0)
{
printf("Parent : sending SIGINT signal\n");
kill(pid,SIGINT);
sleep(3);
}else
{
printf("Parent : sending SIGHUP signal\n");
kill(pid,SIGHUP);
sleep(3);
}
}
printf("Parent : sending SIGQUIT signal\n");
kill(pid,SIGQUIT);
}
}
void signint(int sig)
{
gotsig = sig;
}
void sighup(int sig)
{
gotsig = sig;
}
void sigquit(int sig)
{
gotsig = sig;
}
Above code work fine for me.
Output :
Parent : sending SIGHUP signal
Child : Child process recieved SIGHUP signal
Parent : sending SIGINT signal
Child : Child process recieved SIGINT signal
Parent : sending SIGHUP signal
Child : Child process recieved SIGHUP signal
Parent : sending SIGINT signal
Child : Child process recieved SIGINT signal
Parent : sending SIGHUP signal
Child : Child process recieved SIGHUP signal
Parent : sending SIGQUIT signal
Dady killed me....!
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.
I'm using PTRACE_SEIZE to trace the execution of a child process, but I'm running into an issue where a non-group-stop PTRACE_EVENT_STOP (signal == SIGTRAP) is being emitted when the tracee receives a SIGCONT. I can't seem to find any documentation that might indicate why this is happening. From what I can gather in the ptrace(2) manpage, PTRACE_EVENT_STOP only occurs when one of the following conditions are true:
The tracer called ptrace(PTRACE_INTERRUPT, ...)
The tracee entered group stop (signal is SIGSTOP, SIGTSTP, SIGTTIN, or SIGTTOU)
The tracee called fork/vfork/clone and the tracer automatically attached to the new process/thread
In the sample program below, I'm getting a PTRACE_EVENT_STOP with signal SIGTRAP, but it doesn't match any of those 3 conditions. When I run the program, I get the following output:
[Child] Raising SIGSTOP
Event for pid 29236: Tracee entered group-stop PTRACE_EVENT_STOP (signal: 19)
Event for pid 29236: Tracee entered non-group-stop PTRACE_EVENT_STOP (signal: 5)
Event for pid 29236: Tracee entered/exited syscall
Event for pid 29236: Tracee entered/exited syscall
Event for pid 29236: Tracee received signal (signal: 18)
Event for pid 29236: Tracee entered/exited syscall
[Child] Resumed from SIGSTOP
Event for pid 29236: Tracee entered/exited syscall
Event for pid 29236: Tracee entered/exited syscall
Event for pid 29236: Process exited
Any ideas as to what this particular PTRACE_EVENT_STOP means?
Sample program:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <unistd.h>
static void handle_events(pid_t pid_spec)
{
while (1) {
int status;
pid_t pid = waitpid(pid_spec, &status, __WALL);
printf("Event for pid %d: ", pid);
if (WIFEXITED(status)) {
printf("Process exited\n");
break;
} else if (WIFSIGNALED(status)) {
printf("Process killed by signal\n");
break;
} else if (WIFSTOPPED(status)) {
int ptrace_event = status >> 16;
int signal = WSTOPSIG(status);
switch (ptrace_event) {
case PTRACE_EVENT_STOP:
if (signal == SIGSTOP || signal == SIGTSTP
|| signal == SIGTTIN || signal == SIGTTOU) {
printf("Tracee entered group-stop PTRACE_EVENT_STOP (signal: %d)\n", signal);
ptrace(PTRACE_LISTEN, pid, NULL, NULL);
} else {
printf("Tracee entered non-group-stop PTRACE_EVENT_STOP (signal: %d)\n", signal);
ptrace(PTRACE_SYSCALL, pid, NULL, 0);
}
break;
default:
if (signal == (SIGTRAP | 0x80)) {
printf("Tracee entered/exited syscall\n");
ptrace(PTRACE_SYSCALL, pid, NULL, 0);
} else {
printf("Tracee received signal (signal: %d)\n", signal);
ptrace(PTRACE_SYSCALL, pid, NULL, signal);
}
break;
}
}
}
}
int main()
{
pid_t pid = fork();
if (pid == 0) {
printf("[Child] Raising SIGSTOP\n");
// Allow parent to PTRACE_SEIZE
if (raise(SIGSTOP) != 0) {
_exit(255);
}
printf("[Child] Resumed from SIGSTOP\n");
_exit(0);
}
// Wait for stop
int status;
waitpid(pid, &status, WSTOPPED);
ptrace(PTRACE_SEIZE, pid, NULL, PTRACE_O_TRACESYSGOOD);
// Allow child to continue
kill(pid, SIGCONT);
handle_events(pid);
return EXIT_SUCCESS;
}
strace seems to call PTRACE_SYSCALL whenever it sees a non-group-stop PTRACE_EVENT_STOP:
https://github.com/strace/strace/blob/b1e1eb7731e50900bb4591a3a71b96ab37e106a8/strace.c#L2360
https://github.com/strace/strace/blob/b1e1eb7731e50900bb4591a3a71b96ab37e106a8/strace.c#L2408-L2409
I managed to figure out why the event occurs.
When a process is being ptrace'd with PTRACE_SEIZE, ptrace_trap_notify() will be called to set JOBCTL_TRAP_NOTIFY when SIGCONT is received or JOBCTL_TRAP_STOP will be set when a stopping signal is received. get_signal() will check for JOBCTL_TRAP_MASK (ie. JOBCTL_TRAP_STOP | JOBCTL_TRAP_NOTIFY) and call do_jobctl_trap(). do_jobctl_trap() will then either send <stopping signal> | (PTRACE_EVENT_STOP << 8) if the process enters group stop or else it will send SIGTRAP | (PTRACE_EVENT_STOP << 8).
Thus, when using PTRACE_SEIZE:
A stopping signal will result in a <stopping signal> | (PTRACE_EVENT_STOP << 8) event
A SIGCONT signal will result in a SIGTRAP | (PTRACE_EVENT_STOP << 8) event
This behavior appears to have been introduced in commit fb1d910c178ba0c5bc32d3e5a9e82e05b7aad3cd.
i have a program which creates 2 child processes, so, child 1 sends SIGINT to child 2, child 2 handles SIGINT(the handler of SIGINT blocks SIGUSR1), after SIGINT, child 1 sends SIGUSR1 to child 2.
I don't know why SIGUSR1 terminates child 2. SIGUSR1 gets blocked when SIGINT is sent.
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <unistd.h>
sigset_t base_mask, waiting_mask;
void handler(int signum)
{
signal(SIGINT, handler);
sigemptyset (&base_mask);
sigaddset (&base_mask, SIGUSR1);
/* Block user interrupts while doing other processing. */
if(sigprocmask (SIG_BLOCK, &base_mask, NULL) != 0)
printf("cannot block\n");
else
printf("SIGUSR1 blocked\n");
if(sigismember(&base_mask, SIGUSR1))
printf("sigusr1 in mask\n");
}
int main()
{
if(fork())
{
if(fork())
{
printf("parent out\n");
}
else//child 2
{
printf("i am sib 2 with pid = %d\n", getpid());
signal(SIGINT, handler);
pause();
printf("before sleep\n");
sleep(10);
printf("after sleep\n");
/* After a while, check to see whether any signals are pending. */
sigpending (&waiting_mask);
if (sigismember (&waiting_mask, SIGUSR1))
printf("/* SIGUSR1 pending. */\n");
sleep(2);
printf("2 out\n");
}
}
else//child 1
{
printf("i am sib 1 with pid = %d\n", getpid());
sleep(2);
printf("SIGINT to %d\n", getpid() + 1);
if(kill(getpid() + 1, SIGINT))
printf("Signal cannot be sent\n");
sleep(4);
if(kill(getpid() + 1, SIGUSR1))
printf("Signal 2 cannot be sent\n");
else
printf("SIGUSR1 sent to %d\n", getpid() + 1);
sleep(4);
}
return 0;
}
output:
parent out,
i am sib 1 with pid = 2525,
i am sib 2 with pid = 2526,
SIGINT to 2526,
SIGUSR1 blocked,
sigusr1 in mask,
before sleep,
SIGUSR1 sent to 2526
The problem is: You block the signal here in the signal handler:
void handler(int signum) {
...
if(sigprocmask (SIG_BLOCK, &base_mask, NULL) != 0)
printf("cannot block\n");
else
printf("SIGUSR1 blocked\n");
if(sigismember(&base_mask, SIGUSR1))
printf("sigusr1 in mask\n");
}
But after returning from the signal handler, the blocked signals mask is restored to the value it had on entry of the handler! So, calling sigprocmask() has only effect on the blocked signals during the current handler invocation. You have to call it outside the signal handler.
I am trying to use signals to pass between a parent and child process, but after the first 2 statements are printed
for example in mine it shows:
CHILD 4225: Running, parent is 4224
PARENT 4224: Telling the Child Process 4225 to start
it just gets stuck running forever! I'm not sure where I am going wrong on this...
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
void p_sig_usr(int signo){
if(signo == SIGUSR1){
printf("*** Parent SIGUSR1 handler - Received 'task started' signal from child ***\n");
}
if(signo == SIGUSR2){
printf("*** Parent SIGUSR2 handler - Received 'task completed' signal from child ***\n");
}
else
printf("unexpected signal received");
return;
}
void c_sig_usr(int signo){
if(signo == SIGUSR1){
printf("*** Child SIGUSR1 handler - Received 'task start' signal from parent ***\n");
}
if(signo == SIGUSR2){
printf("*** Child SIGUSR2 handler - Received 'task complete verification' signal from parent ***\n");
}
else
printf("unexpected signal received");
return;
}
int main(void)
{
pid_t child_pid, parent_pid;
parent_pid = getpid();
struct sigaction p_sig;
sigemptyset(&p_sig.sa_mask);
p_sig.sa_flags = 0;
p_sig.sa_handler = p_sig_usr;
child_pid = fork();
if ( child_pid == -1){
perror("failed to fork a new process");
return 1;
}
if (child_pid == 0){
struct sigaction c_sig;
sigset_t c_myset;
sigemptyset(&c_sig.sa_mask);
c_sig.sa_flags = 0;
c_sig.sa_handler = c_sig_usr;
child_pid = getpid();
printf("CHILD %d: Running, parent is %d\n",child_pid, parent_pid);
sigfillset(&c_myset);
sigdelset(&c_myset, SIGUSR1);
sigsuspend(&c_myset);//suspend until get SIGUSR1
printf("CHILD: Telling parent that I'm starting task.\n");
sleep(3);
kill(parent_pid, SIGUSR1);
printf("CHILD: Performing task\n");
sigfillset(&c_myset);
sigdelset(&c_myset, SIGUSR2);
sigsuspend(&c_myset);//suspend and wait for SIGUSR2
printf("CHILD: Telling parent that work is done.\n");
kill(parent_pid, SIGUSR2);
printf("CHILD %d: Finished\n", child_pid);
}
else{
struct sigaction p_sig;
sigset_t p_myset;
sigemptyset(&p_myset);
sleep(3);//parent now sleeping to let child set up handlers
printf("PARENT %d: Telling the Child Process %d to start\n", parent_pid, child_pid);
kill(child_pid, SIGUSR1);
sigfillset(&p_myset);
sigdelset(&p_myset, SIGUSR1);
sigsuspend(&p_myset);//suspend until get SIGUSR1
sleep(3);
kill(child_pid,SIGUSR2);
printf("PARENT: Told child to notify of task completion.\n");
sigfillset(&p_myset);
sigdelset(&p_myset, SIGUSR2);//suspend until get SIGUSR2
printf("PARENT %d: Finished.", parent_pid);
}
return 0;
}
Thank you in advance for the help!
I'm just referring to the documentation for these functions—I have no experience using them.
It appears what sigfillset() is going to do is load the process signal mask into your sigset_t. This means that your sigset_t is going to contain the set of signals that are currently blocked by your process. I assume the default is nothing is blocked, so the set would be empty.
You might want to test this by printing out the contents of the set, or just looking at it in a debugger.
Now from the docs I understand what sigdelset(&p_myset, SIGUSR1) will do is remove the signal SIGUSR1 from the set you just filled. This set is by assumption already empty so it's unlikely this call does anything. Again, verify by looking at it in a debugger.
So now what sigsuspend() is going to do is replace your process signal mask with your new mask, which by assumption isn't any different than the default mask (again, check this in a debugger). Then on the child side will wait until the process receives SIGUSR1 and processes it via a signal handler. So your child will process SIGUSR1 but only because that's the default behaviour.
Your example code doesn't seem to have installed any signal handlers. I think you would have to call the sigaction() function to do that. Therefore very likely the default signal handler will run to process SIGUSR1.
According to this page, the default signal handling for SIGUSR1 is
(i) ... Abnormal termination of the process. The process is terminated with all the consequences of _exit() except that the status made available to wait() and waitpid() indicates abnormal termination by the specified signal.
So I'm guessing the child dies when the parent does kill(child_pid, SIGUSR1). This would mean the child isn't around to signal the parent back.
This is mainly guesswork on my part. What I recommend for you is learning how to use gdb or some other debugger so you can set some breakpoints and step through and learn what the program is actually doing.
You forgot to call sigaction after defining the struct sigaction on both the parent and child. Also, beware that the struct sigaction p_sig is redefined in the parent process.
So, I guess if you change your program to something like listed below, it should work.
--- foo.c 2014-06-16 16:37:10.918932118 -0300
+++ bar.c 2014-06-16 16:37:48.710228467 -0300
## -36,10 +36,6 ##
{
pid_t child_pid, parent_pid;
parent_pid = getpid();
- struct sigaction p_sig;
- sigemptyset(&p_sig.sa_mask);
- p_sig.sa_flags = 0;
- p_sig.sa_handler = p_sig_usr;
child_pid = fork();
if ( child_pid == -1){
perror("failed to fork a new process");
## -51,6 +47,7 ##
sigemptyset(&c_sig.sa_mask);
c_sig.sa_flags = 0;
c_sig.sa_handler = c_sig_usr;
+ sigaction(SIGUSR1, &c_sig, NULL);
child_pid = getpid();
printf("CHILD %d: Running, parent is %d\n",child_pid, parent_pid);
sigfillset(&c_myset);
## -69,6 +66,10 ##
}
else{
struct sigaction p_sig;
+ sigemptyset(&p_sig.sa_mask);
+ p_sig.sa_flags = 0;
+ p_sig.sa_handler = p_sig_usr;
+ sigaction(SIGUSR1, &p_sig, NULL);
sigset_t p_myset;
sigemptyset(&p_myset);
sleep(3);//parent now sleeping to let child set up handlers