Ошибка сегментации с эполлом - PullRequest
0 голосов
/ 14 января 2019

Я пытаюсь создать программу, которая делает следующее:

В качестве аргументов командной строки он получает пути к файлам FIFO. Предполагается, что мониторинг этих FIFO осуществляется с помощью epoll API. Через FIFO гарантируется, что будут отправлены только числа с плавающей точкой. Выходные данные программы должны быть суммой чисел, отправленных через каждый FIFO (программа останавливается, когда сторона записи закрывает все файлы).

Прежде чем я начну с реального кода, вот макрос и функция, которые вы увидите во всей программе:

#define iAssert(cond, msg) crash(cond, msg, __LINE__)
void crash(bool cond, char * msg, int line){
    if(!cond){
        perror(msg);
        fprintf(stderr, "at line %d\n", line);
        exit(EXIT_FAILURE);
    }    
}

Это просто простой механизм утверждения, не имеющий отношения к самой проблеме.

В любом случае, сначала я выбираю количество FIFO, переданных через аргументы командной строки, и создаю дескриптор файла epoll:

int numFifos = argc - 1;
int epollFileDesc = epoll_create(1);
iAssert(-1 != epollFileDesc, "epoll_create");

Затем я создаю массив дескрипторов файлов fifo и массив сумм, которые в следующем цикле я инициализирую нулем.

 int * fifoFileDescriptors = malloc(numFifos * sizeof(int));
 iAssert(NULL != fifoFileDescriptors, "malloc1");

 float * localSums = malloc(numFifos * sizeof(float));
 iAssert(NULL != localSums, "malloc 2");

Пока все хорошо, я думаю. Следующий цикл, кроме инициализации массива сумм, открывает FIFO, заполняет предыдущий массив файловых дескрипторов и регистрирует события.

for(int i = 0; i<numFifos; i++){
        localSums[i] = 0.f;

        int thisFd = open(argv[i+1], O_RDONLY | O_NONBLOCK);
        iAssert(-1 != thisFd, "open");

        fifoFileDescriptors[i] = thisFd;

        FILE * thisFs = fdopen(thisFd, "r");
        iAssert(NULL != thisFs, "fdopen");
        DataPass registerThis;

        registerThis.fifoIndex = i;
        registerThis.file = thisFs;

        struct epoll_event thisEvent;
        thisEvent.events = 0;
        thisEvent.events |= EPOLLIN;
        thisEvent.data.ptr = (void *)&registerThis;

        iAssert(-1 != epoll_ctl(epollFileDesc, EPOLL_CTL_ADD, thisFd, &thisEvent), "epoll_ctl");
    }

Структура DataPass выглядит следующим образом:

typedef struct{
    int fifoIndex;
    FILE * file;
}DataPass;

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

После этого я отслеживаю изменения:

int nOpen = numFifos;

    struct epoll_event events[MAX_EVENTS];

    while(nOpen){
        int active = epoll_wait(epollFileDesc, events, MAX_EVENTS, -1); 
        iAssert(-1 != active, "epoll_wait");

        for(int i = 0; i<active; i++){
            struct epoll_event thisEvent = events[i];


            if(thisEvent.events & EPOLLIN){
                DataPass * thisData = (DataPass *)thisEvent.data.ptr;
                //fifo with index thisData->fifoIndex has sent a message
                float x;
                while(1 == fscanf(thisData->file, "%f", &x)){
                    localSums[thisData->fifoIndex] += x;
                }

            }else if (thisEvent.events & (EPOLLERR | EPOLLHUP)){
                //need to close this connection
                DataPass * thisData = (DataPass *)thisEvent.data.ptr;
                iAssert(-1 != epoll_ctl(epollFileDesc, EPOLL_CTL_DEL, fifoFileDescriptors[thisData->fifoIndex], NULL), "epoll_ctl del");
                fclose(thisData->file);
                close(fifoFileDescriptors[thisData->fifoIndex]);
                nOpen--;
            }
        }
    }

Макрос MAX_EVENTS определен как 4.

После запуска этого (и использования сторонней программы для создания fifo и отправки значений через них) я получаю ошибку сегментации, которую мне удалось отследить до части fscanf. Несмотря на то, что я выследил это, я все еще не понимаю, почему это вызывает.

Есть идеи?

Спасибо.

Ответы [ 2 ]

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

Эта строка:

DataPass registerThis;

Объявляет структуру в стеке. Как только вы снова выполните цикл, эта память будет перезаписана с новой структурой. Тогда вы получите указатель на него:

thisEvent.data.ptr = (void *)&registerThis;

После окончания цикла этот указатель не указывает на что-либо значимое.

Чтобы это исправить, вам нужно выделить эту структуру в куче.

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

Вы вызываете неопределенное поведение, сохраняя указатель на локальную переменную после срока действия стека, в котором она существует

for(int i = 0; i<numFifos; i++){
        DataPass registerThis;
        registerThis.file = thisFs;
        thisEvent.data.ptr = (void *)&registerThis;
}

Не экспортируйте указатели на локальные переменные и не пытайтесь использовать их, когда они больше не существуют. Выделите свое хранилище более долгим способом.

...