Processes synchronization with message queues and signals - c
I have to create three processes:
Reading expression like 1+3+5+12
Checking if expression has correct syntax
Adding numbers and displaying them
Data between processes is shared using pipes mechanism. Processes synchronization uses message queues and signals.I also should be able to manually send signals to each process through console.
The problem I am running into this is that all processes seem to run randomly. Why is that, what's wrong here? It should work...
This is the whole code that compiles correctly:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#define SIZEBUFF 256
// Signal handling for each process
void h_sig1(int signo);
void h_sig2(int signo);
void h_sig3(int signo);
void h_S4(int signo);
// Processes functions
void process1(void);
void process2(void);
void process3(void);
// helper functions
bool isInteger(double val);
static pid_t P1, P2, P3; // PIDs for each process
int P; // parent PID
int pfd12[2], pfd23[2]; // Pipes between processes 1-2 and 2-3
int providePIDY1[2], providePIDY2[2]; // provide all PIDs to 1/2 process
// message in message queue
typedef struct
{
long type;
int sigNum;
} message;
int queue_ID;
int main(void)
{
P = getpid();
// Message queue created
if ((queue_ID = msgget(IPC_PRIVATE,IPC_CREAT|0666)) < 0)
{
printf("msgget\n");
return 1;
}
if((P1 = fork()) == 0)
{
P1 = getpid();
process1();
}
else if((P2 = fork()) == 0)
{
P2 = getpid();
process2();
}
else if((P3 = fork()) == 0)
{
P3 = getpid();
process3();
}
else
{ // Sending signals from parent through operator
// Sending PIDs to process 1
close(providePIDY1[0]);
write(providePIDY1[1], &P, sizeof(int));
write(providePIDY1[1], &P1, sizeof(int));
write(providePIDY1[1], &P2, sizeof(int));
write(providePIDY1[1], &P3, sizeof(int));
close(providePIDY1[1]);
// Sending PIDs to process 2
close(providePIDY2[0]);
write(providePIDY2[1], &P, sizeof(int));
write(providePIDY2[1], &P1, sizeof(int));
write(providePIDY2[1], &P2, sizeof(int));
write(providePIDY2[1], &P3, sizeof(int));
close(providePIDY2[1]);
printf("\nProgram options:\n");
printf("Send signal - 's'(send)\n");
printf("Display processes PIDs 'p'(pids)\n");
printf("Quit program - 'q'(quit)\n");
char choice, choice2, choice3;
while(1)
{
choice = getchar();
if(choice == 's')
{
printf("Which process is receiving the signal - 1, 2, or 3?: ");
choice2 = getchar();
choice2 = getchar();
printf("\n");
if((choice2 < 1) && (choice2 > 3))
{
printf("No such process!");
continue;
}
printf("What signal to send?:\n");
printf("1-S1(end execution)\n2-S2(pause execution)\n3-S3(renew execution)?\n ");
printf("Choice: ");
choice3 = getchar();
choice3 = getchar();
switch(choice2)
{
case '1': //nie można przechwycić sygnałów SIGKILL oraz SIGSTOP (zabicia oraz zatrzymania)
if(choice3 == '1') { kill(0,SIGCONT); kill(P1,SIGUSR1); }
if(choice3 == '2') kill(P1,SIGTSTP);
if(choice3 == '3') { kill(0,SIGCONT); kill(P3,SIGALRM); }
break;
case '2':
if(choice3 == '1') { kill(0,SIGCONT); kill(P2,SIGUSR1); }
if(choice3 == '2') kill(P2,SIGTSTP);
if(choice3 == '3') { kill(0,SIGCONT); kill(P3,SIGALRM); }
break;
case '3':
if(choice3 == '1') { kill(0,SIGCONT); kill(P3,SIGUSR1); }
if(choice3 == '2') kill(P3,SIGTSTP);
if(choice3 == '3') { kill(0,SIGCONT); kill(P3,SIGALRM); }
break;
default: printf("No such operation!!! \n\n");
}
}
if(choice == 'p')
{
// do something
}
if(choice == 'q')
{
// do something
}
}
}
}
void process1(void)
{
// Receiving PIDs
close(providePIDY1[1]);
read(providePIDY1[0], &P, sizeof(int));
read(providePIDY1[0], &P1, sizeof(int));
read(providePIDY1[0], &P2, sizeof(int));
read(providePIDY1[0], &P3, sizeof(int));
close(providePIDY1[0]);
struct sigaction act1;
act1.sa_handler = h_sig1;
sigemptyset(&act1.sa_mask);
act1.sa_flags = 0;
sigaction(SIGUSR1, &act1, 0);
sigaction(SIGTSTP, &act1, 0);
sigaction(SIGALRM, &act1, 0);
struct sigaction act;
act.sa_handler = h_S4;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);
// do something
}
void process2(void)
{
close(providePIDY2[1]);
read(providePIDY2[0], &P, sizeof(int));
read(providePIDY2[0], &P1, sizeof(int));
read(providePIDY2[0], &P2, sizeof(int));
read(providePIDY2[0], &P3, sizeof(int));
close(providePIDY2[0]);
struct sigaction act2;
act2.sa_handler = h_sig2;
sigemptyset(&act2.sa_mask);
act2.sa_flags = 0;
sigaction(SIGUSR1, &act2, 0);
sigaction(SIGTSTP, &act2, 0);
sigaction(SIGALRM, &act2, 0);
struct sigaction act;
act.sa_handler = h_S4;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);
// do something
}
void process3(void)
{
struct sigaction act3;
act3.sa_handler = h_sig3;
sigemptyset(&act3.sa_mask);
act3.sa_flags = 0;
sigaction(SIGUSR1, &act3, 0);
sigaction(SIGTSTP, &act3, 0);
sigaction(SIGALRM, &act3, 0);
struct sigaction act;
act.sa_handler = h_S4;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);
// do something
}
void h_sig1(int signo)
{
message msg;
msg.type = P2;
msg.sigNum = signo;
kill(P2, SIGINT);
// send type of receiving signal to message queue
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
msg.type = P3;
kill(P3, SIGINT);
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
if(signo == SIGUSR1)
{
}
if(signo == SIGTSTP)
{
}
if(signo == SIGALRM)
{
}
}
void h_sig2(int signo)
{
message msg;
msg.type = P1;
msg.sigNum = signo;
kill(P1, SIGINT);
// send type of receiving signal to message queue
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
msg.type = P3;
kill(P3, SIGINT);
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
if(signo == SIGUSR1)
{
}
if(signo == SIGTSTP)
{
}
if(signo == SIGALRM)
{
}
}
void h_sig3(int signo)
{
message msg;
msg.type = P1;
msg.sigNum = signo;
kill(P1, SIGINT);
// send type of receiving signal to message queue
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
msg.type = P2;
kill(P2, SIGINT);
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
if(signo == SIGUSR1)
{
}
if(signo == SIGTSTP)
{
}
if(signo == SIGALRM)
{
}
}
void h_S4(int signo)
{
int res;
message msg;
printf("\nProcess with PID=%d received signal S4", getpid());
if(signo == SIGINT)
{
res = msgrcv(queue_ID, &msg, sizeof(msg.sigNum), msg.type, 0);
if(res >= 0)
{
if(msg.sigNum == SIGUSR1)
{
}
if(msg.sigNum == SIGTSTP)
{
}
if(msg.sigNum == SIGALRM)
{
}
}
}
}
Long version, to compile:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#define SIZEBUFF 200
// Signal handling for each process
void h_sig1(int signo);
void h_sig2(int signo);
void h_sig3(int signo);
void h_S4(int signo); // signal handling for the 4th signal
// Processes functions
void process1(void);
void process2(void);
void process3(void);
// helper functions
bool isInteger(double val);
static pid_t P1, P2, P3; // PIDs for each process
int P; // parent PID
int pfd12[2], pfd23[2]; // Pipes between processes 1-2 and 2-3
int providePIDY1[2], providePIDY2[2]; // provide all PIDs to 1/2 process
// message in message queue
typedef struct
{
long type;
int sigNum;
} message;
int queue_ID;
int main(void)
{
P = getpid();
if (pipe(pfd12) == -1)
{
perror("pipe failed");
exit(1);
}
if (pipe(pfd23) == -1)
{
perror("pipe failed");
exit(1);
}
// Message queue created
if ((queue_ID = msgget(IPC_PRIVATE,IPC_CREAT|0666)) < 0)
{
printf("msgget\n");
return 1;
}
if (pipe(providePIDY1) == -1)
{
perror("pipe failed");
exit(1);
}
if (pipe(providePIDY2) == -1)
{
perror("pipe failed");
exit(1);
}
if((P1 = fork()) == 0)
{
P1 = getpid();
process1();
}
else if(P1 < 0)
{
perror("fork failed");
exit(2);
}
else if((P2 = fork()) == 0)
{
P2 = getpid();
process2();
}
else if(P2 < 0)
{
perror("fork failed");
exit(2);
}
else if((P3 = fork()) == 0)
{
P3 = getpid();
process3();
}
else if(P3 < 0)
{
perror("fork failed");
exit(2);
}
else
{ // Sending signals from parent through operator
// Sending PIDs to process 1
close(providePIDY1[0]);
write(providePIDY1[1], &P, sizeof(int));
write(providePIDY1[1], &P1, sizeof(int));
write(providePIDY1[1], &P2, sizeof(int));
write(providePIDY1[1], &P3, sizeof(int));
close(providePIDY1[1]);
// Sending PIDs to process 2
close(providePIDY2[0]);
write(providePIDY2[1], &P, sizeof(int));
write(providePIDY2[1], &P1, sizeof(int));
write(providePIDY2[1], &P2, sizeof(int));
write(providePIDY2[1], &P3, sizeof(int));
close(providePIDY2[1]);
printf("\nProgram options:\n");
printf("Send signal - 's'(send)\n");
printf("Display processes PIDs 'p'(pids)\n");
printf("Quit program - 'q'(quit)\n");
char choice, choice2, choice3;
while(1)
{
choice = getchar();
if(choice == 's')
{
printf("Which process is receiving the signal - 1, 2, or 3?: ");
choice2 = getchar();
choice2 = getchar();
printf("\n");
if((choice2 < 1) && (choice2 > 3))
{
printf("No such process!");
continue;
}
printf("What signal to send?:\n");
printf("1-S1(end execution)\n2-S2(pause execution)\n3-S3(renew execution)?\n ");
printf("Choice: ");
choice3 = getchar();
choice3 = getchar();
switch(choice2)
{
case '1': //nie można przechwycić sygnałów SIGKILL oraz SIGSTOP (zabicia oraz zatrzymania)
if(choice3 == '1') { kill(0,SIGCONT); kill(P1,SIGUSR1); }
if(choice3 == '2') kill(P1,SIGTSTP);
if(choice3 == '3') { kill(0,SIGCONT); kill(P3,SIGALRM); }
break;
case '2':
if(choice3 == '1') { kill(0,SIGCONT); kill(P2,SIGUSR1); }
if(choice3 == '2') kill(P2,SIGTSTP);
if(choice3 == '3') { kill(0,SIGCONT); kill(P3,SIGALRM); }
break;
case '3':
if(choice3 == '1') { kill(0,SIGCONT); kill(P3,SIGUSR1); }
if(choice3 == '2') kill(P3,SIGTSTP);
if(choice3 == '3') { kill(0,SIGCONT); kill(P3,SIGALRM); }
break;
default: printf("No such operation!!! \n\n");
}
}
if(choice == 'p')
{
printf("\n<Processes PIDs:>\n");
printf("P(initial process)=%d\n",P);
printf("P1(process 1)=%d\n",P1);
printf("P2(process 2)=%d\n",P2);
printf("P3(process 3)=%d\n\n",P3);
}
if(choice == 'q')
{
printf("\n<Quitting program>\n");
msgctl(queue_ID, IPC_RMID, 0);
kill(0, SIGKILL);
}
}
}
}
void process1(void)
{
int dataSize;
char buff[SIZEBUFF];
// Receiving PIDs
close(providePIDY1[1]);
read(providePIDY1[0], &P, sizeof(int));
read(providePIDY1[0], &P1, sizeof(int));
read(providePIDY1[0], &P2, sizeof(int));
read(providePIDY1[0], &P3, sizeof(int));
close(providePIDY1[0]);
printf("\n<Process 1 execution in progress>\n");
/*SIGACTION*/
struct sigaction act1;
act1.sa_handler = h_sig1;
sigemptyset(&act1.sa_mask);
act1.sa_flags = 0;
sigaction(SIGUSR1, &act1, 0);
sigaction(SIGTSTP, &act1, 0);
sigaction(SIGALRM, &act1, 0);
struct sigaction act;
act.sa_handler = h_S4;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);
close(pfd12[0]);
while(1)
{
printf("Provide expr: ");
scanf("%s", buff);
if(strcmp(buff, "0") == 0)
break;
dataSize = strlen(buff) + 1; // plus NULL
if(dataSize > 0)
write(pfd12[1], &dataSize, sizeof(int));
write(pfd12[1], &buff, sizeof(char)*dataSize);
}
dataSize = 0; // info that there's no more data
write(pfd12[1], &dataSize, sizeof(int));
close(pfd12[1]);
printf("\n---Process 1 finished execution---\n");
exit(0);
}
void process2(void)
{
int dataSize;
char buff[SIZEBUFF];
char *token, *end;
int number;
const char delim[2] = "+";
//Odebranie pidow
close(providePIDY2[1]);
read(providePIDY2[0], &P, sizeof(int));
read(providePIDY2[0], &P1, sizeof(int));
read(providePIDY2[0], &P2, sizeof(int));
read(providePIDY2[0], &P3, sizeof(int));
close(providePIDY2[0]);
printf("\n<Process 2 execution in progress>\n");
/*SIGACTION*/
struct sigaction act2;
act2.sa_handler = h_sig2;
sigemptyset(&act2.sa_mask);
act2.sa_flags = 0;
sigaction(SIGUSR1, &act2, 0);
sigaction(SIGTSTP, &act2, 0);
sigaction(SIGALRM, &act2, 0);
struct sigaction act;
act.sa_handler = h_S4;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);
close(pfd12[1]);
read(pfd12[0], &dataSize, sizeof(int));
if(dataSize > 0)
{
sleep(3);
read(pfd12[0], buff, dataSize);
token = strtok(buff, delim);
while( token != NULL )
{
number = strtol(token, &end, 0);
if(!isInteger(number))
break;
}
}
close(pfd12[0]);
// Sending result to process 3
close(pfd23[0]);
write(pfd23[1], &buff, sizeof(int));
close(pfd23[1]);
printf("\n---Process 2 finished execution---\n");
}
void process3(void)
{
int sum = 0;
char buff[SIZEBUFF];
char* token, *end;
int number;
const char delim[2] = "+";
/*SIGACTION*/
struct sigaction act3;
act3.sa_handler = h_sig3;
sigemptyset(&act3.sa_mask);
act3.sa_flags = 0;
sigaction(SIGUSR1, &act3, 0);
sigaction(SIGTSTP, &act3, 0);
sigaction(SIGALRM, &act3, 0);
struct sigaction act;
act.sa_handler = h_S4;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);
printf("\n<Process 3 execution in progress>");
close(pfd23[1]);
read(pfd23[0], &buff, sizeof(int));
token = strtok(buff, delim);
while( token != NULL )
{
number = strtol(token, &end, 0);
sum += number;
}
printf("Sum = %d\n", sum);
close(pfd23[0]);
printf("\n---Process 3 finished execution ---\n");
printf("\n---Program finished execution---\n");
kill(getppid(),SIGKILL);
}
/*******************************************************************************************************/
/****************************************SIGNAL HANDLING (S1-S3)***********************************************/
/*******************************************************************************************************/
void h_sig1(int signo)
{
message msg;
msg.type = P2;
msg.sigNum = signo;
kill(P2, SIGINT);
// send type of receiving signal to message queue
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
msg.type = P3;
kill(P3, SIGINT);
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
if(signo == SIGUSR1)
{
printf("\nProcess 1 received signal S1\n");
printf("Terminating parent process!\n");
kill(getppid(), SIGKILL);
printf("Terminating process 1!\n");
kill(getpid(), SIGKILL);
}
if(signo == SIGTSTP)
{
printf("\nProcess 1 received signal S2\n");
printf("Pausing process 1!\n");
kill(getpid(), SIGSTOP);
}
if(signo == SIGALRM)
{
printf("\nProcess 1 received signal S3\n");
printf("Renewing execution of process 1!\n");
kill(getpid(), SIGCONT);
}
}
void h_sig2(int signo)
{
message msg;
msg.type = P1;
msg.sigNum = signo;
kill(P1, SIGINT);
// send type of receiving signal to message queue
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
msg.type = P3;
kill(P3, SIGINT);
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
if(signo == SIGUSR1)
{
printf("\nProcess 2 received signal S1\n");
printf("Terminating parent process!\n");
kill(getppid(), SIGKILL);
printf("Terminating process 2!\n");
kill(getpid(), SIGKILL);
}
if(signo == SIGTSTP)
{
printf("\nProcess 2 received signal S2\n");
printf("Pausing process 2!\n");
kill(getpid(), SIGSTOP);
}
if(signo == SIGALRM)
{
printf("\nProcess 2 received signal S3\n");
printf("Renewing execution of process 2!\n");
kill(getpid(), SIGCONT);
}
}
void h_sig3(int signo)
{
message msg;
msg.type = P1;
msg.sigNum = signo;
kill(P1, SIGINT);
// send type of receiving signal to message queue
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
msg.type = P2;
kill(P2, SIGINT);
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
if(signo == SIGUSR1)
{
printf("\nProcess 3 received signal S1\n");
printf("Terminating parent process!\n");
kill(getppid(), SIGKILL);
printf("Terminating process 3!\n");
kill(getpid(), SIGKILL);
}
if(signo == SIGTSTP)
{
printf("\nProcess 3 received signal S2\n");
printf("Pausing process 3!\n");
kill(getpid(), SIGSTOP);
}
if(signo == SIGALRM)
{
printf("\nProcess 3 received signal S3\n");
printf("Renewing execution of process 3!\n");
kill(getpid(), SIGCONT);
}
}
/*******************************************************************************************************/
/****************************************Handling S4 signal***********************************/
/*******************************************************************************************************/
void h_S4(int signo)
{
int res;
message msg;
printf("\nProcess with PID=%d received signal S4", getpid());
if(signo == SIGINT)
{
res = msgrcv(queue_ID, &msg, sizeof(msg.sigNum), msg.type, 0);
if(res >= 0)
{
if(msg.sigNum == SIGUSR1)
{
printf("Terminating process\n");
kill(getpid(),SIGKILL);
}
if(msg.sigNum == SIGTSTP)
{
printf("Pausing process\n");
kill(getpid(),SIGSTOP);
}
if(msg.sigNum == SIGALRM)
{
printf("Renewing process\n");
kill(getpid(),SIGCONT);
}
}
}
}
bool isInteger(double val)
{
int truncated = (int)val;
return (val == truncated);
}
Source code analysis
There are consistency problems:
static pid_t P1, P2, P3; // PIDs for each process
int P; // parent PID
Why is P and int instead of a pid_t? Why is it global instead of static? Why is it P instead of P0? Should the whole lot be an array?
static pid_t P[4];
(There would be benefits to using an array!)
There is repetition that should be in a function invoked multiple times:
// Sending PIDs to process 1
close(providePIDY1[0]);
write(providePIDY1[1], &P, sizeof(int));
write(providePIDY1[1], &P1, sizeof(int));
write(providePIDY1[1], &P2, sizeof(int));
write(providePIDY1[1], &P3, sizeof(int));
close(providePIDY1[1]);
// Sending PIDs to process 2
close(providePIDY2[0]);
write(providePIDY2[1], &P, sizeof(int));
write(providePIDY2[1], &P1, sizeof(int));
write(providePIDY2[1], &P2, sizeof(int));
write(providePIDY2[1], &P3, sizeof(int));
close(providePIDY2[1]);
Note that there's also repetition because the P values aren't an array. There's also a possible portability liability; pid_t does not have to be the same size as int.
There are problems with checking inputs:
choice = getchar();
if(choice == 's')
Since choice is a char, you can get erroneous handling of EOF — if you bothered to test for it. You also leave a newline (at least) in the input, and don't skip leading spaces in the input. You'd likely do better with reading a line of data (fgets() or POSIX
readline()) and then using if (sscanf(buffer, " %c", &choice) != 1) { …handle error… } to get the character.
Your next input block is curious:
printf("Which process is receiving the signal - 1, 2, or 3?: ");
choice2 = getchar();
choice2 = getchar();
printf("\n");
if((choice2 < 1) && (choice2 > 3))
The first input reads the newline (assuming there were no trailing spaces, etc), and the second gets a '1', '2', or '3'. However, you test whether the input value is both less than 1 and greater than 3, and there's no known value in the universe for which both conditions are true (NaN values are unknown values). You really wanted something like:
if (choice2 < '1' || choice2 > '3')
After you've determined which signal to send (more or less), you have another block of repeated code because you used P1 etc instead of an array P.
There are chunks of repeated code in your child processes, such as the code that reads the process numbers. These should be in a function, too. The signal handling setup should probably be in a function too, though I've not spent a lot of time checking for the differences and similarities between the different process functions.
Major problem
You say the code is supposed to be processing arithmetic expressions. You have the main program loop reading choices about signal handling, but you seem to have process1() also trying to read expressions. This is bad news; it is indeterminate which of the processes will get to read any given input.
Back to the small stuff
You have:
dataSize = strlen(buff) + 1; // plus NULL
if(dataSize > 0)
write(pfd12[1], &dataSize, sizeof(int));
write(pfd12[1], &buff, sizeof(char)*dataSize);
The test is a little pointless; the minimum value that strlen() can return is 0, so the minimum value in dataSize is 1, so the condition will always be true. (Theoretically, I suppose, you could enter so much data that the size_t returned by strlen() overflows the int dataSize, but you've not allocated enough space for that to be an actual problem — your code will have had other problems before that.)
In process2(), this code is curious:
token = strtok(buff, delim);
while( token != NULL )
{
number = strtol(token, &end, 0);
if(!isInteger(number))
break;
}
There are no circumstances under which the int number; is going to be a non-integer when you scan the string with strtol(). You have a risk of overflow (sizeof(int) != sizeof(long) on 64-bit Unix systems, for example). You have a risk of not being able to interpret the remnants of floating point value (because the . is not a valid part of an integer). You'll need to rework that code.
There's a lot of repetition in the signal handling code; it should be refactored so that you need fewer functions. It'll be easier to understand in the long run. Copy'n'paste'n'edit is a very bad way of programming when the result is near clones of the code in a single program.
I'm not clear what the differences are between the two versions you show; I've not scrutinized them. You should look at how to create an MCVE (How to create a Minimal, Complete, and Verifiable Example?) or SSCCE (Short, Self-Contained, Correct Example) — two names and links for the same basic idea. I'm not sure that either lot of code qualifies as an MCVE; both versions is overkill. Just supply the compilable code.
After compilation
I've compiled the second chunk of code (saved in a file called procsync.c) on my Mac running Mac OS X 10.10.3 with GCC 5.1.0, and using the command line:
$ gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
> -Wold-style-definition -Werror procsync.c -o procsync
$
To my considerable surprise, the code compiled under those very stringent options with no complaints — that is something I very seldom see in code on SO.
Congratulations!
(But there are still the other issues to worry about.)
Related
Two child processes to trigger using epoll and signals
process 1 is by timerfd, process 2 is triggered by signal from process1 tc2_main is equal to main I tried having child1 triggered by timerfd and epoll, child 2 triggered by child 1's epoll and signal, so that child 2 gets triggered every 2 seconds. but child 2 is never triggered process timer is wait timer and send signal to child 2 void process_timer(int efd, int timerfd, int pid) { uint64_t exp; int ret, nfds; struct epoll_event event; for(int i = 0; i < 2; i++){ nfds = epoll_wait(efd, &event, 1, -1); if (nfds == -1) handle_error("epoll_wait"); ret = read(timerfd, &exp, sizeof(uint64_t)); if (ret != sizeof(uint64_t)) handle_error("read"); kill(pid, SIGUSR1); printf("Process %d: Timer expired %lu times\n", getpid(), exp); fflush(stdout); } } this code is set epoll, timer and signal and also set child 2 child 2 is wait SIGUSR1 signal void tc2_main() { int efd, timerfd; struct epoll_event event; struct itimerspec new_value; cnt = (int*)mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1 ,0); struct sched_param param; param.sched_priority = 99; if (sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) { perror("sched_setscheduler"); } if (efd == -1) handle_error("epoll_create1"); nice(10); timerfd = timerfd_create(CLOCK_MONOTONIC, 0); if (timerfd == -1) handle_error("timerfd_create"); new_value.it_value.tv_sec = 2; new_value.it_value.tv_nsec = 0; new_value.it_interval.tv_sec = 2; new_value.it_interval.tv_nsec = 0; sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGUSR1); sigprocmask (SIG_BLOCK, &sigset, NULL); int sfd = signalfd(-1, &sigset, 0); printf("sigfd : %d\n", sfd); int ep_sig_fd = epoll_create1(0); struct epoll_event sig_ev,ev; sig_ev.data.fd = sfd; sig_ev.events = EPOLLINT; if(epoll_ctl(ep_sig_fd, EPOLL_CTL_ADD, sfd, &sig_ev) == -1){ handle_error("epoll_ctl"); } printf("Child process waiting for signal...\n"); if (timerfd_settime(timerfd, 0, &new_value, NULL) == -1) handle_error("timerfd_settime"); efd = epoll_create1(0); if (efd == -1) handle_error("epoll_create1"); event.data.fd = timerfd; event.events = EPOLLIN EPOLLET; if (epoll_ctl(efd, EPOLL_CTL_ADD, timerfd, &event) == -1) handle_error("epoll_ctl"); pid_t pid = fork(); if (pid == -1) handle_error("fork"); else if (pid == 0) { process_timer(efd, timerfd, getppid()); (*cnt)++; exit(EXIT_SUCCESS); } else { pid_t pid2 = fork(); fflush(stdout); if(pid2 == -1) { handle_error("fork"); } else if(pid2 == 0) { struct signalfd_siginfo siginfo; printf("Child 2: waiting for signal...\n"); int s = epoll_wait(ep_sig_fd, &sig_ev, 1, -1); printf("aaa\n"); fflush(stdout); if(s < 0) handle_error("epoll wait"); if (read(sfd, &siginfo, sizeof(siginfo)) != sizeof(siginfo)) { perror("read"); exit(EXIT_FAILURE); } } else { wait(NULL); } } exit(EXIT_SUCCESS); }
client - server program for transmitting text using signals SIGUSR1, SIGUSR2
server typedef struct s_server { unsigned char c; int counter; } t_server; t_server server; void ft_one(int sig, siginfo_t *info, void *context) { (void)sig; (void)context; server.c += server.counter; server.counter /= 2; if (server.counter == 0) { write(1, &server.c, 1); server.c = 0; server.counter = 128; } kill(info->si_pid, SIGUSR1); } void ft_zero(int sig, siginfo_t *info, void *context) { (void)sig; (void)context; server.counter /= 2; if (server.counter == 0) { write(1, &server.c, 1); server.c = 0; server.counter = 128; } kill(info->si_pid, SIGUSR1); } int main(void) { struct sigaction act_one; struct sigaction act_zero; memset(&act_one, '\0', sizeof(act_one)); memset(&act_zero, '\0', sizeof(act_zero)); act_one.__sigaction_u.__sa_sigaction = ft_one; act_zero.__sigaction_u.__sa_sigaction = ft_zero; act_one.sa_flags = SA_SIGINFO; act_zero.sa_flags = SA_SIGINFO; if (sigaction(SIGUSR1, &act_one, NULL) < 0) return (0); if (sigaction(SIGUSR2, &act_zero, NULL) < 0) return (0); printf("server pid: %d\n", getpid()); server.c = 0; server.counter = 128; while (1) pause(); return (0); } client void empty(int sig, siginfo_t *info, void *context) { (void)sig; (void)context; (void)info; } int main(int argc, char **argv) { int i; struct sigaction act; char *str; int serv_pid; memset(&act, '\0', sizeof(act)); act.__sigaction_u.__sa_sigaction = empty; act.sa_flags = SA_SIGINFO; serv_pid = atoi(argv[1]); str = argv[2]; if (sigaction(SIGUSR1, &act, NULL) < 0) return (0); while (*str) { i = 128; while (i > 0) { if (i & (unsigned char)*str) { if (kill(serv_pid, SIGUSR1) == -1) return (0); } else { if (kill(serv_pid, SIGUSR2) == -1) return (0); } i /= 2; pause(); } str++; } return (0); } The screenshots show the result of work, programs. In the first case, I call the client several times. In the second with a lot of text. In both cases, apparently, the response signal from the server does not go away. Why? I can t understand enter image description here. enter image description here
You have a race condition in the client program. There is no guarantee that the signal will be delivered after the client calls pause. The correct way is to use sigprocmask and sigsuspend. Block incoming SIGUSR1 with sigprocmask. After sending the bit, instead of calling pause, call sigsuspend with a mask that unblocks SIGUSR1. sigsuspend will return when the signal is caught, and block again. sigset_t myset, oldset; sigemptyset(&myset); sigaddset (&myset, SIGUSR1); sigprocmask(SIG_BLOCK, &myset, &oldset); while (*str) { ... // pause() -- wrong! race condition! sigsuspend(&oldset); ... }
Child process not being generated and program seems to get caught in infinite loop
I am experimenting with signals forking but I am not sure what is going on with my code. I should get 3 total processes. 1 parent 1 child and 1 grandchild. I decrement the level variable by 1 each time I make a fork so the program should terminate. I don't understand why the program isn't generating any child processes. When I do ps aux| grep selfCaller I only ever see 1 process being run at a time. #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <signal.h> const int TEXT_LEN = 16; const int NUM_SECS_TO_RUN = 30; #define PROGNAME "selfCaller" int numTimesCalled[3] = {0,0,0}; pid_t pidToSignal = -1; pid_t childPid = -1; int level = +2; int shouldRun = 1; void sigAlarmHandler(int sig){ printf("Process %d: called level 0\n",level); int w = rand() % 10 + 1; alarm(w); numTimesCalled[0]++; if(level != 2){ pid_t pid = getppid(); kill(SIGUSR1, pid); } } void sigUs1Handler(int sig){ printf("Process %d: called level 1\n",level); numTimesCalled[1]++; if(level != 2){ pid_t pid = getppid(); kill(SIGUSR2, pid); } } void sigUs2Handler(int sig){ printf("Process %d: called level 2\n",level); numTimesCalled[2]++; } void sigIntHandler(int sig){ shouldRun = 0; } int main (int argc, char* argv[] ) { int comm; if (argc > 1){ comm = strtol(argv[1], NULL, 0); } if (comm == 0 || comm == 1){ level = comm; } srand(getpid()); struct sigaction act; memset(&act, '\0', sizeof(act)); act.sa_handler = sigAlarmHandler; sigaction(SIGALRM, &act, NULL); act.sa_handler = sigAlarmHandler; sigaction(SIGUSR1, &act, NULL); act.sa_handler = sigUs1Handler; sigaction(SIGUSR2, &act, NULL); act.sa_handler = sigIntHandler; sigaction(SIGINT, &act, NULL); // alarm(0); pid_t pi; if(level > 0){ pi = fork(); } printf("pid is %d", pi); if (pi ==-1){ exit(EXIT_FAILURE); } char text[TEXT_LEN]; if(pi == 0){ printf("This is the child"); int r; snprintf(text,TEXT_LEN,"%d",level-1); r =execl(PROGNAME, text, NULL); if (r==-1){ fprintf(stderr,"Cannot find %s\n",PROGNAME); exit(EXIT_FAILURE); } } if (level == 2) { int i; for (i = 0; i < NUM_SECS_TO_RUN; i++) { sleep(1); } } else { pidToSignal = getppid(); while (shouldRun) { sleep(1); } } printf("Level %d: %d %d %d\n",level, numTimesCalled[0],numTimesCalled[1],numTimesCalled[2] ); return(EXIT_SUCCESS); }
How to terminate waitpid on user input?
I am working on a ncurses based file manager in C. The problem is that some child processes can take some time to complete and till that happens it remains stuck due to waitpid. I can't use the WNOHANG flag because the next block of code is dependent on the output of the child process. void getArchivePreview(char *filepath, int maxy, int maxx) { pid_t pid; int fd; int null_fd; // Reallocate `temp_dir` and store path to preview file char *preview_path = NULL; allocSize = snprintf(NULL, 0, "%s/preview", cache_path); preview_path = malloc(allocSize+1); if(preview_path == NULL) { endwin(); printf("%s\n", "Couldn't allocate memory!"); exit(1); } snprintf(preview_path, allocSize+1, "%s/preview", cache_path); // Create a child process to run "atool -lq filepath > ~/.cache/cfiles/preview" pid = fork(); if( pid == 0 ) { remove(preview_path); fd = open(preview_path, O_CREAT | O_WRONLY, 0755); null_fd = open("/dev/null", O_WRONLY); // Redirect stdout dup2(fd, 1); // Redirect errors to /dev/null dup2(null_fd, 2); execlp("atool", "atool", "-lq", filepath, (char *)0); exit(1); } else { int status; waitpid(pid, &status, 0); getTextPreview(preview_path, maxy, maxx); free(preview_path); } } In this case, I would like to carry forward with the rest of the program if the user decides to go to some other file. In what way can I change the architecture of the program?
If I have understood the question correctly then you want to unblock parent on either completion of child or any user input. As suggested in this comment, you could handle SIGCHLD and one more signal say SIGUSR1. SIGUSR1 will be raised when you get user input. Following is the example where both SIGCHLD and 'SIGUSR1' is handled. If use inputs any number then it raises SIGUSR1 to parent and parent kill child. Else child will raise SIGCHLD on exit. #include <stdio.h> #include <signal.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> int raised_signal = -1; static void cb_sig(int signal) { if (signal == SIGUSR1) raised_signal = SIGUSR1; else if (signal == SIGCHLD) raised_signal = SIGCHLD; } int main() { int pid; int i, a; struct sigaction act; sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = cb_sig; if (sigaction(SIGUSR1, &act, NULL) == -1) printf("unable to handle siguser1\n"); if (sigaction(SIGCHLD, &act, NULL) == -1) printf("unable to handle sigchild\n"); pid = fork(); if (pid == 0) { /* child */ for (i = 0; i < 10; i++) { sleep(1); printf("child is working\n"); } exit(1); } else { /* parent */ if (-1 == scanf("%d", &a)) { if (errno == EINTR) printf("scanf interrupted by signal\n"); } else { raise(SIGUSR1); } if (raised_signal == SIGUSR1) { printf("user terminated\n"); kill(pid, SIGINT); } else if (raised_signal == SIGCHLD) { printf("child done working\n"); } exit(1); } return 0; }
why WIFSIGNALED is false if i kill the process with SIGPIPE + why exit code 256?
The program consists of 2 parts (i was asked to use pipe): A. manager - that creates processes that will help him calculate how many instances of a particular char are in the file. B. Count - Calculates how many instances there are in the file and returns the pipe to the manager. Moreover, In each program I changed the behavior of SIGPIPE, and changed the behavior I desired. in the manager.c: for(i = 0; i < len_sym; i++){ ... if (pipe(pipe_fds + (2 * i)) == -1) { perror("ERROR : Failed creating pipe"); exit(EXIT_FAILURE); } if ((curr_child = fork()) == 0){ //son dup2(pipe_fds[2*i + 1], STDOUT_FILENO); close(pipe_fds[2*i + 1]); close(pipe_fds[2*i + 0]); execvp(child_args[0], child_args); } else { //parent if (curr_child == -1){ exit(EXIT_FAILURE); } close(pipe_fds[2*i + 1]); // close writerfd child_pids[i] = curr_child; printf("%d son created with pid %d\n", i + 1, child_pids[i]); // if (i == 1) { // kill(curr_child, SIGPIPE); // } } } for(i = 0; i < len_sym; i++){ curr_child = waitpid(child_pids[i], &exit_code, 0); printf("exit code %d\n", exit_code); printf("child pid %d\n", curr_child); if (curr_child == -1 || 256 == exit_code) { exit(EXIT_FAILURE); } if (WIFEXITED(exit_code)) { int bytes_read; while ((bytes_read = read(pipe_fds[i*2 + 0], buff, BUFF_SIZE)) > 0) { buff[bytes_read] = '\0'; printf("%s", buff); } if (bytes_read == -1) { perror("ERROR : Failed reading from fifo"); close(pipe_fds[i*2 + 0]); exit(EXIT_FAILURE); } close(pipe_fds[i*2 + 0]); child_pids[i] = 0; } } handling SIGPIPE in manager.c void my_signal_handler(int signum) { switch (signum) { case SIGPIPE: printf("SIGPIPE for Manager process %d. Leaving\n", getpid()); for (int i = 0; i < len_sym; i++) { if ((child_pids != NULL) && child_pids[i]) { kill(child_pids[i], SIGTERM); } } exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } void register_signal_handlin() { struct sigaction new_action; memset(&new_action, 0, sizeof(new_action)); new_action.sa_handler = my_signal_handler; if (0 != sigaction(SIGPIPE, &new_action, NULL)) { exit(EXIT_FAILURE); } } in the count.c: for (j = 0; j < file_length; j++){ if (*temp++ == c){ counter++; } } sleep(10); if (argc > 4){ sprintf(buff, "Process %d finishes. Symbol %c. Instances %d.\n", getpid(), c, counter); int buff_len = strlen(buff); int bytes_written; while ((bytes_written = write(writerfd, p, buff_len)) > 0) { p += bytes_written; buff_len -= bytes_written; } if (bytes_written == -1) { perror("PROCESS ERROR : Failed writing to fifo"); close(writerfd); return EXIT_FAILURE; } close(writerfd); } handling SIGPIPE in count.c void my_signal_handler(int signum) { switch (signum) { case SIGPIPE: printf("SIGPIPE for process %d. Symbol %c. Counter %d.\n", getpid(), c, counter); exit(EXIT_FAILURE); case SIGTERM: exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } void register_signal_handling() { struct sigaction new_action; memset(&new_action, 0, sizeof(new_action)); new_action.sa_handler = my_signal_handler; if (0 != sigaction(SIGPIPE, &new_action, NULL)) { exit(EXIT_FAILURE); } if (0 != sigaction(SIGTERM, &new_action, NULL)) { exit(EXIT_FAILURE); } } So in fact I run the manger program, and at the same time opens a new terminal, and from it sends kill -13 to a particular process. the problems: In the manager I get an exit code of 256. Why? The WIFSIGNALED flag does not turn on, although I have sent a signal to the process. The return from the waitpid function is not -1, but the process number I sent kill -13 from the second terminal. I tried to send kill to one of the children from the manager and for some reason he activates the signal handler of the manager rather than the child's signal handler. I am in the process of learning OS, so I will be happy for any help.