Проблемы с отслеживанием ошибки шины / ошибки Seg в C и Linux

#c #linux #pthreads #segmentation-fault #bus-error

#c #linux #pthreads #ошибка сегментации #ошибка шины

Вопрос:

У меня есть программа, которая обрабатывает данные нейронных импульсов, которые передаются в UDP-пакетах по локальной сети.

Моя текущая программа имеет два потока: поток пользовательского интерфейса и рабочий поток. Рабочий поток просто прослушивает пакеты данных, анализирует их и делает их доступными для потока пользовательского интерфейса для отображения и обработки. Моя текущая реализация работает просто отлично. Однако по ряду причин я пытаюсь переписать программу на C , используя объектно-ориентированный подход.

Текущая рабочая программа инициализировала 2-й поток с помощью:

 pthread_t netThread;
net = NetCom::initUdpRx(host,port);
pthread_create(amp;netThread, NULL, getNetSpike, (void *)NULL);
  

Вот getNetSpike функция, которая вызывается новым потоком:

 void *getNetSpike(void *ptr){
    while(true)
    {
        spike_net_t s;
        NetCom::rxSpike(net, amp;s);
        spikeBuff[writeIdx] = s;
        writeIdx = incrementIdx(writeIdx);
        nSpikes =1;
        totalSpikesRead  ;
    }
} 
  

Теперь в моей новой OO-версии программы я настраиваю 2-й поток почти таким же образом:

 void SpikePlot::initNetworkRxThread(){
    pthread_t netThread;
    net = NetCom::initUdpRx(host,port);
    pthread_create(amp;netThread, NULL, networkThreadFunc, this);
}
  

Однако, поскольку pthead_create принимает указатель на функцию void, а не указатель на метод-член объекта, мне нужно было создать эту простую функцию, которая обертывает SpikePlot.getNetworSpikePacket() метод

 void *networkThreadFunc(void *ptr){
        SpikePlot *sp = reinterpret_cast<SpikePlot *>(ptr);

    while(true)
    {
        sp->getNetworkSpikePacket();
    }
}
  

Который затем вызывает getNetworkSpikePacket() метод:

 void SpikePlot::getNetworkSpikePacket(){

    spike_net_t s;
    NetCom::rxSpike(net, amp;s);
    spikeBuff[writeIdx] = s;  // <--- SegFault/BusError occurs on this line
    writeIdx = incrementIdx(writeIdx);
    nSpikes =1;
    totalSpikesRead  ; 
}
  

Код для двух реализаций почти идентичен, но 2-я реализация (версия OO) выходит из строя с ошибкой SegFault или BusError после первого прочитанного пакета. Используя printf , я сузил, какая строка вызывает ошибку:

 spikeBuff[writeIdx] = s;
  

и хоть убейте, я не могу понять, почему это приводит к сбою моей программы.

Что я здесь делаю не так?

Обновление: я определяю spikeBuff как закрытый член класса:

 class SpikePlot{
private:
    static int const MAX_SPIKE_BUFF_SIZE = 50;
    spike_net_t spikeBuff[MAX_SPIKE_BUFF_SIZE];
       ....
}
  

Затем в конструкторе SpikePlot я вызываю:

 bzero(amp;spikeBuff, sizeof(spikeBuff));
  

и установите:

 writeIdx =0;
  

Обновление 2: Хорошо, что-то действительно странное происходит с моими индексными переменными. Чтобы проверить их работоспособность, я изменил getNetworkSpikePacket на:

 void TetrodePlot::getNetworkSpikePacket(){
    printf("Before:writeIdx:%d nspikes:%d totSpike:%dn", writeIdx, nSpikes, totalSpikesRead);

    spike_net_t s;
    NetCom::rxSpike(net, amp;s);
//  spikeBuff[writeIdx] = s;
    writeIdx  ;// = incrementIdx(writeIdx);
//  if (writeIdx>=MAX_SPIKE_BUFF_SIZE)
        // writeIdx = 0;
    nSpikes  = 1;
    totalSpikesRead  = 1; 
    printf("After:writeIdx:%d nspikes:%d totSpike:%dnn", writeIdx, nSpikes, totalSpikesRead);
}
  

И я получаю следующий вывод на консоль:

 Before:writeIdx:0 nspikes:0 totSpike:0
After:writeIdx:1 nspikes:32763 totSpike:2053729378

Before:writeIdx:1 nspikes:32763 totSpike:2053729378
After:writeIdx:1 nspikes:0 totSpike:1

Before:writeIdx:1 nspikes:0 totSpike:1
After:writeIdx:32768 nspikes:32768 totSpike:260289889

Before:writeIdx:32768 nspikes:32768 totSpike:260289889
After:writeIdx:32768 nspikes:32768 totSpike:260289890
  

Этот метод — единственный метод, в котором я обновляю их значения (помимо конструктора, в котором я устанавливаю их равными 0). Все другие виды использования этих переменных доступны только для чтения.

Комментарии:

1. Что и где находится определение spikeBuff?

2. Где writeIdx инициализируется и как incrementIdx выглядит?

3. Следующая попытка: проверьте значение writeIdx перед сбойным доступом. Если что-то пошло не так, это может быть отрицательное или огромное положительное число.

4. Как насчет incrementIdx() ? И где это writeIdx объявлено? Является ли это глобальным?

5. @BjoernD, я пробовал распечатать writeIdx и установить для него значение 0 прямо перед строкой сбоя, и оно всегда имеет правильное значение.

Ответ №1:

Я собираюсь пойти на риск и сказать, что все ваши проблемы вызваны обнулением массива spike_net_t.

В C вы не должны обнулять объекты с элементами, отличными от [вставьте слово для ‘struct-like’ здесь]. т. Е. Если у вас есть объект, который содержит сложный объект (std-строку, вектор и т. Д. И т. Д.), Вы не можете обнулить его, так как это разрушает инициализациюобъекта, выполненного в конструкторе.

Комментарии:

1. хорошо, я не думал, что мне нужно это делать, но я добавил его, когда что-то не работало.

Ответ №2:

Это может быть неправильно, но….

Казалось, вы переместили логику цикла ожидания из метода в статическую оболочку. Поскольку ничто не удерживает рабочий поток открытым, возможно, этот поток завершается после первого ожидания UDP-пакета, поэтому во второй раз sp в статическом методе теперь указывает на экземпляр, который покинул область видимости и был уничтожен?

Можете ли вы попробовать assert(sp) в оболочке, прежде чем пытаться вызвать его getNetworkSpikePacket() ?

Комментарии:

1. не точная проблема, но она указала мне правильное направление для решения ошибки.

2. В чем именно заключалась проблема? Хотите отредактировать вопрос или ответ и показать, что пошло не так?

Ответ №3:

Похоже, что ваш reinterpret_cast может вызывать некоторые проблемы. Когда вы вызываете pthread_create, вы передаете «this», который является SpikePlot *, но внутри networkThreadFunc вы преобразуете его в TetrodePlot *.

Связаны ли SpikePlot и TetrodePlot? Это не указано в том, что вы опубликовали.

Комментарии:

1. упс … извините, в моем коде это не так, я допустил ошибку при копировании / вставке кода, поэтому с тех пор я исправил код в вопросе.

Ответ №4:

Если вы выделяете spikeBuff массив в любом месте, убедитесь, что вы выделяете достаточно места для хранения, поэтому writeIdx это не индекс, выходящий за рамки.

Я бы также проверил, что initNetworkRxThread это вызывается для выделенного экземпляра spikePlot object (а не только для объявленного указателя).

Комментарии:

1. и, предположительно, вы распечатали значение writeIdx непосредственно перед сбоем, и оно находится в пределах массива. Верно?

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