I have to write program in C on Linux. It has to have 3 processes - first reads from STDIN, sends message through FIFO to second process, which counts lenght of recevied message and sends result to third process (also through FIFO), which displays it on STDOUT. I have to synchronize it using semaphores. Also I have to add signal handling (which I am doing using shared memory) - one signal to end program, second to stop it, third to resume. Signal can be send to any of the processes. I have some code already, but it's not working as it should.
First problem is with synchronization - as you can see by running it, first message is received by second process, but then it stuck. First and second processes are showing their messages, but not the third one. There are very similar, so it's confusing. I have to send another message, then P3 is showing the length of previous one.
Second problem is with signals - after sending one, I have to press enter (for SIGUSR) or send message (for SIGINT) for it to be served.
Any ideas what's wrong? There are some improvements from what I posted before, but it's still not working properly and I don't have much time to finish it (till monday). I know it's a lot of code, but if someone could just analyze communication of second and third process, I will be very grateful.
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <sys/sem.h>
#include <signal.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>
#define WRITE 1
#define READ 0
#define MEM_ID 1
#define MEM_SIZE 1
#define OK "[ \033[1;32mOK\033[0m ]\n"
#define ERR "[\033[1;31mFAIL\033[0m] "
#define SIGNAL "\033[1;33m\033[5m>> SIGNAL <<\033[0m\n"
#define S1 SIGINT
#define S2 SIGUSR1
#define S3 SIGUSR2
#define S4 SIGCONT
/* union semun - from POSIX specification for semctl() */
/* NB: on Mac OS X, and apparently in defiance of POSIX, <sys/sem.h> declares union semun */
/*
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
} arg;
*/
/*
union semun
{
int val;
ushort *array;
};
*/
int process1(void);
int process2(void);
int process3(void);
void signal_callback(int signo);
//void signal_handling(int snd, int rcv); // JL
void signal_handling(void); // JL
void sem_down(int semid, int semnum);
void sem_up(int semid, int semnum);
char work = 1, quit = 0;
char inter_snd = 0, inter_rcv = 0;
struct sembuf semstruct;
int sem23, sem12, mem;
char *message;
int res[3];
static const char *fifoname[] = { "1fifo2", "2fifo3" };
static
void main_quit(int n)
{
printf("%s(): signal %d\n", __func__, n); // JL
kill(res[0], S1);
}
int main(void)
{
//union semun arg; // JL
printf("[G] Launching\n");
signal(SIGINT, main_quit);
signal(SIGTERM, main_quit);
// creating FIFO
printf("[G] Creating FIFO... ");
res[0] = mkfifo(fifoname[0], 0644);
res[1] = mkfifo(fifoname[1], 0644);
if ((res[0] == -1) || (res[1] == -1))
{
perror(ERR);
unlink(fifoname[0]);
unlink(fifoname[1]);
return 1;
}
else
printf(OK);
// create two semaphores and set values
printf("[G] Creating semaphores... ");
sem12 = semget(READ, 1, IPC_CREAT | 0644);
sem23 = semget(WRITE, 1, IPC_CREAT | 0644);
if ((sem23 == -1) || (sem12 == -1))
{
perror(ERR);
return 1;
}
else
printf(OK);
printf("[G] Initializing semaphores values... ");
semctl(sem12, 0, SETVAL, 0);
semctl(sem12, 1, SETVAL, 1);
semctl(sem23, 0, SETVAL, 0);
semctl(sem23, 1, SETVAL, 1);
printf(OK);
// creating shared memory
printf("[G] Reserving shared memory... ");
mem = shmget(MEM_ID, MEM_SIZE, IPC_CREAT | 0644);
message = (char *)shmat(mem, 0, 0);
if (mem == -1)
{
perror(ERR);
return 1;
}
else
printf(OK);
if ((res[0] = fork()) == 0)
{
process1();
exit(0);
}
if ((res[1] = fork()) == 0)
{
process2();
exit(0);
}
if ((res[2] = fork()) == 0)
{
process3();
exit(0);
}
printf("[G] Building process tree... ");
if ((res[0] == -1) || (res[1] == -1) || (res[2] == -1))
{
perror(ERR);
return 1;
}
else
{
printf(OK);
printf("[G] P1[pid]: %d, P2[pid]: %d, P3[pid]: %d\n", res[0], res[1], res[2]);
}
wait(NULL);
wait(NULL);
wait(NULL);
printf("[G] Deleting FIFO... ");
res[0] = unlink(fifoname[0]);
res[1] = unlink(fifoname[1]);
if ((res[0] == -1) || (res[1] == -1))
perror(ERR);
else
printf(OK);
printf("[G] Freeing shared memory... ");
res[0] = shmdt((char *)message);
res[1] = shmctl(mem, IPC_RMID, 0);
if ((res[0] == -1) || (res[1] == -1))
perror(ERR);
else
printf(OK);
printf("[G] Deleting semaphores... ");
res[0] = semctl(sem23, 0, IPC_RMID, 0);
res[1] = semctl(sem12, 0, IPC_RMID, 0);
if ((res[0] == -1) || (res[1] == -1))
perror(ERR);
else
printf(OK);
printf("[G] Ending...\n");
return 0;
}
int process1(void)
{
char tab[100];
FILE *fifoh;
signal(S1, signal_callback);
signal(S2, signal_callback);
signal(S3, signal_callback);
signal(S4, signal_callback);
fifoh = fopen(fifoname[0], "w");
setbuf(fifoh, NULL);
printf("[P1] Ready.\n");
//while (fgets(tab, sizeof(tab), stdin) > 0) // JL
while (fgets(tab, sizeof(tab), stdin) != 0) // JL
{
if (work)
{
sem_down(sem12, WRITE);
printf("[P1] Sending: %s", tab);
fprintf(fifoh, "%s\n", tab);
sem_up(sem12, READ);
}
//signal_handling(inter_snd, inter_rcv); // JL
signal_handling(); // JL
}
fclose(fifoh);
printf("[P1] Ending...\n");
return 0;
}
int process2(void)
{
char tab[100];
FILE *fifo_in, *fifo_out;
printf("[P2] Ready.\n");
fifo_in = fopen(fifoname[0], "r");
fifo_out = fopen(fifoname[1], "w");
setbuf(fifo_out, NULL);
setbuf(fifo_in, NULL);
signal(S1, signal_callback);
signal(S2, signal_callback);
signal(S3, signal_callback);
signal(S4, signal_callback);
do
{
if (work)
{
sem_down(sem12, READ);
fscanf(fifo_in, "%s", (char *)tab);
sem_up(sem12, WRITE);
printf("[P2] Received \"%s\" with length %zu.\n", tab, strlen(tab));
sem_down(sem23, WRITE);
fprintf(fifo_out, "%d\n", (int)strlen(tab));
sem_up(sem23, READ);
}
//signal_handling(inter_snd, inter_rcv); // JL
signal_handling(); // JL
} while (!quit);
fclose(fifo_in);
fclose(fifo_out);
printf("[P2] Ending...\n");
return 0;
}
int process3(void)
{
FILE *fifo_in;
int count;
printf("[P3] Ready.\n");
signal(S1, signal_callback);
signal(S2, signal_callback);
signal(S3, signal_callback);
signal(S4, signal_callback);
fifo_in = fopen(fifoname[1], "r");
setbuf(fifo_in, NULL);
do
{
if (work)
{
sem_down(sem23, READ);
fscanf(fifo_in, "%d\n", (int *)&count);
sem_up(sem23, WRITE);
printf("[P3] Received: %d characters.\n", count);
}
//signal_handling(inter_snd, inter_rcv); // JL
signal_handling(); // JL
} while (!quit);
fclose(fifo_in);
printf("[P3] Ending...\n");
return 0;
}
//void signal_handling(int snd, int rvc)
void signal_handling(void)
{
if (inter_snd > 0)
{
printf("Signal received...\n");
semstruct.sem_op = -3;
semop(sem23, &semstruct, 1);
*message = inter_snd;
inter_snd = 0;
semstruct.sem_op = 3;
semop(sem12, &semstruct, 1);
printf("Sending to other processes\n");
kill(0, S4);
}
if (inter_rcv)
{
inter_rcv = 0;
semstruct.sem_op = -1;
semop(sem12, &semstruct, 1);
switch (*message)
{
case 1:
printf("Quitting...\n");
quit = 1;
break;
case 2:
printf("Stopping...\n");
work = 0;
break;
case 3:
printf("Starting...\n");
work = 1;
break;
default:
printf("There's garbage in memory :/..\n");
}
semstruct.sem_op = 1;
semop(sem23, &semstruct, 1);
}
}
void signal_callback(int signo)
{
printf(SIGNAL);
switch (signo)
{
case S1:
inter_snd = 1;
break;
case S2:
inter_snd = 2;
break;
case S3:
inter_snd = 3;
break;
case S4:
inter_rcv = 1;
break;
}
}
void sem_down(int semid, int semnum)
{
semstruct.sem_flg = 0;
semstruct.sem_num = semnum;
semstruct.sem_op = -1;
do
{
errno = 0;
semop(semid, &semstruct, 1);
} while (errno == EINTR);
}
void sem_up(int semid, int semnum)
{
semstruct.sem_flg = 0;
semstruct.sem_num = semnum;
semstruct.sem_op = 1;
semop(semid, &semstruct, 1);
}
Expected behaviour:
[P3] Ready.
[P2] Ready.
[P1] Ready.
asd
[P1] Sending: asd
[P2] Received "asd" with length 3.
[P3] Received: 3 characters.
as
[P1] Sending: as
[P2] Received "as" with length 2.
[P3] Received: 2 characters.
Signals:
Signal should be recieved and sent to every other processes. What happens next is specified in singnal_handling. Every process should show their message (Quitting/Stopping/etc).
Actual behaviour:
[P3] Ready.
[P2] Ready.
[P1] Ready.
asd
[P1] Sending: asd
[P2] Received "asd" with length 3.
dd
[P1] Sending: dd
[P2] Received "dd" with length 2.
[P3] Received: 3 characters. //first message
as
[P1] Sending: as
[P2] Received "as" with length 2.
[P3] Received: 2 characters. //second
asd
[P1] Sending: asd
[P2] Received "asd" with length 3.
[P3] Received: 2 characters. // third
And with signals, well... I figured out that after SIGINT I could send message, hit enter twice and then I got expected behaviour (quitting program). Pressing enter also works when I'm sending another signals:
$ kill -s SIGUSR1 2900
$ kill -s SIGUSR2 2900
Gives:
asd
[P1] Sending: asd
[P2] Received "asd" with length 3.
[P3] Received: 3 characters.
Signal received...
Sending to other processes
>> SIGNAL <<
Stopping...
>> SIGNAL <<
>> SIGNAL <<
>> SIGNAL <<
Signal received...
Sending to other processes
>> SIGNAL <<
>> SIGNAL <<
>> SIGNAL <<
Starting...
[P1] Sending:
Starting...
asd
[P1] Sending: asd
[P2] Received "asd" with length 3.
Starting...
[P3] Received: 3 characters.
So again - after sending a signal I have to send a message for it to be handled. So, it's kinda working. But very, very bad.
Taking your code verbatim onto a Mac running Mac OS X 10.10.3, using GCC 5.1.0, using the standard compilation options I use for compiling code from Stack Overflow, I get compilation warnings as shown (I called your code
semshm.c
):The first problem is peculiar to Mac OS X; it seems to define
union semun
in<sys/sem.h>
despite the POSIX specification forsemctl()
saying explicitly:The only excuse for the POSIX requirement is bad historical precedent, which Mac OS X has over-ridden. So, the double declaration of
struct semun
is not held against you. It is curious that POSIX requires an extra member, though, compared with what you specify. Anyway, I commented out that declaration to get past that.There are other warnings, though many of them are not all that serious. Unused variables are not good, but not hugely harmful. The format mismatch between
%d
andsize_t
matters if, as on my machine,%d
looks for a 32-bit value andsize_t
is a 64-bit quantity. The test forfgets()
should be!= 0
; it returns NULL (or 0 — I use 0 too) on failure.The most curious problem is the unused arguments to
signal_handling()
. The calls pass the global variablesinter_snd
andinter_rcv
to the function, but the function ignores those values and simply manipulates the global variables. There is at least a serious disconnect between the calling and called code here.For the purposes of getting on with life, I converted
signal_handling()
to a parameterless functionvoid signal_handling(void);
and left its body unchanged (but fixed all the calls to it). I added:to
main_quit()
in defiance of the advice on how to avoid usingprintf()
in a signal handler. I also fixed the 'i' before 'e' except after 'c' spelling mistakes. Also 'quitting' has two t's and 'stopping' has two p's — English is such a weird language.Finally, I extracted the two FIFO names (
1fifo2
and2fifo3
) into an array:and used either
fifoname[0]
orfifoname[1]
in place of the literals. Repeated file name literals are a bad idea. I also used the new names to clean up (unlink(fifoname[0]); unlink(fifoname[1]);
) the FIFOs if either existed after failing to create them.With the code compiled, I ran it a few times. One of the last runs was:
I typed an interrupt, which was handled. I typed 'absolute baloney', of which the 'absolute' part was apparently received in one place, but the 'baloney' part never made it. That's puzzling. I typed 'commestible goods for everyone' and to all appearances, it was completely ignored. I tried another interrupt, which apparently interrupted things. I tried control-D (EOF), which apparently did nothing. I used control-backslash (control-\) to generate a quit signal, which did indeed stop the program. I'd done that before; reruns showed the wisdom of cleaning up after spotting that the FIFOs were already created as I got the failure message, then reran the code and it worked OK. The output shows green OK messages, and flashing yellow-ish
>>> SIGNAL <<<
messages.So, you didn't tell us how to use the program, or the output to expect from it.
Your 'no compiler warnings' assertion was a little optimistic, but not too far off the truth.
Now you need to upgrade the question to specify/illustrate the inputs you give it and the expected behaviour from that input and to show the unexpected behaviour you actually get from it.
One semi-working version of
semshm.c
This is the 'as compiled', partially annotated code that I compiled and ran. To compile it on Linux or other more accurately POSIX-compliant platform (more accurate than Mac OS X, that is), you'll need to uncomment one of the
union semun
blocks. I'd normally remove<sys/types.h>
(modern POSIX almost never requires you to include it; older versions from the last millennium did require it — see POSIX 1997 which did, but compare with POSIX 2004 which did not).Another mostly working version of
semshm.c
Taking the extra information about how the code is supposed to be working, and putting in a lot of extra diagnostic printing, and observing that process 2 and 3 get stuck waiting on a semaphore which is not signalled, I came up with this variant of the code:
Note the two
sum_up()
calls marked 'crucial'; they are indeed crucial. Also note that the input operations are error checked; that too is crucial. If you don't check that they worked, you miss the EOF indications. Always, but always check that the I/O operations worked correctly.With that code, a sample run was:
I've not worked out how much of the code could be eliminated — but my suspicion is that 'quite a lot' is an accurate statement. The semaphore operations are really not necessary; the processes would block correctly on the file streams anyway. I'm not convinced that the shared memory helps at all, either. And I'm not convinced that
work
is helping very much, but it might conceivably do something useful under signal handling. Note that it is not shared between processes (it is private to each process), so changes made towork
by one process only affect that process, not the other processes.