I'm trying to send a signal around circle of processes for a certain amount of times. my first argument represents the number of processes I wish to create. my second one is just a place holder I am currently initiating to be 0. My third is the number of time I want to pass this signal around. I have designed the processes to have a relationship as such: Parent->child1,
child1->child2, child2->child3.... and so on. I'm just figuring out C and I'm confused to why my code is stopping midway. It runs for an iteration or two and then stalls out. Can someone explain why?
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int startProcess;
int N;
int numOfCycles;
sigset_t killSet;
void myHandler1 () {
if(N >= 2 && numOfCycles > 0) {
printf("N=%d, numOfCycles=%d, Signal caught. PID = %d\n",N,numOfCycles,getpid());
numOfCycles--;
kill((getpid()+1),SIGUSR1);
}
else if(N >= 2 && numOfCycles == 0) {
exit(1);
}
else if(N == 1 && numOfCycles > 0) {
printf("N=%d, numOfCycles=%d, Signal caught. PID = %d\n",N,numOfCycles,getpid());
numOfCycles--;
kill(startProcess,SIGUSR1);
}
else if(N == 1 && numOfCycles == 0) {
exit(1);
}
else {
printf("Cycle Complete\n");
exit(1);
}
}
void main(int arg, char ** argv) {
struct sigaction temp, vitas, arctic;
sigemptyset(&killSet);
sigaddset(&killSet,SIGUSR1);
N = atoi(argv[1]);
pid_t process1;
startProcess = atoi(argv[2]);
numOfCycles = atoi(argv[3]);
temp.sa_handler = myHandler1;
temp.sa_flags = SA_RESTART;
// vitas.sa_handler = myHandler2;
// vitas.sa_flags = SA_NODEFER;
//
// arctic.sa_handler = myHandler3;
// arctic.sa_flags = SA_RESTART;
sigaction(SIGUSR1, &temp, NULL);
if (N > 1 ) {
process1 = fork();
if(process1 == 0) {
if(N > 2) {
printf("I am a child with PID=%d, PPID=%d, N =%d\n",getpid(),getppid(),N);
}
N--;
char narg = N+'0';
char *pnarg = &narg;
if(startProcess == 0) {
char nstartProcess[6];
startProcess=getppid();
sprintf(nstartProcess,"%d",startProcess);
char *pstartProcess = &nstartProcess[0];
execl("circle",argv[0],pnarg,pstartProcess,argv[3],NULL);
}
else{
if(N == 1){
printf("I am the final child with PID=%d, PPID=%d, N =%d, startProcess=%d\n",getpid(),getppid(),N,startProcess);
printf("\nSignal Passing start\n\n");
kill(startProcess, SIGUSR1);
while(1) {
sigsuspend(&killSet);
}
}
else {
execl("circle",argv[0],pnarg,argv[2],argv[3],NULL);
}
}
}
else {
printf("I am a parent with PID=%d, PPID=%d, N =%d, startProcess=%d\n",getpid(),getppid(),N,startProcess);
wait(NULL);
while(1) {
sigsuspend(&killSet);
}
}
}
}
stalls out. Can someone explain why?
The final child is not woken up from sigsuspend(&killSet) because you did sigaddset(&killSet,SIGUSR1); - you seem to have thought you have to add the signal to be waited for to the set, but on the contrary the signals in the given set are blocked from delivery. So just drop the sigaddset call.
To add to #Armali and as the sigset was not the only point to consider, it's seems more simple to provide an implementation with a far more simple handler and which avoid fork+exec, you'll found explanations in the comments. I'm not sur i'have understood what is your intent, from the comments i guess that you are looking to launch N processus with the last one sending cycles time SIGUSR1 to the parent, each child processus sending SIGUSR1 to their parent on receipt. Correct me if i'm wrong.
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int startProcess;
int N;
int numOfCycles;
sigset_t killSet;
// Two counters for the signal handler
int numSignals=0;
int numSignalsTotal=0;
// A simple handler that do nothing else that incrementing values in the process.
// To use a SIGINFO handler : void myHandler1 (int signal, siginfo_t *info, void *ucontext) {
void myHandler1 (int signal) {
numSignals++;
numSignalsTotal++;
}
void main(int arg, char ** argv) {
struct sigaction temp;
// As Armali pointed that, here you should not add SIGUSR1 to the sigset a you want
// to suspend your process and waiting for it.
sigemptyset(&killSet);
N = atoi(argv[1]);
pid_t process1 = 0;
startProcess = atoi(argv[2]);
numOfCycles = atoi(argv[3]);
// A more complete initialization here, you need to include SIGUSR1 here to associate it
// to your handler. It seems you doesn't need specific flag like SA_RESTART for it here,
// as the handler code doesn't require that.
temp.sa_handler = myHandler1;
sigemptyset(&temp.sa_mask);
sigaddset(&temp.sa_mask,SIGUSR1);
temp.sa_flags = 0;
temp.sa_restorer = NULL;
// Always check that your function calls return OK. Always.
if (sigaction(SIGUSR1, &temp, NULL) != 0) {
perror("ERR: Failed to set handler ...");
exit(1);
}
// Instead of a mix of fork + exec, it is more simple to keep one process and fork it for each new process.
// Here, we fork at least once, the parent process will alaways be "PARENT" and have a 0 startProcess,
// the child processes will fork themselves, the parent process is their and the new forked one will be
// the next child.
while (startProcess == 0 || N > 1 ) {
process1 = fork();
// To follow the forks : printf(" Fork pid=%d %s %s %s %s\n", process1, argv[0],argv[1],argv[2],argv[3]);
if(process1 != 0 && startProcess == 0) {
// If fork() gives us a pid and we have startProcess 0, then we are the "PARENT" process.
// The only thing we have to do is waiting for signals from our childs, if there is childs.
printf(" I am a parent with PID=%d, PPID=%d, N =%d, startProcess=%d\n",getpid(),getppid(),N,startProcess);
// If there's no child, we simply quit the loop.
if (N == 1) break;
// For reference, "PARENT" output what is its first child.
printf(" -> child = %d\n" , process1);
// Here, while we have cycles, we wait for signals
while(numOfCycles > 0) {
// If you use a handler, you need to operate synchronously with it, so here we block SIGUSR1 as we will wait for it.
if (sigprocmask(SIG_BLOCK, &temp.sa_mask, NULL) != 0) {
perror("ERR: Failed to block usr1...");
exit(1);
}
// We wait for a SIGUSR1.
sigsuspend(&killSet);
// We have a SIGUSR1, we unblock SIGUSR1 as we will modify numSignals and don't want handler doing that at the same time.
if (sigprocmask(SIG_UNBLOCK, &temp.sa_mask, NULL) != 0) {
perror("ERR: Failed to unblock usr1...");
exit(1);
}
// We output on STDOUT that "PARENT" caught a SIGUSR1, we should have the number of signals received in numSignals
printf(" ===> PARENT : N=%d, numOfCycles=%d, Signal caught. PID = %d, count = %d\n",N,numOfCycles,getpid(), numSignals);
// We substract that from our cycles and reset numSignals
numOfCycles-=numSignals; numSignals=0;
}
// If we are here, no more cycles, so we output on STDOUT a summary of "PARENT" processing
printf(" PARENT : N=%d, PID = %d, total = %d\n",N,getpid(), numSignalsTotal);
// Before leaving, "PARENT" should wait for its potential remaining childs to exit
printf(" PARENT waiting for child to terminate.\n");
if (wait(NULL) == -1) {
perror("ERR: PARENt failed to wait for childs termination...");
exit(1);
}
// We quit the loop, so we exit
break;
} else {
// If only one process (N=1) was asked, nothing more to do for the fork, we quit the loop then exit
if (N == 1 && startProcess == 0) break;
// We get the parent process pid in startProcess, as we are a child process
startProcess = getppid();
// If we are the forked process, we decrement N, it will "our" N as child
if (N > 1 && process1 == 0) {
N--;
}
if(process1 == 0) {
// If we are the forked process, we simply state our identity on STDOUT, the final child should be the one with N=1
if (N > 1) {
printf(" I am a child with PID=%d, PPID=%d, N =%d, startProcess=%d\n",getpid(),getppid(),N,startProcess);
} else {
printf(" I am the final child with PID=%d, PPID=%d, N =%d, startProcess=%d\n",getpid(),getppid(),N,startProcess);
}
if (N == 1) {
// If we are the final child, we simply have to send SIGUSR1 signals to our parent process for the number of cycles,
// after that we simply quit the loop and exit, no more forks are required.
while(numOfCycles > 0) {
printf("\n Cycle %d, first Signal start to %d\n\n", numOfCycles--, startProcess);
if (kill(startProcess, SIGUSR1) != 0) {
perror("ERR: LAST CHILD : Failed to send USR1...");
exit(1);
}
}
break;
}
// Note that if we are NOT the final child, we have to go through the loop to fork a new process for the next child.
} else {
if (N > 1) {
// If we are here, we are the forked process for a child and we are not the final child.
// So we do our payload, waiting for SIGUSR1 signals from our child and sending SIGUSR1 to our parent for the
// required number of cycles.
while(numOfCycles > 0) {
// If you use a handle, you need to operate synchronously with it, so here we block SIGUSR1 as we will wait for it.
if (sigprocmask(SIG_BLOCK, &temp.sa_mask, NULL) != 0) {
perror("ERR: Failed to block usr1...");
exit(1);
}
// We wait for a SIGUSR1.
sigsuspend(&killSet);
// We have a SIGUSR1, we unblock SIGUSR1 as we will modify numSignals and don't want handler doing that at the same time.
if (sigprocmask(SIG_UNBLOCK, &temp.sa_mask, NULL) != 0) {
perror("ERR: Failed to unblock usr1...");
exit(1);
}
// We output on STDOUT that this child caught a SIGUSR1, we should have the number of signals received in numSignals
printf(" ===> CHILD: N=%d, numOfCycles=%d, Signal caught. PID = %d, count = %d\n",N,numOfCycles,getpid(), numSignals);
// For each receveid signals from our child, we send one to our parent
while (numSignals > 0) {
printf("\n Signal Passing start to %d\n\n", startProcess);
if (kill(startProcess, SIGUSR1) != 0) {
perror("ERR: CHILD : Failed to send USR1...");
exit(1);
}
// We decrement accordingly our number of cycles and signals
numOfCycles--;
numSignals--;
}
}
// If we are here, no more cycles, so we output on STDOUT a summary of the child processing
printf(" CHILD : N=%d, PID = %d, total = %d\n",N,getpid(), numSignalsTotal);
// We quit the loop, so we exit, then terminate our child
break;
}
}
}
}
// We should always restore signal handler to default when we exit.
temp.sa_handler = SIG_DFL;
sigemptyset(&temp.sa_mask);
sigaddset(&temp.sa_mask,SIGUSR1);
temp.sa_flags = 0;
temp.sa_restorer = NULL;
// Always check that your function calls return OK. Always.
if (sigaction(SIGUSR1, &temp, NULL) != 0) {
perror("ERR: Failed to restore SIGUSR1 ...");
exit(1);
}
// We output on STDOUT the related PID at each process termination.
printf(" End of PID %d\n",getpid());
}
I'm doing a homework assigment (the regular "write your own unix shell in c" assigment)
and cant make my child process run in the background properly, the are KILLED right before calling execvp
my intuition tells me the problem is in the signal handler or the usage of return in the parent process after fork().
This is how I've implemented it (just for the background process) :
void ExeExternal(char *args[MAX], char* cmd,ExecutionMode ExeMode) {
int pID;
switch(pID = fork())
{
case -1:
// Add your code here (error)
/*
your code
*/
perror("fail to fork in : ExeExternal(...)\n");
exit(-1);
case 0 :
// Child Process
signal(SIGTSTP, SIG_DFL);
signal(SIGINT, SIG_DFL);
signal(SIGCONT, SIG_DFL);
usleep(20000);
setpgrp();
printf("trying to execvp\n");
int run_result=execvp(args[0],args);
if(run_result==-1)
{
perror("execvp(...) result in execvp\n");
exit(-1);
}
break; //wont reach this part ever!
default://the parent. pID holds child's pID
setpgid(pID, pID); //note, this is also done in the child
if(ExeMode==BACKGROUND) //no need to WAIT()
{
return;
}
}
}
and this is my signal handler (registered in my main() with signal(SIGCHLD,handle_sigchld)
void handle_sigchld(int sig) {
pid_t pid;
int status;
pid=waitpid(WAIT_ANY,&status,WUNTRACED | WNOHANG);
if (pid >0)
{
if(WIFEXITED(status)) //if terminated normally, return or exit
{
printf("\n[%d] %d Done \n", ID, pid); //[1] a.out : 12340 214 secs
}
else if (WIFSIGNALED(status)) //killed with SIGINT
{
printf("handle_sigchld:[%d] pid %d KILLED\n",ID,pid);
}
else if (WIFSTOPPED(status)) //suspended with SIGCONT
{
printf("\n[%d] %s %d %d secs (stopped)\n", ID, value,pid,time);
}
}
}
I've tried to plant some prinft's and my child seems to be killed right before executing execvp(...)
Ive tried to print the args[0] and args[1] and it exited there, so it looks like it falls upon accessing them,
also - is that the right way to use return ? ive tried to replace it with waitpid(pid,status,WNOHANG) but it didnt helped
ANY help is greatly appreciated! even just a hint in the right direction
I've been asked to develop the consumer (client) side to a producer (server), where the producer creates processes, waits until the consumer has read shared memory and deleted processes, then passes control back to the producer for the killing of processes and the shutting down of the shared memory block.
I've researched the difference between sleep and wait, and realise that as soon as fork() is called, the child process begins running.
The below code is after the creation of processes and checks if they're parent processes. If they are, they wait(0). *Now for my question, how do I know where the code in the consumer starts to be executed, and how do I pass it back? *
else if(pid > 0)
{
wait(0);
}
Below can be seen the main loop the producer uses.
int noToCreate = atoi(argv[2]); // (user inputs on cmd line "./prod 20 10 5" - 20 size of shared mem, 10 process to be created, 5 processes to be deleted)
while(*memSig != 2)
{
while(*memSig == 1) // set memsignature to sleep while..
{
sleep(1);
}
for(B = 0; B < noToCreate; B++)
{
pid = fork();
if(pid == -1)
{
perror("Error forking");
exit(1);
}
else if(pid > 0)
{
wait(0);
}
else
{
srand(getpid());
while(x == 0)
{
if(*randNum == 101)
{
*randNum = rand() % (100 -
1) + 1;
*pidNum = getpid();
printf("priority: %d
Process ID: %d \n", *randNum, *pidNum);
x = 1;
}
else
{
*randNum++;
*pidNum++;
}
}
exit(0);
}
} /* Closes main for loop */
if(*memSig == 0)
{
*memSig = 1;
}
} /* Closes main while loop */
Thanks a bunch guys :)
wait make parent blocked until any child end .You can use waitpid let parent wait specific child.
When a child process end, it will set a signal SIG_CHILD.
The pid is zero for the child process after the fork, so you are in the child process at your call to the srand function.
The other pid is that for the child process which allows he original thread to wait for the child to finish. If you wish to pass data between the processes consider using a pipe. A popen call returns two file descriptors, one to write end and the other to the read end. Set this up before the fork and the two processes can communicate.
wait makes the parent wait for any child to terminate before going on (preferably use waitpid to wait for a certain child), whereas sleep puts the process to sleep and resumes it, as soon as the time passed as argument is over.
Both calls will make the process block.
And it is NOT said that the child will run immediately, this is indeterminate behavior!
If you want to pass data between producer and consumer, use pipes or *NIX sockets, or use the return-value of exit from the child if a single integer is sufficient.
See man wait, you can get the return value of the child with the macro WEXITSTATUS.
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int
main(int argc, char *argv[])
{
pid_t cpid, w;
int status;
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Code executed by child */
printf("Child PID is %ld\n", (long) getpid());
if (argc == 1)
pause(); /* Wait for signals */
_exit(atoi(argv[1]));
} else { /* Code executed by parent */
do {
w = waitpid(cpid, &status, WUNTRACED | WCONTINUED);
if (w == -1) {
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");
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
exit(EXIT_SUCCESS);
}
}
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);
}