Ответ 1
Я смог воспроизвести вашу проблему на моей малине Pi 1, модель B, запустив ваш script и подключив перемычку между землей и GPIO27 для имитации красных нажатий кнопок. (Это штыри 25 и 13 на моей конкретной модели Pi.)
Интерпретатор python сбой с ошибкой сегментации в потоке, предназначенном для опроса событий GPIO после того, как red
возвращается с обработки нажатия кнопки. Посмотрев на реализацию модуля Python GPIO
, мне ясно, что из-за обратного вызова обработчика события небезопасно вызывать remove_event_detect
, и это вызывает сбой. В частности, удаление обработчика событий, в то время как этот обработчик событий в настоящее время работает, может привести к повреждению памяти, что приведет к сбоям (как вы видели) или другим странным поведением.
Я подозреваю, что вы удаляете и повторно добавляете обработчики событий, потому что вы обеспокоены получением обратного вызова в то время, когда вы передаете нажатие кнопки. Нет необходимости делать это. Модуль GPIO объединяет один поток опроса для мониторинга событий GPIO и будет ожидать возврата одного обратного вызова перед вызовом другого, независимо от количества просматриваемых событий GPIO.
Я предлагаю вам просто звонить на add_event_detect
при запуске script и никогда не удалять обратные вызовы. Простое удаление add_events
и remove_events
(и их invocations) из вашего script исправит проблему.
Если вас интересует подробная информация о проблеме в модуле GPIO
, вы можете ознакомиться с исходным кодом C для этого модуля. Взгляните на run_callbacks
и remove_callbacks
в файле RPi.GPIO-0.6.2/source/event_gpio.c
. Обратите внимание, что обе эти функции используют глобальную цепочку узлов struct callback
. run_callbacks
выполняет цепочку обратного вызова, захватывая один node, вызывая обратный вызов, а затем следуя этой ссылке node для следующего обратного вызова в цепочке. remove_callbacks
будет проходить одну и ту же цепочку обратного вызова и освободить память, связанную с обратными вызовами на конкретном выводе GPIO. Если remove_callbacks
вызывается в середине run_callbacks
, node, в настоящее время удерживаемый run_callbacks
, может быть освобожден (и его память может быть повторно использована и перезаписана) до того, как будет следовать указатель на следующий node.
Причина, по которой вы видите эту проблему только для красной кнопки, скорее всего, связана с порядком вызовов add_event_detect
и remove_event_detect
, заставляет память, ранее использованную обратным вызовом node, для красной кнопки, которая будет исправлена для некоторых другая цель и перезаписанная ранее память, используемая в обратном вызове зеленой кнопки node, аналогично исправлена. Однако будьте уверены, что проблема существует для обеих кнопок - просто удача в том, что память, связанная с обратным вызовом зеленой кнопки, не изменяется до того, как будет следовать указатель на следующий обратный вызов node.
В целом, существует проблема отсутствия синхронизации потоков вокруг использования цепочки обратного вызова в модуле GPIO в целом, и я подозреваю, что подобные проблемы могут возникнуть, если вызываются remove_event_detect
или add_event_detect
во время работы обработчика событий, даже если события удалены из другого потока! Я бы предположил, что автор модуля RPi.GPIO
должен использовать некоторую синхронизацию, чтобы гарантировать, что цепочка обратного вызова не может быть изменена при выполнении обратных вызовов. (Возможно, помимо проверки того, изменяется ли цепочка в самой теме опроса, pthread_mutex_lock
и pthread_mutex_unlock
могут использоваться для предотвращения изменения других цепочек обратного вызова, когда он используется в потоке опроса.)
К сожалению, на данный момент это не так, и по этой причине я предлагаю вам полностью не называть remove_event_detect
, если вы можете его избежать.