C: общая память и разветвление, оператор print выполняется несколько раз - PullRequest
1 голос
/ 15 марта 2019

Я работаю над программой, которая вычисляет гипотезу Коллатца для заданного числа, используя общую память. Родитель создаст дочерний процесс, затем ребенок вычислит гипотезу и, используя разделяемую память, предоставит ее родителю, чтобы родитель мог распечатать значение. Если дочерний элемент не может вычислить полную гипотезу, поскольку ему не хватает места для хранения ее в структуре разделяемой памяти, тогда родительский процесс создаст новый дочерний процесс, который продолжится там, где остановился последний. У меня возникла проблема, когда операторы print в родительском процессе, которые отображают результаты дочерних процессов из общей памяти, печатаются несколько раз.

/*********************************
 * Applies the Collatz conjecture
 * to the given positive integer
 * using shared memory.
 *********************************/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/shm.h>
#include <sys/stat.h>

#define MAX_SEQUENCE 30

typedef struct {
    int size; // Number of values in this sequence
    short partial; // Flag
    long num[MAX_SEQUENCE];
} shared_data;

int main(int argc, char* argv[])
{
    // Name of the shared memory segment
    const char *name = "myMemorySeg";

    // Shared memory file descriptor
    int shm_fd;

    // A pointer to the shared memory segment
    shared_data* shared_memory;

    // Handle input validation
    if (argc != 2) {
        fprintf(stderr, "Usage: %s num\n", argv[0]);
        exit(1);
    }
    // Get number from argument
    int n = atoi(argv[1]);

    do {
        int pid;
        // Create a child process with shared memory space
        pid = create_child(&name, &shm_fd, &shared_memory);

        // Parent
        if (pid > 0) {
            wait(NULL);

            // Print out collatz results
            int i;
            for (i = 0; i < shared_memory->size; i++)
                printf("%d ", shared_memory->num[i]);

            // If this was only part of the sequence
            //     Then start the next sequence at the collatz of the last sequence value
            if (shared_memory->partial)
                n = get_collatz(shared_memory->num[MAX_SEQUENCE - 1]);
        }
        // Child
        else if (pid == 0) {
            // Generate the collatz sequence and store the result in the shared memory
            int i = 0;
            shared_memory->num[i++] = n; // Store the initial number
            while (n != 1 && i < MAX_SEQUENCE) {
                n = get_collatz(n);
                shared_memory->num[i++] = n; // Store the next number
            }
            // If we have filled the sequence array and n hasn't reached 1
            //     then this is only a partial sequence
            shared_memory->partial = (i == MAX_SEQUENCE && n != 1) ? 1 : 0;
            // What is the sequence size?
            shared_memory->size = i;

            // Kill the child process
            exit(0);
        }

        // Remove the shared memory object
        shm_unlink(name);

    } while (shared_memory->partial); // While the last sequence was partial
    printf("\n");

    return 0;
}

/********************************
* create_child()
*
* Opens a shared memory space
* and creates a child process
* to share that space with the
* parent.
*
* Returns the process id if
* successful, otherwise exits
* the parent process.
********************************/
int create_child(char **name, int *shm_fd, shared_data** shared_memory) {
    // Create a shared memory object
    *shm_fd = shm_open(*name, O_CREAT|O_RDWR, 0666);

    // Configure the size of the shared memory object
    ftruncate(*shm_fd, sizeof(shared_data));

    // Memory map the shared memory object
    *shared_memory = (shared_data *) mmap(0, sizeof(shared_data), PROT_WRITE, MAP_SHARED, *shm_fd, 0);

    // Create child process
    int pid;
    // Return -1 if error
    if ((pid=fork()) == -1) {
        perror("Failed to create child process");
        exit(1); // Kill parent process
    }
    // Otherwise return the pid created by fork
    return pid;
}

/********************************
* get_collatz()
*
* Returns the result of running
* the input n through the
* collatz conjecture function.
********************************/
int get_collatz(int n) {
    return (!(n%2)) ? (n/2) : (3*n + 1);
}

Вот как выглядит вывод консоли:

Console output

Интересно, если я добавлю оператор печати с новой строкой в ​​родительский процесс, прежде чем он выведет результаты дочернего процесса из общей памяти, вот так:

    do {
        int pid;
        printf("\n");
        // Create a child process with shared memory space
        pid = create_child(&name, &shm_fd, &shared_memory);

        // Parent
        if (pid > 0) {
            wait(NULL);

            // Print out collatz results
            int i;
            for (i = 0; i < shared_memory->size; i++)
                printf("%d ", shared_memory->num[i]);

            // If this was only part of the sequence
            //     Then start the next sequence at the collatz of the last sequence value
            if (shared_memory->partial)
                n = get_collatz(shared_memory->num[MAX_SEQUENCE - 1]);
        }
        // Child

Тогда операторы вывода будут выводиться правильное количество раз.

Console output with print statement

Еще один интересный факт заключается в том, что это решение работает, только если я помещаю оператор печати новой строки перед вызовом create_child (), а не если я помещаю его после.

Я не хочу, чтобы они были разделены новыми строками, я хочу, чтобы они печатали все в одной строке. Любые идеи, что вызывает эти дополнительные заявления печати?

1 Ответ

3 голосов
/ 15 марта 2019

Вам нужно либо добавить соответствующие вызовы сброса, либо изменить стандартный вывод, чтобы он не буферизовался.

Библиотека пытается быть эффективной и фактически не записывает в терминал, пока у нее не будет полной строки. Таким образом, он хранит частичные строки в буфере. Когда вы fork, вы сталкиваетесь с двумя практически идентичными процессами, то есть каждый из них имеет одинаковые буферизованные данные. Если они оба заканчивают строку вывода, они оба запишут буферизованные данные.

У вас все еще может быть проблема, что все различные выходы перемешаны вместе. Более обычный способ справиться с этим - иметь только один процесс, отвечающий за все выходные данные, и «рабочие» процессы передают свои результаты обратно процессу «менеджера», который должен быть напечатан в разумном порядке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...