С целью выявления ошибок доступа библиотека Electric Fence запрашивает для каждой выделенной области как минимум две страницы памяти. По умолчанию конец области приходится на конец первой страницы. Выход за пределы области, т.е. обращение ко второй странице, вызывает ошибку сегментации. Если переменная EF_PROTECT_BELOW равна 1, начало области выравнивается по началу второй страницы. В связи с тем, что за один вызов функции malloc() выделяется не менее двух страниц памяти, библиотека Electric Fence способна потреблять достаточно много памяти, поэтому ее рекомендуется использовать только при отладке.
А.2.6. Выбор средств отладки
Мы рассмотрели четыре разных, несовместимых друг с другом средства диагностирования неправильных случаев использования динамической памяти. Ни одно из средств не гарантирует нахождение всех ошибок, но это лучше, чем полное отсутствие проверок. Чтобы облегчить поиск ошибок, выделите код, в котором происходит работа с динамической памятью. Если программа пишется на C++, создайте класс, обрабатывающий все обращения к динамической памяти. При написании программы на языке С постарайтесь минимизировать число функций, в которых выделяется и освобождается память. Тестируя программу, не забывайте о том, что одновременно должно использоваться только одно средство отладки памяти, так как эти средства несовместимы.
Какое же из четырех средств выбрать? Поскольку чаще всего забывают согласовать число операций выделения и освобождения памяти, на начальных этапах разработки лучше применять утилиту mtrace. Она доступна во всех Linux-системах и хорошо себя зарекомендовала. Пройдя данную фазу тестирования, воспользуйтесь утилитой Electric Fence для нахождения неправильных обращений к памяти. Связка двух этих утилит позволяет найти практически все ошибки, связанные с использованием динамической памяти.
А.2.7. Исходный текст программы, работающей с динамической памятью
В листинге А.2 показан исходный текст программы, на примере которой иллюстрируется выделение, освобождение и использование динамической памяти. Описание программы было дано в разделе А.2.1, "Программа для тестирования динамической памяти".
/* Использование функций работы с динамической памятью. */
/* Программе передается один аргумент, определяющий
размер массива. Этот массив состоит из указателей
на (возможно) выделенные буферы памяти.
В процессе работы программы ей можно задавать
следующие команды:
выделение памяти -- а <индекс> <размер_буфера>
освобождение памяти -- d <индекс>
чтение памяти -- r <индекс> <смещение>
запись в память -- w <индекс> <смещение>
выход -- q
Ответственность за соблюдение правил доступа
к динамической памяти лежит на пользователе. */
#ifdef MTRACE
#include <mcheck.h>
#endif /* MTRACE */
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
/* Выделение памяти указанного размера. */
void allocate(char** array, size_t size) {
*array = malloc(size);
}
/* Освобождение памяти. */
void deallocate(char** array) {
free((void*)*array);
}
/* Чтение указанной ячейки памяти. */
void read_from_memory(char* array, int position) {
volatile char character = array[position];
}
/* Запись в указанную ячейку памяти. */
void write_to_memory(char* array, int position) {
array[position] = 'a';
}
int main{int argc, char* argv[]) {
char** array;
unsigned array_size;
char command[32];
unsigned array_index;
char command_letter;
int size_or_position;
int error = 0;
#ifdef MTRACE
mtrace();
#endif /* MTRACE */
if (argc != 2) {
fprintf(stderr, "%s: array-size\n", argv[0]);
return 1;
}
array_size = strtoul(argv[1], 0, 0);
array = (char**)calloc(array_size, sizeof(char*));