Не могу найти решение для моей игры в догадки - PullRequest
1 голос
/ 29 апреля 2020

Я делаю другую версию моей игры в догадки. На этот раз дочерний процесс должен отправить свое предположение родителю, который затем оценивает это. Я думаю, что я делаю неправильно, что мой ребенок бегает только один раз, но не может понять, как угадать, пока не найдет правильное число.

#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 Ответ

2 голосов
/ 29 апреля 2020

Один из способов - поместить ребенка в al oop, а затем удалить очередь сообщений, как только вы получите правильный ответ, что приведет к сбою msgsnd при EIDRM для выхода из l oop:

#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);
}

Я также исправил несколько других вещей в вашей программе:

  • Вместо того, чтобы использовать фиксированный KEY, я изменил его на IPC_PRIVATE, что исключает возможность столкновение клавиш. Поскольку вы не пытаетесь открыть ту же очередь в другом месте, нет причин использовать фиксированную.
  • Я избавился от statbuff и вашего IPC_STAT вызова. Они не делали ничего полезного.
  • Я удалил ваш второй звонок на srand. Делая два так близко друг к другу, time(NULL) был одинаковым оба раза, поэтому ваша дочерняя программа будет иметь одно и то же состояние случайных чисел, и в результате каждый раз будет угадываться с первой попытки.
  • Return значение успешного msgrcv - это размер сообщения, который всегда будет одинаковым (вероятно, 16). Я изменил его, чтобы проверить фактическое предположение, в buf.szam.
  • Ваша первая проверка guess была до вашей первой msgrcv, что привело к ложному предположению, которое было не от ребенка. Я изменил ваш while l oop на do-while l oop, чтобы избежать этого.

И вот еще несколько вещей, которые следует исправить, но я оставляю их в качестве упражнений для читатель:

  • Избавьтесь от всего, что вы на самом деле не используете, например false (кстати, ужасное имя переменной)
  • Не будь таким " умный "с запятыми, как в perror("msgget"), exit(EXIT_FAILURE);. Просто используйте фигурные скобки и точку с запятой.
  • Вы должны сохранить результат fork() в переменной, чтобы вы могли проверить, отрицательный ли он, что будет указывать на ошибку.
  • Размер, который вы передаете msgsnd и msgrcv должны быть размером второго элемента структуры сообщения (т. Е. Не включая mtype или заполнение сразу после него), а не размером всей структуры.
  • Вы должны проверить возврат msgrcv, чтобы убедиться, что он не потерпит неудачу.
  • Запуск потомка с постоянной l oop, как я это сделал, является самым простым подходом, но не обязательно самым эффективным или Лучший. Подумайте о том, чтобы родитель отправлял сообщения дочернему элементу, чтобы он выдавал только одно предположение за раз вместо заполнения очереди настолько, насколько это возможно. (Даже если вы сделаете это изменение, вы все равно должны позволить родителю удалить очередь сообщений в конце, потому что в противном случае она не исчезнет go, пока вы не перезагрузите или не очистите ее вручную с помощью ipcrm.)
...