One way is to put the child in a loop, and then delete the message queue once you get the right answer, which will make msgsnd
fail with EIDRM
to exit the loop:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <sys/wait.h>
#include <time.h>
#define FLAG 0666
struct message {
long mtype;
int szam;
};
int main()
{
int number, false=1, guess;
int mqid;
struct message buf;
mqid = msgget(IPC_PRIVATE, FLAG | IPC_CREAT);
if (mqid < 0)
perror("msgget"), exit(EXIT_FAILURE);
srand(time(NULL));
number = rand() % 256;
if (fork() == 0)
{
buf.mtype = 2;
int sndres;
do {
buf.szam = rand() % 256;
sndres = msgsnd(mqid, &buf, sizeof(struct message), 0);
} while(sndres == 0);
exit(EXIT_SUCCESS);
}
do {
msgrcv(mqid, &buf, sizeof(struct message), 2, 0);
guess = buf.szam;
if (guess > number)
printf("Too high!\n");
else if (guess < number)
printf("Too low!\n");
} while ( guess != number );
printf("Winner! Yes, the answer was %d \n",number);
msgctl(mqid, IPC_RMID, NULL);
wait(NULL);
exit(EXIT_SUCCESS);
}
I fixed a few other things in your program too:
- Rather than using a fixed
KEY
, I changed it to IPC_PRIVATE
, which avoids the possibility of a key collision. Since you aren't trying to open the same queue elsewhere, there's no reason to use a fixed one.
- I got rid of
statbuff
and your IPC_STAT
call. They weren't doing anything useful.
- I removed your second call to
srand
. By doing two so close together, time(NULL)
was the same both times, so your child program would have the same random number state, and so would end up guessing right on its first try every time.
- The return value of a successful
msgrcv
is the size of the message, which will always be the same (probably 16). I changed it to check the actual guess, in buf.szam
.
- Your first check of
guess
was before your first msgrcv
, which resulted in a spurious guess that wasn't from the child. I changed your while
loop to a do-while
loop to avoid this.
And here's some more things that should be fixed, but that I leave as exercises for the reader:
- Get rid of everything you don't actually use, like
false
(a horrible name for a variable, by the way)
- Don't be so "clever" with commas, as in
perror("msgget"), exit(EXIT_FAILURE);
. Just use braces and a semicolon.
- You should save the result of
fork()
to a variable, so you can check if it's negative, which would indicate failure.
- The size you pass to
msgsnd
and msgrcv
is supposed to be the size of the second member of the message struct (i.e., not including mtype
or the padding right after it), not the size of the whole struct.
- You should check the return of
msgrcv
to make sure it doesn't fail.
- Running the child in a constant loop like I did is the simplest approach, but not necessarily the most efficient or best. Consider making the parent send messages to the child, so that it only emits one guess at a time instead of filling the queue as much as it can. (Even if you do make this change, you should still let the parent delete the message queue at the end, because otherwise it won't go away until you either reboot or clean it up manually with
ipcrm
.)