Sending and handling a signal on a cloned thread - c

UPDATE: This appears to be a timing issue. Adding a call to sleep before the call to kill makes everything work as expected.
I have been playing with clone(2) and trying to get a handle on how it works. I am currently having trouble sending signals to a cloned process. I have the following code:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sched.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
volatile int keep_going = 1;
typedef void (*sighandler_t)(int);
void handler(int sig) {
printf("Signal Received\n");
keep_going = 0;
}
int thread_main(void* arg) {
struct sigaction usr_action;
sigset_t block_mask;
sigfillset(&block_mask);
usr_action.sa_handler = &handler;
usr_action.sa_mask = block_mask;
usr_action.sa_flags = 0;
sigaction(SIGUSR1, &usr_action, NULL);
printf("Hello from cloned thread\n");
while(keep_going);
}
int main(int argc, char **argv) {
void* stack = malloc(4096);
int flags = SIGCHLD;
int child_tid = clone(&thread_main, stack + 4096, flags, NULL);
if (child_tid < 0) {
perror("clone");
exit(EXIT_FAILURE);
}
printf("My pid: %d, child_tid: %d\n", (int) getpid(), (int) child_tid);
int kill_ret = kill(child_tid, SIGUSR1);
if (kill_ret < 0) {
perror("kill");
exit(EXIT_FAILURE);
}
int status = 0;
pid_t returned_pid = waitpid(child_tid, &status, 0);
if (returned_pid < 0) {
perror("waitpid");
exit(EXIT_FAILURE);
}
if (WIFEXITED(status)) {
printf("exited, status=%d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("killed by signal %d\n", WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
printf("stopped by signal %d\n", WSTOPSIG(status));
} else if (WIFCONTINUED(status)) {
printf("continued\n");
}
exit(EXIT_SUCCESS);
}
Which yields the following output:
My pid: 14101, child_tid: 14102
killed by signal 10
The child was obviously killed as a result of the signal, why did the signal handler not get called?

To avoid the race condition, catch the signal on the parent, before the clone() call. The child inherits a copy of the parent's signal handlers. You can reset it later on the parent to SIG_DFL if you want. (Also, getpid() is async-signal-safe, if you want to emulate SIG_DFL behaviour on the parent).

The child is not receiving the signal because before the child has reached to the call to sigaction the parent is sending the signal and thats why it is getting killed. You should avoid setting the signal handler this way. Still if you want to do this way only then make sure is parent is waiting until the child sets up the signal handler. With this scenario you should see the expected result.

First what is strange is you didn't get this message :
"Hello from cloned thread\n"
therefore your child tread gets terminated before it manages to setup the signal handler.
EDIT:
I just saw your comment about sleep. Try to add another variable, which is set when the sigaction gets executed. The main thread should be blocked until this variable is not set.

Related

sending signals to child processes , SIGCONT ,SIGSTOP

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);
}

waitpid stops waiting after signal is sent

I am currently working on a C project for university. Among other things I should signal the parent process using SIGUSR1.
The problem I'm facing at the moment is that I also need to wait for the child process to terminate so I can safely shut down everything in the end (removing shared Memory etc.).
At the moment I am using sigaction() to respond to the signal and waitpid() to wait for the child to terminate (that was the plan anyways ^^). But when I signal the parent using kill(), waitpid() stops waiting and runs the remainder of the parent even though the child is still running.
I feel like I'm missing something obvious but I can't figure it out.
Any help is greatly appreciated,
stay safe
Tim
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
void handle_sigusr1(int sig) {
printf("Recieved signal %i.\n", sig);
}
int main() {
pid_t pid;
pid = fork();
if (pid == -1) {
perror("fork:");
return EXIT_FAILURE;
}
else if (pid == 0) {
printf("Hello from the child.\n");
kill(getppid(), SIGUSR1);
sleep(3);
printf("Hello again from the child.\n");
return EXIT_SUCCESS;
}
else {
printf("Hello from the parent.\n");
struct sigaction sa;
sa.sa_handler = &handle_sigusr1;
sigaction(SIGUSR1, &sa, NULL);
int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status))
printf("Exit status: %i\n", WEXITSTATUS(status));
printf("Finished waiting for child.\n");
return EXIT_SUCCESS;
}
}
Output:
Hello from the parent.
Hello from the child.
Recieved signal 10.
Exit status: 0
Finished waiting for child.
tim#schlepptop:signalTest$ Hello again from the child.
PS: WEXITSTATUS(status) is usually 0 but sometimes it's also something like 16 or 128.
Per POSIX waitpid() documentation:
RETURN VALUE
... If wait() or waitpid() returns due to the delivery of a signal to the calling process, -1 shall be returned and errno set to [EINTR]. ...
You need to check the return value:
int status
do
{
errno = 0;
int rc = waitpid(pid, &status, 0);
if ( rc != -1 )
{
break;
}
}
while ( errno == EINTR );

synchronization between two process in c

I am trying to send signals between two child in alternative way for 100 times.
Here is my snippet of code.
here is the link to the whole question:
sending signal between two child process
But i have synchronization issue in the loop.
where is the right position to put the sigsuspend()?
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <ctype.h>
pid_t pid2;
struct sigaction act;
sigset_t mask,oldmask,temp;
void sighandler(int signum, siginfo_t *info, void *ptr)
{
printf("Received signal %d\n", signum);
printf("Signal originates from process %lu\n",
(unsigned long)info->si_pid);
pid2 = info->si_pid;
}
int main(int argc,char **argv)
{
int i,j,counter = 0,counter2 = 0;
sigemptyset(&mask);
sigemptyset(&temp);
//sigemptyset(&oldmask);
sigaddset(&mask,SIGUSR1);
//sigset_t mask;
memset(&act, 0, sizeof(act));
act.sa_sigaction = sighandler;
act.sa_flags = SA_SIGINFO;
if(sigaction(SIGUSR1, &act, NULL) == -1)
fprintf(stderr, "sigaction failed: %s\n", strerror(errno));
pid_t current, pidOther;
current = getpid();
pidOther = atol(argv[1]);
int k;
for(k = 0;k < 100;k++){
if(pidOther != 0){ // second child
kill(pidOther,SIGUSR1);
sigprocmask(SIG_BLOCK,&mask,&oldmask);
counter++;
printf("2nd child = %d sent signal to 1st child = %d signal number = %d\n",getpid(),pidOther,counter);
//sigprocmask(SIG_BLOCK,&mask,&oldmask);
sigsuspend(&temp);
}
if(pidOther == 0) // fisrt child
{
//pause();
kill(pid2,SIGUSR1);
sigprocmask(SIG_BLOCK,&mask,&oldmask); // was blank
counter++;
printf("\nj=%d 1st child = %d sent signal to 2nd child = %d signal counter = %d\n",j,getpid(),pid2,counter);
printf("test1\n");
sigsuspend(&temp); // was pause()
}
}
return 0;
}
I don't see you calling fork() anywhere. Also taking the process ID of the second process is not the way your program should know about the child process. Here's a simple example of how to use fork.
pid_t pid = fork();
if (pid == 0)
{
// executes only in child process..
// do stuff related what you need to do in child process
}
else
{
// executes only in parent process
// pid variable contains the child process's PID.
// do stuff related what you need to do in parent process
}
// runs in both parent and child.
The problem is that the first time the first child loops, pid2 is 0, so it sends the signal to every process in the process group (including itself), which means it will start looping immediately, sending signals (just) back to itself...

signal handler showing confusion in C

I am trying to use signal to sync N processes then print out something.
Each child process register a handler which print "yo" and "hihi" when catching SIGUSR1.
I use kill(0, SIGUSR1) to trigger every process. Since the default action for catching SIGUSR1 is being killed, I set a do-nothing handler for the main process so that it will wait all child died.
The fork and send signal program will repeat for k times, I expect it will show out N*k times "yo" and "hihi". However, it doesn't show enough "yo" and "hihi" as I expect. The number of "yo" is different every execution.
Here is my code, and thanks for your help!
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <time.h>
#include <signal.h>
#include <sys/time.h>
#include <string.h>
#define N 3
int pid_id[N];
void handler2 (int signum)
{
printf("hihi\n");
}
void handler (int signum)
{
signal(SIGUSR2, handler2);
printf("yo\n");
raise(SIGUSR2);
}
void handler_do_nothing (int signum)
{
;
}
void child(int process_index)
{
struct sigaction sa;
/* Register */
memset(&sa, 0, sizeof(sa));
sa.sa_handler = handler;
sigaction(SIGUSR1, &sa, NULL);
printf("I am %d.\n", getpid());
pid_id[process_index] = getpid();
sleep(1);
exit(0);
}
int main()
{
int i, k, status;
pid_t pid[N];
pid_t pid_wait;
struct sigaction sa_main;
/* Register */ /* Main process will terminate if catch SIGUSR1 by default setting*/
memset(&sa_main, 0, sizeof(sa_main));
sa_main.sa_handler = handler_do_nothing;
sigaction(SIGUSR1, &sa_main, NULL);
/* Race k times */
for (k=0;k<3;k++)
{
for (i=0;i<N;i++)
{
pid[i] = fork();
if (pid[i]==0)
{
child(i);
}
}
// sleep();
kill(0, SIGUSR1);
for (i=0;i<N;i++)
{
do
{
pid_wait = waitpid(pid[i], &status, WNOHANG);
printf("I am waiting..\n");
sleep(1);
}while(pid_wait != pid[i]);
}
}
printf("all done\n");
return 0;
}
Your child processes are being signalled before they have had time (i.e. execution resource scheduled) to install the new signal handler.
This means that when the main program sends SIGUSR1, some subset of the child processes will still have handler_do_nothing installed.
If you want to wait until the child processes have all finished setting up, you will need to add some interprocess communication mechanism - e.g. the children could signal the parent process when they are ready.

Parent process doesn't complete after child is terminated in C

I'm having trouble with a process forking exercise. I want to fork a child process and have it hang after announcing it has been forked, and wait for a signal to terminate, after which the parent process must announce it is terminating and then exit.
I can get the processes forked and have the parent wait for the hanging child to be killed by the signal, but it seems to kill the parent as well. I tried killing the child process specifically by its PID, but with no success.
Thanks for any help!
Code:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
void catchInt (int signum)
{
printf("\nMy sincerest apologies, master\n");
/*kill(0, SIGINT);*/
exit(0);
}
void ignoreInt (int signum)
{
wait(NULL);
}
int main () {
pid_t pid;
/* fork process */
pid = fork();
if (pid < 0) /* error handler */
{
fprintf(stderr, "Fork Failed");
exit(-1);
}
else if (pid == 0) /* child */
{
printf("Child reporting in\n");
signal(SIGINT, catchInt);
for ( ;; )
pause();
}
else /* parent */
{
/* parent will wait for the child to complete */
signal(SIGINT, ignoreInt);
wait(NULL);
printf("You're welcome\n");
exit(0);
}
}
Even assuming you fix the code so it compiles (you've not defined tempPID), there are problems:
You set the child to go to sleep until a signal arrives.
You set the parent to wait until the child dies.
So, you have a state where neither process is going to do anything more.
You probably need the parent to send a signal to the child:
kill(pid, SIGINT);
It is not clear that you need the parent to set a signal handler.
You probably want the child to set a signal handler.
You probably don't want the infinite loop in the child.
Oh, and void main() is incorrect - int main() or int main(void) or int main(int argc, char **argv) are the approved declarations for main().
And it is tidier if you return a value (0) from main(). The C99 standard does permit you to drop off the end of main() and will treat that as returning zero, but only if the function is properly declared as returning an int.
The header for wait() and relatives in POSIX is <sys/wait.h>.
And, because I'm a sucker, here's code that compiles and might even do what you want:
#include <stdio.h>
#include <signal.h>
#include <unistd.h> /* getpid() */
#include <stdlib.h>
#include <sys/wait.h>
void catchInt(int signum)
{
printf("Child's PID is %d\n", (int)getpid());
printf("My sincerest apologies, master\n");
exit(1);
}
int main()
{
pid_t pid = fork();
if (pid < 0) /* error handler */
{
fprintf(stderr, "Fork Failed");
exit(-1);
}
else if (pid == 0) /* child */
{
printf("Child reporting in\n");
signal(SIGINT, catchInt);
pause();
}
else /* parent */
{
sleep(1);
kill(pid, SIGINT);
wait(NULL);
printf("You're welcome\n");
}
return(0);
}
Just figured out what I was doing wrong, I should have realized SIGINT is sent to every process, and so the parent was simply being sent an unhandled SIGINT, causing it to exit. Thanks for all the help (my apologies on the sloppy coding, I really shouldn't wait until the program is completed to clean that up), the code's been edited above and works as intended.
Thanks again.

Resources