Нулевой счетчик ссылок и все еще нет ошибки сегментации - PullRequest
0 голосов
/ 29 мая 2018

У меня есть навыки разработки в Python / C API и распределения памяти, и я ожидал, что следующий код на C ++, встроенный в Python, будет проблематичным и приведет к чему-то вроде ошибки сегментации:

#include <Python.h> 
#include <iostream>

int main(){
  Py_Initialize();

  PyObject* pythonList = Py_BuildValue("[i i]",1,2);

  Py_DECREF(pythonList); // I checked with Py_REFCNT(pythonList) that reference count is now 0

  PyList_Check(pythonList); // hence, I was expecting here something like a segmentation fault, but this does not happen...

  std::cout << "Ok, goodbye" << std::endl;
  return 0;
}

Однако ничегоплохое происходит во время выполнения (и отображается «Хорошо, до свидания»).

  • Является ли этот код действительно нормальным, несмотря на доступ (в PyList_Check(pythonList);) к PyObject, который былed к нулю?

  • Или этот код неправильный , и было просто везением, что здесь нет ошибки сегментации (почему?)?

1 Ответ

0 голосов
/ 29 мая 2018

Код неправильный - вызов Py_DECREF() освободил объект, что означает, что pythonList является висячим указателем, поэтому, когда PyList_Check() пытается задержать этот указатель, неопределенныйвызывается поведение.

Что касается того, почему это не привело к ошибке сегментации, формальный ответ состоит в том, что неопределенное поведение не должно приводить к каким-либо конкретным наблюдаемым последствиям (таким как ошибка сегментации).Неопределенное поведение может привести к тому, что программа сделает буквально что-нибудь , и программист обязан избегать его вызова.

На практике есть более удовлетворительное объяснение, хотя: на большинстве популярныхВ системах ошибки сегментации возникают, когда программа пытается получить доступ к странице виртуальной памяти, которая не сопоставлена ​​с любым возрастом физической памяти, или сопоставлена ​​с физической страницей, к которой у программы нет доступа.Таким образом, если вы установили pythonList для указания некоторой случайной / недействительной ячейки памяти, а затем попытались разыменовать ее, вы, вероятно, получите ошибку сегментации.Однако pythonList не указывает на случайную ячейку памяти, он указывает на ячейку памяти, где находился действительный объект списка Python (вплоть до момента, когда Py_DECREF() освободил его).«Освобождение» этой памяти просто означает, что структура данных кучи процесса теперь включает эту часть памяти в свой «список свободной памяти» в качестве памяти, которую можно использовать повторно в следующий раз, когда какая-то другая часть процесса захочет выделить память.Он не требует сообщения MMU о том, что эта область памяти теперь недоступна (и вообще говоря, это невозможно, поскольку MMU проверяет действительность памяти страниц , а не отдельных байтов, и это довольно частоиметь страницу памяти, которая содержит как действительные объекты, так и области освобожденной памяти, смешанные вместе).Таким образом, система проверки работоспособности MMU / segmentation-fault не будет отлавливать ваше чтение освобожденной памяти (valgrind может отловить это, но за счет вашей программы, работающей в 10-100 раз медленнее, чем обычно).

...