Сортировка с общей памятью с дочерними процессами - PullRequest
0 голосов
/ 30 января 2019

Я пытаюсь использовать дочерние процессы в моей быстрой сортировке, чтобы левая половина сортировалась у одного ребенка, а правая половина - у другого.У меня это работало до реализации shmget, но теперь я считаю, что я повреждаю массив где-то, потому что все мои значения становятся нулевыми после печати массива.Извините, если я где-то совершаю какую-то глупую ошибку, я пытаюсь научиться использовать fork и shmget, но столкнулся с некоторыми проблемами.Я пытаюсь взять текстовый файл в качестве аргумента командной строки и задал такой разделитель, как ';'Я должен удалить разделитель и определить числа между ними, поместить их в массив и отсортировать их с помощью дочерних процессов.У меня работает синтаксический анализ и работает быстрая сортировка, но теперь, когда я пытаюсь реализовать общую память, я столкнулся с некоторыми проблемами.

Спасибо

Я посмотрел несколькоразличные примеры, но тот, на котором это в основном основано, - пример geeksforgeeks с сортировкой слиянием с помощью fork.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;
}

Ожидаемый результат: массив передается в несортированном в первую очередь.Затем массив отсортирован.

Фактический вывод: массив заполнен нулями, и программа не завершает выполнение

Ответы [ 2 ]

0 голосов
/ 30 января 2019

Есть несколько ошибок.Я смог [наконец-то] найти их все, и рабочая версия приведена ниже.

Вот краткое изложение:

  1. В функции fork / sort, rpid = fork(); сделано выше основной if оператор.Если значение if равно false, создается процесс зомби.
  2. Размер общей области слишком мал.Это только количество элементов, а не sizeof(int) * number_of_elements
  3. Данные считываются в общую область.Затем создается общая область, и указатель на фактические [не общие] данные теряется. нет копий данных в общую область
  4. В функции fork / sort вызов функции partition выполняется после первого fork вызов, поэтому он вызывается и родителем и потомком, поэтому они конфликтуют / участвуют в гонке.
  5. Есть способ слишком много создаваемых процессов и некоторые изfork вызовы не выполняются, так как свободных слотов процесса больше нет.

(1) Как я уже упоминал выше, rpid = fork(); нужно идти после if (low < high) допредотвратить создание процесса зомби, если оператор if имеет значение false.Подробнее об этом ниже в разделе (4).


(2) Область вашей общей памяти слишком мала.Это вызывает segfault во время окончательной печати. ​​

Это неверно:

size_t SHM_SIZE = theArray->length;

Это должно быть:

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

(3) Вы создаете theArray в нераспределенной памяти из вызова на getArray.

Он устанавливает shm_array из вызова на parseFileToArray.Это все еще в общей памяти.

Позже, чтобы получить общую область, вы делаете:

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

Это возвращаемое значение shm_array теперьв общей памяти, но данные по-прежнему находятся в старом значении 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* в низкоуровневую функцию, которая выполняет [non-shared] malloc для shm_array, чтобы можно было исключить дополнительную операцию копирования.


(4) В своей процедуре форка вы звоните:

int partitionIndex = partition(arr, low, high);

Вы делаете это после fork, поэтому оба parent и rpid child пытаются выполнить операцию разбиения, поэтому они конфликтуют.

Итак, quickSortFork необходимо начать с:

if (low < high) {
    int partitionIndex = partition(arr, low, high);

    rpid = fork();

(5) Вы создаете путь слишком много процессов, и вызовы fork начинают сбой из-за недоступных слотов процессов.

Возможно, поэтому программазависание.

Это, вероятно, не будет наблюдаться при небольшом количестве элементов массива, но произойдет, если массив достаточно велик (например, 100,000 элементов)


Вот рабочая версия [с некоторым дополнительным отладочным кодом].

Чтобы решить последнюю проблему fork, я создал quickSortStd, который выполняет не используйте fork и вызывайте его вместо этого.

Один из способов решения проблемы слишком большого количества вызовов fork состоит в том, чтобы quickSortFork отслеживал глубину рекурсии и вызывалверсия fork после того, как глубина становится достаточно высокой.

Как правило, добавление большего количества процессов / потоков после определенного числа становится контрпродуктивным, поскольку издержки переключения между процессами затмевают преимущества параллелизма.Это опция настройки.

Я добавил простую версию этой идеи в quickSortFork, и она, кажется, работает, поэтому отрегулируйте предел глубины в соответствии с вашими потребностями.

#include <unistd.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

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

int findFileLength(FILE * inFile, char delimiter);
int *parseFileToArray(FILE * inFile, char delimiter, int length);
int partition(MyArray * arr, int low, int high);
void quickSortFork(MyArray * arr, int low, int high);
void quickSortStd(MyArray * arr, int low, int high);
void swap(MyArray * arr, int a, int b);

void
prtnice(const char *who,int *arr,int length)
{
    int hang = 0;

    printf("%s: LENGTH %d\n",who,length);

    for (int i = 0; i < length; i++) {
        if (hang == 0)
            printf("%s/%8.8d:",who,i);

        printf(" %d", arr[i]);

        ++hang;
        if (hang == 10) {
            printf("\n");
            hang = 0;
        }
    }
    printf("\n");
}

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

    prtnice("INP",parsedFile,length);

    fclose(inFile);                     // close the file after using
    free(stringInt);

    return parsedFile;
}

void
printArray(const char *who,MyArray * arr, int length)
{
    prtnice(who,arr->shm_array,length);
}

void
quickSortFork(MyArray * arr, int low, int high)
{
    pid_t lpid,
     rpid;

    static int depth = 0;
    if (depth++ > 5) {
        quickSortStd(arr,low,high);
        --depth;
        return;
    }

    printf("Fork: ENTER low=%d high=%d\n",low,high);

    if (low < high) {
        int partitionIndex = partition(arr, low, high);

        rpid = fork();
        if (rpid < 0) {
            perror("Right child not created.\n");
            exit(-1);
        }

        if (rpid == 0) {
            printf("I am the right child!\tMy process id: %d\n", getpid());
            quickSortFork(arr, partitionIndex + 1, high);
            exit(EXIT_SUCCESS);
        }

        lpid = fork();
        if (lpid < 0) {
            perror("Left child not created.\n");
            exit(-1);
        }

        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;
        printf("Fork: WAIT rpid=%d\n",rpid);
        waitpid(rpid, &status, 0);
        printf("Fork: WAIT lpid=%d\n",lpid);
        waitpid(lpid, &status, 0);
    }

    --depth;

    printf("Fork: EXIT low=%d high=%d\n",low,high);
}

void
quickSortStd(MyArray * arr, int low, int high)
{
    pid_t lpid,
     rpid;

    printf("Std: ENTER low=%d high=%d\n",low,high);

    if (low < high) {
        int partitionIndex = partition(arr, low, high);
        quickSortStd(arr, partitionIndex + 1, high);
        quickSortStd(arr, low, partitionIndex - 1);
    }

    printf("Std: EXIT low=%d high=%d\n",low,high);
}

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

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

#if 0
    size_t SHM_SIZE = theArray->length;
#else
    size_t SHM_SIZE = sizeof(int) * theArray->length;
#endif

    setlinebuf(stdout);

    theArray->key = IPC_PRIVATE;

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

    printArray("BEF",theArray, theArray->length);

    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;

    printArray("SHM",theArray, theArray->length);
#if 1
    quickSortFork(theArray, 0, theArray->length - 1);
#else
    quickSortStd(theArray, 0, theArray->length - 1);
#endif
    printArray("AFT",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;
}
0 голосов
/ 30 января 2019

Непосредственная проблема заключается в том, что в

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

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

Пусть родительский компонент выполняет разбиение, и только тогдавилка.

...