I am trying to write a C program in which the parent process suspends the child process and after a few seconds continues executing it.
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
void sigstop();
void sigcont();
void sigquit();
int main()
{
int pid;
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0)
{ /* child */
signal(SIGSTOP,sigstop);
signal(SIGCONT,sigcont);
signal(SIGQUIT, sigquit);
for(;;); /* loop for ever */
}
else {
printf("\nPARENT: sending SIGSTOP to suspend the process\n\n");
kill(pid, SIGSTOP);
sleep(5);
printf("\nPARENT: sending SIGCONT to continue the suspended process\n\n");
kill(pid, SIGCONT);
sleep(5);
printf("killing child");
kill(pid,SIGQUIT);
sleep(5);
}
}
void sigstop()
{
printf("CHILD: I've been suspended\n");
}
void sigcont()
{
printf("CHILD: I'm back\n");
}
void sigquit()
{
printf("My DADDY has Killed me!!!\n");
exit(0);
}
printf inside sigstop() never gets executed and sigquit() gets called before printf("killing child");. How does this happen and how can I get output in proper order ?
If you read the signal(7) manual page you will see that
The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.
You simply can not catch that signal.
The last bit, about "Killing child" get printed in the wrong order is very easy to fix: Add a trailing newline to the string you print.
This is because output to stdout (which is what printf writes to) is by default line-buffered. This means that the internal buffer of stdout is flushed (i.e. actually written to the terminal) on a newline. If you don't have the newline, then the buffer will be flushed when the process exits, which is after you exit the child process.
Related
I am learning communication between two process using signals in C on GeeksForGeeks https://www.geeksforgeeks.org/signals-c-set-2/?ref=lbp. And I was trying to run the code provided on the website. I simply copy the code to an online C compiler.
// C program to implement sighup(), sigint()
// and sigquit() signal functions
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
// function declaration
void sighup();
void sigint();
void sigquit();
// driver code
void main()
{
int pid;
/* get child process */
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0) { /* child */
signal(SIGHUP, sighup);
signal(SIGINT, sigint);
signal(SIGQUIT, sigquit);
for (;;)
; /* loop for ever */
}
else /* parent */
{ /* pid hold id of child */
printf("\nPARENT: sending SIGHUP\n\n");
kill(pid, SIGHUP);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGINT\n\n");
kill(pid, SIGINT);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGQUIT\n\n");
kill(pid, SIGQUIT);
sleep(3);
}
}
// sighup() function definition
void sighup()
{
signal(SIGHUP, sighup); /* reset signal */
printf("CHILD: I have received a SIGHUP\n");
}
// sigint() function definition
void sigint()
{
signal(SIGINT, sigint); /* reset signal */
printf("CHILD: I have received a SIGINT\n");
}
// sigquit() function definition
void sigquit()
{
printf("My DADDY has Killed me!!!\n");
exit(0);
}
However, all I got is like this
PARENT: sending SIGHUP
PARENT: sending SIGINT
PARENT: sending SIGQUIT
I am wondering is it my computer problem?
The code incorrectly assumes that the child installs the handlers before the parent sends the signals. It could be so, but it is not guaranteed. Of course, if the handlers are not installed, the child is killed immediately. You may confirm that by testing that kill returns -1 and errno is set to ESRCH.
Also keep in mind that printf is not signal safe.
Not all online C compilers/environments are made equally. Try repl.it, I just tried it, and got the exact results that geeks4geeks posted.
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int pid = 0;
// some very time-consuming function
void childLabor() {
for (long long i=1;i<=10000000000;i++) {
//printf("i'm printing\n");
fflush(stdout);
}
}
// stop the process who calls this
void stopYourself() {
// TODO
}
void childReceiveStop() {
signal(SIGTSTP, childReceiveStop);
printf("I have important things to do first before stopping\n");
fflush(stdout);
// do important things
printf("I stop myself now\n");
fflush(stdout);
stopYourself();
}
void childReceiveContinue() {
signal(SIGCONT, childReceiveContinue);
}
int main()
{
pid = fork();
if (pid==0) {
signal(SIGTSTP, childReceiveStop);
signal(SIGCONT, childReceiveContinue);
stopYourself(); // wait until parent sends SIGCONT
childLabor();
}
else {
// start/stop child every 2 second
kill(pid,SIGCONT);
for (int i=1; i<=100; i++) {
printf("sending signal stop\n");
fflush(stdout);
kill(pid, SIGTSTP);
sleep(3);
printf("sending signal start\n");
kill(pid, SIGCONT);
sleep(1);
}
}
return 0;
}
Basically what I want to do in this example is let the child print for 3 seconds, then stop it, then let it print again, ... When the child receives SIGTSTP, it should stop. And when it receives SIGCONT, it should continue.
However, with or without handler, when the child process receives the SIGTSTP signal, it does not stop at all.
How can I fix this problem? Thank you.
SIGTSTP for sending stop signal from the terminal to a process. In your case, you ant to send stop signal from parent process. So you'd need SIGSTOP instead of SIGTSTP. So replace SIGTSTP with SIGSTOP.
Also SIGSTOP can't caught. So you don't need to have handlers for SIGSTOP.
So basically I have 4-5 functions in my program. It is a lot of lines of codes and reading and writing from a file and it could end in an infinite loop (worse case) and I would like to terminate my program if it goes beyond lets say 20 seconds. below code is not working, my program hangs and the OS terminates it for me instead of the program terminating itself. I think the main problem I am having is the alarm is set in the main and when the alarm time limit is reached the process is executing in another function and this is causing the program to shut without closing files and killing child processes. This is what I have for now:
volatile sig_atomic_t keep_going = 1;
/* The signal handler just clears the flag and re-enables itself. */
void
catch_alarm (int sig)
{
printf("Alarm went off");
exit(EXIT_SUCCESS);
}
void function1
{}
void forkingfunction()
{
or(i=0;i<size;i++,temp++)
{
pid_t pID = vfork();
if (pID == 0) // child
{
printf("\nchild pid %d\n",getpid());
//open some files and read and write
function1();
exit(EXIT_SUCCESS);
kill(pID,SIGKILL);
}
}
else if (pID < 0) // failed to fork
{
perror("Failed to fork:");
}
}
void function2
{
function1();
}
int main()
{
int options
while(options){
switch (options)
{
case 1:
case 2:
}
}
signal (SIGALRM, catch_alarm);
alarm (0.1);//testing for 0.1 seconds
function1();
return 0;
}
there is only a certain set of function which can be executed safely from a signal handler. And exit is not one of them. Neither is printf.
You might able to use the _exit() function instead (with underscore in front). However it will only exit the top-most process, leaving the children running.
You can still kill everything using kill(0, signal), as here.
void catch_alarm (int sig) {
kill(0, SIGTERM);
}
Here is an example of a working poc code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void catch_alarm (int sig) {
kill (0,SIGTERM);
}
void forkingfunction()
{
int i;
for(i=0;i<4;i++,i++) {
pid_t pID = fork();
if (pID == 0) {
sleep(5);
printf("\nchild pid %d\n",getpid());
exit(EXIT_SUCCESS);
}
else if (pID < 0) {
perror("Failed to fork:");
}
}
}
int main()
{
signal (SIGALRM, catch_alarm);
alarm (1);//testing for 0.1 seconds
forkingfunction();
sleep(10);
printf("\nnormal exit\n");
return 0;
}
So after spending more than 24 hrs trying to fix this. The solution was actually simple. Keep a global array of PID of children alive +1 for parent PID. before sending the kill(PID, SIGTERM) we have to mention a handler for SIGTERM which sleeps till the process is killed. Thus a graceful exit.
I'm learning the signal of inter process communication, I made the very simple test code below:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
void sighup();
void sigint();
void sigquit();
int main(int argc, const char *argv[])
{
int child_pid;
if((child_pid = fork()) < 0) exit (1);
if(child_pid == 0) {
sleep(2);
signal(SIGHUP, sighup);
signal(SIGINT, sigint);
signal(SIGQUIT, sigquit);
puts("this is the end of the child process");
} else {
printf("\n Parent: sending SIGHUP signal to child\n\n");
kill(child_pid, SIGHUP);
printf("\n Parent: sending SIGINT signal to child\n\n");
kill(child_pid, SIGINT);
printf("\n Parent: sending SIGQUIT signal to child\n\n");
kill(child_pid, SIGQUIT);
}
}
void sighup() {
signal(SIGHUP, sighup);
printf("CHILD: I have received a SIGHUP\n");
}
void sigint() {
signal(SIGINT, sigint);
printf("CHILD: I have received a SIGINT\n");
}
void sigquit() {
sleep(2);
printf("CHILD: My parent process has killed me!!");
printf("CHILD: cleaning up...\n");
exit(0);
}
It seems like the child process doesn't do anything, even doesn't print the end of the process string. any idea?
Your signal handlers are not being invoked in the child because of a race condition. The parent thread sends the child thread a signal before the child calls signal() that overrides the signal handling behavior.
In this case, the child receives a SIGINT and performs its default behavior, which is to terminate. Thus the child terminates before executing the statements after sleep(2).
I'm trying this example that I took from: http://www.cs.cf.ac.uk/Dave/C/node24.html:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void sighup(); /* routines child will call upon sigtrap */
void sigint();
void sigquit();
main() {
int pid;
/* get child process */
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0) { /* child */
printf("\nI am the new child!\n\n");
signal(SIGHUP,sighup); /* set function calls */
signal(SIGINT,sigint);
signal(SIGQUIT, sigquit);
printf("\nChild going to loop...\n\n");
for(;;); /* loop for ever */
}
else /* parent */
{ /* pid hold id of child */
printf("\nPARENT: sending SIGHUP\n\n");
kill(pid,SIGHUP);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGINT\n\n");
kill(pid,SIGINT);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGQUIT\n\n");
kill(pid,SIGQUIT);
sleep(3);
}
}
void sighup() {
signal(SIGHUP,sighup); /* reset signal */
printf("CHILD: I have received a SIGHUP\n");
}
void sigint() {
signal(SIGINT,sigint); /* reset signal */
printf("CHILD: I have received a SIGINT\n");
}
void sigquit() {
printf("My DADDY has Killed me!!!\n");
exit(0);
}
But I do not see any output from the child process.
Is it the expected behaviour? If so; why?
Thank you very much!
Your code has a major race condition. You do not ensure that the child has finished calling signal before the parent sends the signals. You either need to use some kind of synchronization primitive to make the parent wait for the child to install the handlers, or you need to install the signal handlers before forking so the child inherits them.
Here's the easiest way I know to synchronize processes like this:
Before forking, call pipe(p) to create a pipe.
fork().
In the parent, close(p[1]); (the writing end) and read(p[0], &dummy, 1);
In the child, close(p[0]); and close(p[1]); after installing the signal handlers.
When the parent returns from read, you can be sure the child has setup its signal handlers. You can also close(p[0]); in the parent at this point.
Edit 2: Perhaps a better and easier approach:
Before forking, call sigprocmask to block all signals and save the old signal mask.
In the parent, call sigprocmask again right after forking to restore the original signal mask.
In the child, call sigprocmask right after installing the signal handlers to restore the original signal mask.
You should use getpid() instead of pid().