#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 прямо перед его использованием, и возникает та же проблема