I forked a child and I am trying to synchronize them so they print
child 0
parent 0
child 1
parent 1
I have to use sigsuspend though, this is my code for the moment and all I get is parent suspend. There is no trace of the child.
int c=0, receivedP=0, receivedC=0;
sigset_t setParent, setChild;
void handler(int s){
if(s==SIGUSR1){
receivedC=1;
printf("parent --sig1--> child\n");
c++;
}
else{
receivedP=1;
printf("child --sig2--> parent\n");
}
}
void child(){
sigfillset(&setChild);
sigdelset(&setChild,SIGUSR1);
sigdelset(&setChild,SIGINT); //this makes me able to terminate the program at any time
while(1){
if(receivedC==0){
printf("child suspend\n");
sigsuspend(&setChild);
}
receivedC=0;
printf("child %d\n",c);
kill(getppid(),SIGUSR2);
}
}
void parent(pid_t pf){
sigfillset(&setParent);
sigdelset(&setParent,SIGUSR2);
sigdelset(&setParent,SIGINT); //this makes me able to terminate the program at any time
kill(pf,SIGUSR1);
while(1){
if(receivedP==0){
printf("parent suspend\n");
sigsuspend(&setParent);
}
receivedP=0;
printf("parent %d\n",c);
kill(pf,SIGUSR1);
}
}
int main(){
signal(SIGUSR1,handler);
signal(SIGUSR2,handler);
pid_t p;
p= fork();
if(!p)child();
else parent(p);
return 0;
}
Anybody knows what's causing this?
I think you are running foul of one of the classic problems with signals.
while(1){
if(receivedP==0){
printf("parent suspend\n");
sigsuspend(&setParent);
}
receivedP=0;
printf("parent %d\n",c);
kill(pf,SIGUSR1);
}
Imagine what happens if the signal from the child arrives in between the instructions for if(receivedP==0) and sigsuspend(&setParent). The handler will execute, and will set receivedP to one, but the main loop won't check it again; it will go into sigsuspend and never come out.
In order to use sigsuspend safely, you need to have the signals you care about be blocked at all times when the program is not calling sigsuspend. You do that with sigprocmask. It's also necessary to ensure that the signals are blocked during the execution of the handler, which requires you to use sigaction instead of signal (but you should do that anyway, as signal is severely underspecified and system-to-system variations will bite you in the ass).
Once you ensure that the signal can only be delivered during a sigsuspend, you no longer need the receivedP and receivedC variables; you know that the signal happened, or sigsuspend would not have returned. (This would not be true if your program was waiting for more than a single signal in each process, but at that point things get much more complicated; don't worry about it till it comes up.)
In fact, once you ensure that, you don't need to do anything in the signal handler. Your counter variable can be local to parent and child. It's always best to do as little in a signal handler as possible; the letter of the C standard allows you to do almost nothing without risking undefined behavior, and POSIX only opens it up a little bit more. (Exercise for you: change this program to use sigwaitinfo so that it doesn't need handler functions at all.)
This modification of your program works reliably for me. I also corrected a number of other style problems and minor errors: note the loops in parent and child doing things in different orders, the error checking in main, and that I am only blocking SIGUSR1 and SIGUSR2, because there are several other signals that should be allowed to terminate the process (SIGTERM, SIGHUP, SIGQUIT, SIGSEGV, …) and you don't want to have to maintain a list. It is sufficient to block the signals that the program has installed handlers for.
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
static void handler(int unused)
{
}
static void child(sigset_t *ss)
{
unsigned int c = 0;
pid_t parent_pid = getppid();
sigdelset(ss, SIGUSR1);
for (;;) {
sigsuspend(ss);
printf("child %u\n", c++);
kill(parent_pid, SIGUSR2);
}
}
static void parent(sigset_t *ss, pid_t child_pid)
{
unsigned int c = 0;
sigdelset(ss, SIGUSR2);
for (;;) {
printf("parent %u\n", c++);
kill(child_pid, SIGUSR1);
sigsuspend(ss);
}
}
int main(void)
{
// Ensure line-buffered stdout.
if (setvbuf(stdout, 0, _IOLBF, 0)) {
perror("setvbuf");
return 1;
}
// This signal mask is in effect at all times _except_ when sleeping
// in sigsuspend(). Note that _only_ the signals used for IPC are
// blocked. After forking, each process will modify it appropriately
// for its own use of sigsuspend(); this does not affect the kernel-side
// copy made by sigprocmask().
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss, SIGUSR1);
sigaddset(&ss, SIGUSR2);
if (sigprocmask(SIG_BLOCK, &ss, 0)) {
perror("sigprocmask");
return 1;
}
// Always use sigaction(), not signal(); signal() is underspecified.
// The mask here is the signal mask to use _while the handler is
// executing_; it should also block both IPC signals.
struct sigaction sa;
sa.sa_handler = handler;
sa.sa_mask = ss;
sa.sa_flags = SA_RESTART;
if (sigaction(SIGUSR1, &sa, 0) || sigaction(SIGUSR2, &sa, 0)) {
perror("sigaction");
return 1;
}
pid_t child_pid = fork();
if (child_pid < 0) {
perror("fork");
return 1;
}
if (child_pid == 0)
child(&ss);
else
parent(&ss, child_pid);
// we never get here but the compiler might not know that
return 0;
}
I recommend you read the GNU C Library Manual's section on signal handling all the way through; it contains several other bits of helpful advice on using signals safely.
Related
I have a problem with my code,
I want all the children stop when the program start.
and after that I want just the child with the index of i to continue executing and others to be stopped .
I want to execute them in this order p0 ,p1,p2,p3,p4,p0,p1....
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#define N 5
void handler(int i)
{
if (i == SIGCONT)
{
printf("signal cont\n");
}
}
int main()
{
int pid[N];
for (int i = 0; i < N; i++)
{
if ((pid[i] = fork()) == 0)
{
/* code */
while (1)
{
printf("ici fils %d\n", i);
usleep(50000);
}
}
else
{
kill(pid[i], SIGSTOP);
// kill(pid[i], SIGSTOP);
if (i == N - 1)
{
kill(pid[i], SIGCONT);
sleep(2);
kill(pid[i], SIGSTOP);
kill(pid[0], SIGCONT);
}
else
{
kill(pid[i], SIGCONT);
sleep(2);
kill(pid[i], SIGSTOP);
kill(pid[i + 1], SIGCONT);
}
// kill(pid[i], SIGKILL);
waitpid(pid[i], NULL, 0);
}
signal(SIGCONT, &handler);
}
}
There are several issues with your code, among them:
Any processes to be stopped via SIGSTOP must not have a handler registered for that signal. Registering a handler causes the handler's behavior to replace the default behavior of stopping the process.
It's usually a bad idea to register a handler for SIGCONT. Doing so will not prevent a SIGCONT from continuing the process, which is a special characteristic of SIGCONT that can be surprising, but also the handler will fire whenever a SIGCONT is delivered, even if the process was not stopped, which is often a different kind of surprise.
You register your signal handlers only in the parent, after the first fork. The subsequently forked children will inherit those, but the first one will not. Among other things, this will prevent the first child's pause() from being unblocked by the signals the parent sends to it. You can make each child register any needed handlers for itself, or you can register them in the parent, before the first fork.
There is a race between each child's pause() and the parent's first kill() targeting that child. It is possible for the child to receive the SIGCONT before it calls pause(), in which case it will wait for the next signal. You can prevent that by blocking SIGCONT in the parent before forking, and using sigsuspend() in the child, with an appropriate mask, instead of the initial pause(). In that case, you probably want to unblock SIGCONT after returning from that initial sigsuspend().
The parent attempts to send signals to processes that it has not forked yet (kill(pid[i + 1], SIGCONT);).
It's not clear what the full behavior you are trying to achieve is, but you may want to fork all the children first, and only then start sending signals.
Update
With respect to the update to the question,
You apparently want to cycle repeatedly through the child processes, but your code runs through them only once. This is a good reason to implement what I already suggested above: fork all the children first, then, separately, do all the signalling.
In the child processes, instead of using pause(2), use raise(3) to signal the calling process to stop with SIGSTOP. There is no real need to register signal handlers.
In the parent process, after creating a child process, wait for it to stop (or terminate) by using waitpid(2) with the WUNTRACED flag set. The WIFSTOPPED(...) macro can be used to specifically determine the status of the child. The WCONTINUE flag can be used to wait for a child process to continue, and like before there is the WIFCONTINUED(...) macro.
Here is a cursory example, with no meaningful error handling. Note that concurrent sleeps, while simple, are not technically a consistent way to schedule things. The output of this program may differ slightly between executions.
#define _POSIX_C_SOURCE 200809L
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#define CHILDCOUNT 5
sig_atomic_t looping = 1;
void handler(int sig) {
(void) sig;
looping = 0;
}
pid_t create_child(void) {
pid_t pid = fork();
if (!pid) {
/* child */
raise(SIGSTOP);
pid_t self = getpid();
printf("SIGCONT in %d\n", self);
while (1) {
printf("RUNNING in %d\n", self);
sleep(1);
}
/* bug net */
exit(EXIT_SUCCESS);
}
return pid;
}
void killwait(pid_t pid, int sig) {
kill(pid, sig);
int status;
waitpid(pid, &status, WUNTRACED | WCONTINUED);
if (WIFSTOPPED(status))
printf("P: C(%d) STOPPED!\n", pid);
if (WIFCONTINUED(status))
printf("P: C(%d) CONTINUED!\n", pid);
if (WIFSIGNALED(status) && SIGKILL == WTERMSIG(status))
printf("P: C(%d) SUCCESSFULLY KILLED!\n", pid);
}
int main(void) {
pid_t pids[CHILDCOUNT];
/* tentative: catch this in all processes so the parent may reap manually */
signal(SIGINT, handler);
for (size_t i = 0; i < CHILDCOUNT; i++) {
pid_t current = pids[i] = create_child();
printf("Parent now has child (%d) [#%zu].\n", current, i);
killwait(current, 0);
}
for (size_t i = 0; looping; i = (i + 1) % CHILDCOUNT) {
pid_t current = pids[i];
printf("P: C(%d) STARTING [#%zu].\n", current, i);
killwait(current, SIGCONT);
sleep(2);
killwait(current, SIGSTOP);
}
for (size_t i = 0; i < CHILDCOUNT; i++)
killwait(pids[i], SIGKILL);
}
I have the following code:
//includes...
#define SIG_INT 0
//prints out <Text> when SIG_INT is received from siginfo_t*
void handler(int, siginfo_t*, void*);
int main()
{
pid_t ambulance1 = 0;
pid_t ambulance2 = 0;
struct sigaction sigact;
sigact.sa_sigaction = handler;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = SA_SIGINFO;
sigaction(SIGUSR1, &sigact, NULL);
ambulance1 = fork();
if(ambulance1 > 0) {
ambulance2 = fork();
if(ambulance2 > 0) { // parent
int status;
waitpid(ambulance1, &status, 0);
waitpid(ambulance2, &status, 0);
printf("HQ went home!\n");
}
}
if(ambulance1 == 0 || ambulance2 == 0) {
union sigval signalValueInt;
signalValueInt.sival_int = SIG_INT;
sigqueue(getppid(), SIGUSR1, signalValueInt);
printf("Ambulance[%d] ended.\n", getpid());
}
return 0;
}
What happens is: sometimes the second ambulance's sigqueue(getppid(), SIGUSR1, signalValueInt); doesn't get received, and the output is something like the following:
Ambulance[20050] ended. // main() prints out this
Ambulance[20051] ended. // main() prints out this
// handler() prints out this with write() ONLY ONCE!
HQ went home! // main() prints out this
I know that the signal is lost, because the two signals arrived too quickly after one another, and the operating sys. thinks it's an error-duplicate, so it gets ignored.
My question is:
Is there a way to tell the operating system not to do that?
I wouldn't like to use two different signals (ex.: SIGUSR1 and SIGUSR2) for the same purpose, and I also wouldn't like to use delay in one of the child process.
The answer is in the manual page of signal(7).
But breafly:
if a standard signal arrives (like SIGUSR1) while the handler is running it will get ignored by the operating system.
if a real-time signal arrives (like SIGRTMIN) while handler is running it will be processed after the handler is done running.
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
void handler(int signumber)
{
return;
}
int main()
{
int i, pid;
int children_count = 5;
int arr_childprocesses[5];
int parent_pid = getpid();
for(i=0;i<children_count;i++)
{
pid = fork();
if(pid == -1)
{
perror("Err");
exit(EXIT_FAILURE);
}
if(pid == 0) break;
arr_childprocesses[i] = pid;
}
if (pid == 0) // children
{
kill(parent_pid,SIGUSR1);
printf("Child(%d) sig sent. Waiting 5s...\n",getpid());
sleep(5);
printf("Child(%d) terminated.\n",getpid());
}
else // parent
{
signal(SIGUSR1,handler);
for(i=0;i<children_count;++i)
{
waitpid(arr_childprocesses[i],NULL,0);
printf("Parent: Signal received.\n");
}
printf("Parent(%d) signals received. Waiting 3s...\n",getpid());
sleep(3);
printf("Parent(%d) terminated.\n",getpid());
}
exit(EXIT_SUCCESS);
}
I want to wait until all the children send me a signal. Then do some work with the children and with the parent too. But the program stops until all the children terminate. How should I do this?
Result:
Update 1: full code plus result included
You are probably facing a race here.
The parent receives the SIGUSR1 before its handler for this signal had been set up. As the default behaviour on receiving a SIGUSR1 is to end, the parent dies.
You want to setup the signal handler inside the parent before forking off the child.
(If from the programs design it is unacceptbale for the child to have SIGUSR1 signal handler set up, just call signal(SIGUSR1, SIG_DFL) as the 1st statement inside the child to deinstall this handler.)
To prove this theory you might like to temporarily add a sleep(1); inside the child just before the call to kill().
As a hint to fulfill your assignment:
Have a look at sigaction() as it provides a much more powerful interface to signalling then the function signal() does. Especially read about the SA_SIGINFO flag as it enables passing a siginfo_t typed variable to your signal handler. This latter variable carries info on who (identified by PID) sent the signal, which is the key to your solution.
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.
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);
}