逐个元素地读取是可行的,但无法读取大块

时间:2018-11-12 21:16:05

标签: c pipe fork

我正在尝试使用N个进程确定元素是否在向量中退出,如果为true,则返回其所有位置。每个过程都接收一个索引和一个步骤。索引从0到“ numberOFProcesses -1”,并且每个进程检查元素都从索引开始,并逐步增加。

工作原理:让我们假设我们有4个流程。流程0检查元素0,4,8 ...,流程1检查元素1,5,9 ...等等。

我是如何实现的:我有2个管道:一个用于位置;第二个管道用于存储目标的出现次数。每当进程找到目标时,它都会增加出现的次数,并将索引写到“ 索引”管道中,最后,在退出作业时,它会写入“ 出现次数”传递出现的次数(如果有),并返回true或false。我最初想直接返回出现的次数,但我意识到“ WEXITSTATUS”仅使用8位,这可能是个问题。

问题:尝试读取大小为“出现次数”的块失败或给出无效结果。一次读取一个值似乎很好。我也使用valgrind和gdb检查了它,但似乎找不到问题。 Valgrind在尝试读取块时报告大量问题,但一次读取一个时报告0个错误。只有在流程找到目标后,才读取事件。

P.S。我知道我可以这样离开,但多次阅读毫无意义。

现在,输入一些代码:

#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
#include <sys/signal.h>
#include <sys/types.h>
/**
 * @brief basic defines
 * 
 */
#define MAX_RAND 100
#define TRUE 1
#define FALSE 0

#define CHILDREN 0

#define READ 0
#define WRITE 1

int size = 13;
int *array;
int target;
int index_pipe[2];
int occurences_pipe[2];

/**
 * @brief this populates the array with random number
 * 
 * @param array the given array
 * @param size the size of the array
 */
void populate(int *array, int size)
{
    for (int i = 0; i < size; i++)
    {
        array[i] = rand() % MAX_RAND;
    }
}

/**
 * @brief this determines whether an elements occurs in an array and writes to pipes the number 
 * of occurences and the the indexes on which resides the target
 * 
 * @param target the value we are looking for
 * @param index the index of the process, i.e. the process id
 * @param step the step, i.e. the number of processes
 * @return int the search status. This returns true if "target occurs", FALSE otherwise
 */
int search(int target, int index, int step)
{
    int i = index;
    int numberOfOccurences = 0;

    /**
     * @brief each process will start at position index and will check values starting with index, incrementing with step
     * ex: process 0 will check 0,4,8,12..
     *     process 1 will check 1,5,9,13...
     */
    while (i < size)
    {
        if (target == array[i])
        {
            /**
             * @brief if the target occues increment the number of occurences and write an index to pipe
             * 
             */
            numberOfOccurences++;
            write(index_pipe[WRITE], &i, sizeof(int));
        }
        i += step;
    }

    /**
     * @brief write occurences to pipe if, and only if, the number of occurences is not 0, 
     * i.e. we have found the target at least once and return TRUE or FALSE
     * 
     */
    if (numberOfOccurences != 0)
    {
        write(occurences_pipe[WRITE], &numberOfOccurences, sizeof(int));
        return TRUE;
    }

    return FALSE;
}

/**
 * @brief this prints a given array
 * 
 * @param array the array we want to print
 * @param size the size of the array
 */
void printArray(int *array, int size)
{
    printf("Array: \n");
    for (int i = 0; i < size; i++)
    {
        printf("%d ", array[i]);
    }
    printf("\n");
}

/**
 * @brief entry point
 * 
 * @return int EXIT_SUCCESS
 */
int main()
{
    /**
     * @brief initialize and allocate memory
     * 
     */
    size = 13;
    array = (int *)malloc(sizeof(int) * size);

    pipe(index_pipe);
    pipe(occurences_pipe);

    int numerOfProccesses = 3;
    int target = 15;
    int totalOccurences = 0;
    int status = -1;
    int exit_status = -1;
    int occurences = -1;

    populate(array, size);
    array[size - 1] = target;
    printArray(array, size);

    size_t processes[numerOfProccesses];

    /**
     * @brief create childrens and put them to work
     * 
     */
    for (int i = 0; i < numerOfProccesses; i++)
    {
        processes[i] = fork();
        if (CHILDREN == processes[i])
        {
            /**
             * @brief get the search status and exit
             * 
             */
            int exit_status = search(target, i, numerOfProccesses);
            exit(exit_status);
        }
    }

    /**
     * @brief wait for children to exit
     * 
     */
    for (int i = 0; i < numerOfProccesses; i++)
    {
        /**
         * @brief wait for each children. If a children is done AND it has found taget, i.e. returned TRUE,
         * then read the number of occurrences from pipe
         * 
         */
        wait(&status);
        if (WIFEXITED(status))
        {
            exit_status = WEXITSTATUS(status);

            if (exit_status == TRUE)
            {
                read(occurences_pipe[READ], &occurences, sizeof(int));
                totalOccurences += occurences;
            }
        }
    }

    /**
     * @brief if the number of occurrences is 0, then we have'nt found target
     * 
     */
    if (totalOccurences == 0)
    {
        printf("%d not found \n", target);
    }
    else
    {
        /**
         * @brief else allocate memory for an array of size "occurrences" and read from index pipe
         * 
         */
        printf("Found %d on %d positions\n", target, totalOccurences);
        int *indexes = (int *)malloc(sizeof(int) * 3);
        // for (int i = 0; i < totalOccurences; i++)
        // {
        //     int value;
        //     read(index_pipe[READ], &value, sizeof(int));
        //     printf("Read %d \n", value);
        // }
        int pipe_status;
        pipe_status = read(index_pipe[READ], indexes, totalOccurences);
        printf("Pipe read %d bytes\n", pipe_status);
        printArray(indexes, totalOccurences);
    }
    return 0;
}

预期输出:

Array:
83 86 77 15 93 35 86 92 49 21 62 27 15
Found 15 on 2 positions
Read 3
Read 12
Array:
3 12

我在一次读取一个块时得到了这个信息:

Array:
83 86 77 15 93 35 86 92 49 21 62 27 15
Found 15 on 2 positions
Pipe read 2 bytes
Array:
3 0

P.S。我是在Linux机器上写的。我使用以下代码进行了编译:gcc -g -o search search.c -Wextra

1 个答案:

答案 0 :(得分:1)

...
read(occurences_pipe[READ], &occurences, sizeof(int));
totalOccurences += occurences;

int *indexes = (int *)malloc(sizeof(int) * 3);
read(index_pipe[READ], indexes, totalOccurences);

好吧,您正在读取未知数量的字节,这是从管道中每个进程中发现的数字的总和,并将其保存到sizeof(int) * 3个字节中,这很可能会溢出。 totalOccurences也是所有进程的 sum 。我想你的意思是:

int *indexes = (int *)malloc(sizeof(int) * totalOccurences);
read(index_pipe[READ], indexes, sizeof(int) * totalOccurences);
  1. 我喜欢这种并发性和与多个进程进行通信的单一管道的想法。您可以通过在读取read(index_pipe[READ], ..., sizeof(int) * occurences)循环中使用realloc + if (exit_status == TRUE)来加快处理速度。这样,您可以更早地释放管道中缓冲的数据。
  2. 如果您只是对所有事件的总和感兴趣,我认为没有必要使用encesences_pipe。您只需在index_pipe[READ]上设置O_NONBLOCK,然后逐字节(或逐块)读取它,直到所有线程完成并且都将返回0。所有事件的总和是所有线程退出后从index_pipe读取的字节数除以sizeof(int)
  3. 我认为线程将更适合于此类任务,您是在fork上的进程之间复制整个数组,而使用pthreads时,每个线程将使用相同的内存。
  4. 并且,出于对K&R的热爱,don't cast the result of malloc