使用子进程对共享内存进行排序

时间:2019-01-29 21:13:58

标签: c fork shared-memory

我正在尝试在我的快速排序中使用子进程,以使左半部分在一个孩子中排序,右半部分在另一个孩子中排序。在shmget实施之前,我已经使它工作了,但是现在,我相信我正在某个地方破坏该数组,因为在打印该数组之后,我的所有值都变为零。抱歉,如果我在某个地方犯了一些愚蠢的错误,我正在尝试学习如何使用fork和shmget,但是遇到了一些麻烦。我试图将文本文件作为命令行参数,并给定分隔符,例如“;”。我必须删除定界符并确定其间的数字,将它们放置在数组中并使用子进程对其进行排序。我可以进行解析,而快速排序也可以,但是现在我尝试实现共享内存时遇到了一些问题。

谢谢

我看了几个不同的示例,但这主要基于的示例是带有fork的mergesorting的geeksforgeeks示例。 https://www.geeksforgeeks.org/concurrent-merge-sort-in-shared-memory/

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include "fileParser.h"
#include "dataManagement.h"

int main(int argc, char *argv[]){
    char *file = argv[1];
    char delimiter = argv[2][0];
    MyArray *theArray = getArray(file, delimiter);

    size_t SHM_SIZE = theArray->length;

    theArray->key = IPC_PRIVATE;


    if((theArray->shmid = shmget(theArray->key, SHM_SIZE, IPC_CREAT | 0666)) < 0){
        perror("shmget");
        _exit(-1);
    }

    if ((theArray->shm_array = shmat(theArray->shmid, NULL, 0)) == (int *) -1)
    {
        perror("shmat");
        _exit(1);
    }

    printArray(theArray, theArray->length);
    quickSortFork(theArray, 0, theArray->length-1);
    printArray(theArray, theArray->length);

    if (shmdt(theArray->shm_array) == -1)
    {
        perror("shmdt");
        _exit(1);
    }

    if (shmctl(theArray->shmid, IPC_RMID, NULL) == -1)
    {
        perror("shmctl");
        _exit(1);
    }

    return 0;
}

dataManagement.h


#include <unistd.h>
#include <sys/wait.h>
#include "fileParser.h"

int partition(MyArray *arr, int low, int high);
void quickSortFork(MyArray *arr, int low, int high);
void swap(MyArray *arr, int a, int b);


void printArray(MyArray *arr, int length) {
    for(int i = 0; i < length; i++){
        printf("%d  ", arr->shm_array[i]);
    }
    printf("\n");
}


void quickSortFork(MyArray *arr, int low, int high){
    pid_t lpid, rpid;
    rpid = fork();
    if(low < high){
        int partitionIndex = partition(arr,low, high);
        if(rpid < 0){
            perror("Right child not created.\n");
            exit(-1);
        } else if(rpid == 0 ){
            printf("I am the right child!\tMy process id: %d\n",getpid());
            quickSortFork(arr, partitionIndex + 1, high);
            exit(EXIT_SUCCESS);
        } else {
            lpid = fork();
            if(lpid < 0){
                perror("Left child not created.\n");
            } else if (lpid == 0) {
                quickSortFork(arr, low, partitionIndex - 1);
                printf("I am the left child!\tMy process id: %d\n", getpid());
                exit(EXIT_SUCCESS);
            }
        }

        int status;

        waitpid(rpid, &status, 0);
        waitpid(lpid, &status, 0);
    }
}


int partition(MyArray *arr, int low, int high){
    int i = low, j = high;
    int pivot = arr->shm_array[(low+high)/2];
    while(i < j){
        while(arr->shm_array[i] < pivot)
            i++;
        while(arr->shm_array[j] > pivot)
            j--;
        if(i < j){
            swap(arr,i,j);
        }
    }
    return i;
}


void swap(MyArray *arr, int a, int b){
    int temp = arr->shm_array[a];
    arr->shm_array[a] = arr->shm_array[b];
    arr->shm_array[b] = temp;
}

fileParser.h


int findFileLength(FILE *inFile, char delimiter);
int *parseFileToArray(FILE *inFile, char delimiter, int length);

typedef struct {
    int shmid;
    key_t key;
    int length;
    int *shm_array;
} MyArray;


MyArray *getArray(char *fileName, char delimiter){
    FILE *numberFile = fopen(fileName, "r"); // open for reading

    if (numberFile == NULL) // unable to open file
        return NULL;
    MyArray *array = malloc(sizeof(MyArray));
    array->length = findFileLength(numberFile, delimiter);
    array->shm_array = parseFileToArray(numberFile, delimiter,array->length);

    return array;

}


int findFileLength(FILE *inFile, char delimiter){
    char c;
    int length = 0;
    c = fgetc(inFile);
    while(c != EOF){
        if(c != delimiter && c != '\n'){
            length++;
            while((c = fgetc(inFile)) != EOF && c != '\n' && c != delimiter);

        } else {
            c = fgetc(inFile);
        }
    }
    rewind(inFile); // resets the file pointer to the start
    return length;
}


int *parseFileToArray(FILE *inFile, char delimiter, int length){
    int *parsedFile = malloc(sizeof(int) * length);
    char c;
    char *stringInt = malloc(sizeof(char) * 100); // string that is used to combine multiple characters and convert to an integer
    int stringIntP = 0, parsedArrayP = 0; // pointers for our arrays, the first is for the string that determines the integer, the second is for our resulting array
    c = fgetc(inFile);
    while(c != EOF){
        if(c != delimiter && c != '\n'){

            for(;c != '\n' && c != delimiter; (c = fgetc(inFile)) != EOF){
                stringInt[stringIntP++] = c;
            }
            stringIntP = 0;
            parsedFile[parsedArrayP++] = atoi(stringInt); // convert string number to integer value
            memset(stringInt, 0, 100);  // clear the string that builds the integer value from chars
        } else {
            c = fgetc(inFile);
        }
    }
    for(int i = 0; i < length; i++){
        printf("%d ", parsedFile[i]);
    }
    printf("\n");
    fclose(inFile); // close the file after using
    free(stringInt);
    return parsedFile;
}

预期的输出:首先传递未排序的数组。然后对数组进行排序。

实际输出:充满0的数组,程序未完成执行

2 个答案:

答案 0 :(得分:4)

有几个错误。我终于能够找到所有这些,下面是一个可用的版本。

以下是摘要:

  1. 在fork / sort函数中,rpid = fork();在主if语句的上方上方完成。如果if为假,则创建一个僵尸进程。
  2. 共享区域的大小太小。它只是元素的数量,不是 sizeof(int) * number_of_elements
  3. 数据被读取到非共享区域。然后创建共享区域,并且指向实际[非共享]数据的指针丢失。数据没有 复制到共享区域
  4. 在fork / sort函数中,对partition函数的调用是在第一个fork调用之后的 中完成的,因此,两者均被调用父母和孩子,因此他们发生冲突/竞争。
  5. 创建的进程过多,并且没有更多的空闲进程插槽,fork调用失败。

(1)如上所述,如果rpid = fork();语句为假,if (low < high)必须紧随if之后才能防止创建僵尸进程。在下面的第(4)节中对此有更多的了解。


(2)您的共享内存区域太小。在最终打印期间会导致段错误。

这是不正确的:

size_t SHM_SIZE = theArray->length;

它必须是:

size_t SHM_SIZE = sizeof(int) * theArray->length;

(3)您是通过调用theArray非共享内存中创建getArray

它将shm_array设置为从parseFileToArray的呼叫。这是非共享内存中的 still

稍后,要获取共享区域,请执行以下操作:

theArray->shm_array = shmat(theArray->shmid, NULL, 0)

此返回值shm_array现在位于共享内存中,但 data 仍然位于{em> old 值中{1}} [再次,在非共享内存中]。指向实际数据的指针永远是 lost

要解决此问题,您将需要以下内容:

shm_array

当然,当程序正常运行时,最好将int *shm_array; if ((shm_array = shmat(theArray->shmid, NULL, 0)) == (int *) -1) { perror("shmat"); _exit(1); } int *oldptr = theArray->shm_array; for (int idx = 0; idx < theArray->length; ++idx) shm_array[idx] = oldptr[idx]; free(oldptr); theArray->shm_array = shm_array; 调用移至对shm*执行[非共享] malloc的低级函数中,因此您可以省去多余的复制操作。


(4)在您的fork例程中,您正在调用:

shm_array

您要在int partitionIndex = partition(arr, low, high); 之后进行此操作,因此, 父母和fork的孩子都试图进行分区操作,因此会产生冲突。

因此,rpid首先需要:

quickSortFork

(5)您正在创建 way 进程,并且由于进程插槽不可用,if (low < high) { int partitionIndex = partition(arr, low, high); rpid = fork(); 调用开始失败。

这可能是程序似乎挂起的原因。

使用少量数组元素可能无法观察到,但是如果数组足够大(例如100,000个元素),则会发生这种情况


这是一个有效的版本[带有一些额外的调试代码]。

为解决最后的fork问题,我创建了一个fork,它不使用<{>> 使用quickSortStd,而是调用了该名称。

处理过多fork调用问题的一种方法是让fork跟踪递归深度,并在深度足够高后调用非fork版本。

通常,在一定数量后添加更多进程/线程会适得其反,因为在进程之间进行切换的开销使并行性的优势黯然失色。这是一个调整选项。

我在quickSortFork中添加了一个简单的想法,它似乎可行,因此请调整深度限制以适合您的需求。

quickSortFork

答案 1 :(得分:0)

一个直接的问题是

void quickSortFork(MyArray *arr, int low, int high){
    pid_t lpid, rpid;
    rpid = fork();
    if(low < high){
        int partitionIndex = partition(arr,low, high);

父母和孩子都开始划分相同的范围,显然是互相踩脚趾。

让父级进行分区,然后再进行分叉。