Передать указатель на гарантированно обнуленную память - PullRequest
2 голосов
/ 15 августа 2010

Мне нужно обнулить записи разных размеров в файле. Для этого я в настоящее время размещаю фиктивные записи, memset обнуляя их и передавая их функции записи.

Существует ли область, которая гарантированно всегда обнуляется (и имеет достаточно большой размер), на которую я могу вместо этого указать, устраняя необходимость повторного выделения и обнуления памяти?

Ответы [ 9 ]

8 голосов
/ 15 августа 2010

Если есть разумная верхняя граница для размера записи, выделите глобальную переменную только для чтения, содержащую нули.(Поскольку это объект статической длительности, он автоматически инициализируется нулем.)

const unsigned char zero_filled_buffer[MAX_RECORD_SIZE]; /*at file scope*/

Если функция записи представляет собой C fwrite или POSIX write или другую функцию, вы можете (должны, дляwrite) вызывайте его в цикле, чтобы буфер не должен был быть таким же большим, как самая большая запись, настолько же большим, как самый большой кусок, который вы пишете за один раз.

Такая переменная будет принимать нольпространство в вашем исполняемом файле под типичными размещенными реализациями. ДОБАВЛЕНО : обратите внимание, что в отношении стандарта C приведенное выше объявление в точности эквивалентно const unsigned char zero_filled_buffer[MAX_RECORD_SIZE] = {0};, однако некоторые компиляторы (включая gcc) включают нули в исполняемый файл, если вы явно добавили = {0}, нонет, если вы не включите инициализатор.

Интеллектуальный загрузчик программ в системе с виртуальной памятью может использовать систему виртуальной памяти для использования единой совместно используемой доступной только для чтения страницы физической памяти ОЗУ для всех такихобъекты;Я не знаю, делают ли это на практике. ДОБАВЛЕНО : Например, Linux (Debian lenny amd64) этого не делает.

Альтернативный подход POSIX - mmap файл и вызов memset для буферов с нулевым заполнением.

3 голосов
/ 15 августа 2010

См. calloc .

Функция calloc() должна выделять неиспользуемое пространство для массива nelem элементов, каждый из которых имеет размер elsize в байтах.Пробел должен быть инициализирован для всех битов 0.

В качестве альтернативы (я не пробовал этого), если вы вообще не хотите какого-либо выделения, вы могли бы open и / или mmap /dev/zero и прочитайте из него блоки record_size и запишите их в файл, в котором вы перезаписываете записи.

2 голосов
/ 15 августа 2010

По крайней мере, если Linux выделит память через mmap(), вы получите заполненный нулями буфер. Недостатком является то, что вы не можете просто выделить нужную вам память, а только кратные размеру страницы

#include <unistd.h>
long sz = sysconf(_SC_PAGESIZE);
1 голос
/ 15 августа 2010

Как уже отмечалось, вам нужно выделить один раз самый большой регион, который вам когда-либо понадобится; Вы можете передать это в любое время, когда вам нужна область такого размера или меньше.

В большинстве реализаций отсутствует часть адресного пространства, которая не отображается в ОЗУ, но при чтении будет безобидно читать ноль. Такое может быть приятно, но я не знаю ни одного.

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

1 голос
/ 15 августа 2010

Если вы хотите большую область памяти, которая всегда обнуляется, вы должны выделить ее самостоятельно и установить в ноль.Не обойтись без этого, но вы должны сделать это только один раз.Удостоверьтесь, что он по крайней мере такой же большой, как самый большой объем обнуляемой памяти, который вам понадобится в любой момент времени.

Затем, когда вам нужно передать указатель на обнуленную память, вы можете передать указатель в этомблок, который вы выделили.

1 голос
/ 15 августа 2010

Да, просто выделите достаточно большой блок для любой из этих записей и обнулите его один раз.Каждый раз передавайте адрес этого блока функции записи с размером записи, который вы фактически хотите обнулить.Передача буфера для записи не приводит к истечению срока его действия или чего-либо еще.Имейте в виду, запись также не освобождает буфер, который вы передаете;это зависит от вас.

0 голосов
/ 16 августа 2010

Вот гарантированная работа, возможность выполнения (скомпилируйте с gcc zeroed_mem_region.c -Wall -std=gnu99):

#include <sys/mman.h>
#include <assert.h>
#include <stdio.h>

size_t const zeroed_size = 512;
char const *zeroed;

int main()
{
    zeroed = mmap(
            NULL,
            zeroed_size,
            PROT_READ,
            MAP_PRIVATE|MAP_ANONYMOUS,
            -1,
            0);
    printf("zeroed region at %p\n", zeroed);
    for (size_t i = 0; i < zeroed_size; ++i) {
        assert(zeroed[i] == 0);
    }
    printf("testing for writability\n");
    ((char *)zeroed)[0] = 1;
    return 0;
}

Обратите внимание, что для тестирования обнулено char const *, на самом деле это будет void const *.

Плюсы

  • Предотвращает использование распределителя malloc
  • Гарантируемый регион не записывается (генерирует SIGSEGV)
  • Быстрее, чем использование malloc
  • Не вставлять дерьмо в исполняемый файл
  • Нет необходимости в шаге memset (отметьте mmap(2))

Минусы

  • Специфично для Unix / Linux (анонимные сопоставления начиная с Linux 2.4)
  • Уменьшает возможность оптимизации компилятора (хотя, очевидно, в этой области не существует)
0 голосов
/ 15 августа 2010

Скорость функции записи будет на * порядка ниже, чем memset.

Профилируйте ее!

* даже с флеш-накопителями

0 голосов
/ 15 августа 2010

получить системный размер страницы с помощью API системной информации (я просто не могу вспомнить точное имя), выделить 1 страницу памяти, установить ее на ноль, записывать ее последовательно снова и снова

...