Использование глобального массива указателей для очистки перед выходом в C - PullRequest
0 голосов
/ 06 января 2019

У меня есть небольшая программа, которая содержит переменную, которая мне нужна для malloc:

char **v;
v = (char**)malloc(sizeof(char *) * MAX_EVENTS);
    for (int i = 0; i < MAX_EVENTS; i++)
        v[i] = (char *)malloc(MAX_NAME_SIZE);

Чтобы сделать Valgrind счастливым, чтобы избежать утечек памяти, я настроил обработчики для сигналов завершения. Этот обработчик просто освободит это распределение перед выходом, а также завершит дочерние процессы.

 static void term_handler() {
    if (v != NULL) {
        for (int i = 0; i < MAX_EVENTS; i++) {
            if (v[i] != NULL)
                free(v[i]);
        }
        free(v);
    }
    for (int i = 0; i < MAX_PROCS; i++)
        if (children[i])
            kill(children[i], SIGTERM);
    exit(EXIT_SUCCESS);
}

Чтобы получить доступ к v из обработчика, я поставил его как глобальную переменную. children является статическим массивом pid_t children[MAX_PROCS];, но потенциально может быть также неправильным .

Какой самый чистый способ получить доступ к этим выделениям из обработчика? Наличие глобальных переменных не рекомендуется, но также нет утечек памяти и неправильно завершенных программ. Должен ли я сохранить массив указателей на мои выделения в качестве глобальной переменной? Или я должен просто избегать обработки неожиданных сигналов?

1 Ответ

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

Обработчики сигналов хитры в том, что они вызываются асинхронно, и поэтому существует только небольшой набор вызовов функций , которые можно безопасно вызывать из обработчика сигнала. В частности, выделение или освобождение памяти из обработчика сигнала - это нет-нет (как и вызов exit () !), Поэтому не делайте этого.

Однако, если вы хотите убедиться, что память освобождается (*), вы можете сделать это, попросив обработчик сигналов «сообщить» основному потоку вашей программы, что пора ей выходить. Затем основной поток может выйти из цикла обработки событий, освободить память и выполнить любую другую работу по очистке, которую он обычно выполняет перед выходом.

Итак, возникает вопрос, как обработчик сигнала может безопасно сказать основному потоку выполнить контролируемый / изящный выход?

Если основной поток выполняет цикл обработки событий, который выполняется по фиксированному расписанию (например, каждые очень много миллисекунд), это может быть так же просто, как объявление глобальной переменной (например, volatile bool pleaseQuitNow = false;, которую основной поток проверяет на каждой итерации его цикла событий, и с помощью обработчика сигнала установите эту переменную в другое значение. Затем основной поток увидит измененную переменную на своей следующей итерации и ответит, выйдя из цикла событий.

Если цикл обработки событий основного потока основан на событиях, с другой стороны (например, он заблокирован внутри select () или poll () или аналогичный, и вызов выигран не возвращаться в течение некоторого неопределенного промежутка времени), то альтернативным способом пробуждения основного потока будет создание pipe () или socketpair () при запуске программы и попросите главный поток посмотреть один из двух файловых дескрипторов для получения статуса готовности к чтению. Затем, когда обработчик сигнала запускается, он может отправить () байт в другой файловый дескриптор, что заставит первый файловый дескриптор указывать состояние готовности к чтению. Основной поток может ответить на это состояние готовности к чтению, разорвав цикл обработки событий и выйдя изящно.

В дополнение к тому, что вы избегаете асинхронных небезопасных вызовов, преимущество этого способа заключается в том, что у вас есть только один путь выключения / очистки для тестирования / отладки / обслуживания вместо двух.

(*) Конечно, в любой современной ОС память все равно будет освобождена с помощью процедур очистки процессов ОС; но valgrind будет жаловаться на утечки памяти, поэтому лучше по возможности освободить память вручную, если только так, чтобы вы могли использовать valgrind для обнаружения «настоящих» утечек памяти без необходимости каждый раз сортировать кучу ложных срабатываний.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...