C:共享内存和分叉,打印语句执行多次

时间:2019-03-15 19:45:19

标签: c

我正在开发一个程序,该程序使用共享内存为给定的数字计算collat​​z猜想。父级将创建一个子进程,然后子级将计算推测,并使用共享内存将其提供给父级,以便父级可以将值打印出来。 如果子进程无法计算出完整的猜想,因为它没有足够的空间将其存储在共享内存结构中,那么父进程将创建一个新的子进程,以继续最后一个进程的停止。 我遇到一个问题,即父进程中显示共享内存中子级结果的打印语句被多次打印。

/*********************************
 * 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()调用之前,而我放置在new_print打印语句之后,该解决方案才有效。

我不希望这些行之间用换行符分隔,我希望它们全部打印在一行上。有什么想法导致这些额外的打印语句吗?

1 个答案:

答案 0 :(得分:3)

您要么需要添加适当的刷新调用,要么需要更改标准输出以不进行缓冲。

该库正在尝试提高效率,直到有完整的行才真正写入终端。因此,它将部分行存储在缓冲区中。 fork结束时,您将获得两个基本相同的进程,即它们各自具有相同的缓冲数据。如果它们两个都完成一行输出,则它们都会写入缓冲的数据。

您可能仍然有一个问题,所有各种输出混杂在一起。处理此问题的更常用方法是只由一个过程负责所有输出,而“工人”过程将其结果传达回“经理”过程,以便以理智,有序的方式进行打印。