1

I'm making a different version of my guessing game. This time, the child process has to send it's guess to the parent, which then evaluates that. What I think I'm doing wrong is that my child only runs once, but can't figure out how to get guesses until it finds the correct number.

#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 KEY 19950914
#define FLAG 0666

struct message {
    long mtype;
    int szam;
};

int main()
{
    int number, false=1, guess=0;
    int mqid;
    struct message buf;
    struct msqid_ds statbuff;

    mqid = msgget(KEY, FLAG | IPC_CREAT);

    if (mqid < 0)
            perror("msgget"), exit(EXIT_FAILURE);

    srand(time(NULL));
    number = rand() % 256;

    if (fork() == 0)
    {
            srand(time(NULL));
            buf.mtype = 2;
            buf.szam = rand() % 256;
            msgsnd(mqid, &buf, sizeof(struct message), 0);
            msgctl(mqid, IPC_STAT, &statbuff);

    exit(EXIT_SUCCESS);
    }

    while ( guess != number )
    {
            if (guess > number)
                    printf("Too high!\n");
            else if (guess < number)
                    printf("Too low!\n");

            guess = msgrcv(mqid, &buf, sizeof(struct message), 2, 0);
    }

    printf("Winner! Yes, the answer was %d \n",number);

    wait(NULL);

    exit(EXIT_SUCCESS);
}

1 Answers1

2

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.)
  • 2
    Thank you for such a comprehensive review of what I did wrong. Honestly this community has been great, and the help I got from here has been a huge help now that we can't go to university, and have a harder time understanding what we should really do. – Bakos Dominik Apr 28 '20 at 23:55