inarray-allocator

Библиотека в составе которой расположены исходники альтернативного аллокатора памяти в пределах ранее созданного массива.

Сборка

Для сборки необходимо установленные пакеты CUnit, CUnit-devel, cmake, gcc. Приведу на примере сборки CentOS 7

Последовательность сборки

yum install CUnit CUnit-devel cmake gcc git
git clone https://github.com/bayrepo/arrayallocator.git
cd arrayallocator/build
cmake ..
make
make unit-test
make install

Установка из пакета

Последовательность установки CentOS 7

wget http://download.opensuse.org/repositories/home:/bayrepo/CentOS_7/home:bayrepo.repo -O /etc/yum.repos.d/home:bayrepo.repo
yum install inarray-allocator inarray-allocator-devel -y

установка завершена

Последовательность установки CentOS 6

wget http://download.opensuse.org/repositories/home:/bayrepo/CentOS_6/home:bayrepo.repo -O /etc/yum.repos.d/home:bayrepo.repo
yum install inarray-allocator inarray-allocator-devel -y

установка завершена

Список функций

brp_malloc_init

int brp_malloc_init(void *array_ptr, long array_size, char *_allocalg) или int brp_malloc_init(void *array_ptr, long array_size)

Инициализация массива array_ptr и размером array_size, выделение памяти в массиве производить по алгоритму _allocalg. _allocalg на текущий момент может принимать только "SBRK". Вызов функции brp_malloc_init(void *array_ptr, long array_size) по умолчанию использует SBRK. Результат: 0 - успешно инициализировано, 1 - ошибка инициализации.

Пример:

brp_malloc_init

char storage[1000] = {0};
if (brp_malloc_init((void *)storage, 1000)){
    printf("Error\n");
    exit(-1);
}

brp_malloc

void *brp_malloc(void *storage, long size)

Аналог malloc, только в качестве первого параметра передается массив, где происходить выделение памяти. Возвращает указатель на выделенный участок или NULL.

Пример:

brp_malloc

char storage[1000] = {0};
if (brp_malloc_init((void *)storage, 1000)){
    printf("Error\n");
    exit(-1);
}

char *data = brp_malloc((void *)storage, 100);
if (!data){
    printf("Not enough of memory\n");
    exit(-1);
}

brp_free

void brp_free(void *storage, void *ptr)

Аналог классического free, только в качестве первого параметра передается массив, в котором происходило выделение памяти с помощью brp_malloc или brp_calloc.

Пример:

brp_malloc

char storage[1000] = {0};
if (brp_malloc_init((void *)storage, 1000)){
    printf("Error\n");
    exit(-1);
}

char *data = brp_malloc((void *)storage, 100);
if (!data){
    printf("Not enough of memory\n");
    exit(-1);
}

brp_free((void *)storage, data);

brp_realloc

void *brp_realloc(void *storage, void *ptr, long new_size)

Аналог realloc, берет выделенный ранее участок ptr, создает в storage новый участок и копирует в новое место содержимое ptr, если ptr = NULL, то копирование не происходит, создается новый участок памяти с обнуленным содержимым. Если new_size меньше размера ptr, то содержимое ptr урезается до new_size и копируется в новое место. Функция возвращает указатель на новый блок памяти или NULL, если произошла ошибка или не хватило места.

brp_calloc

void *brp_calloc(void *storage, int elem_numbers, int elem_size)

Аналог calloc, выделает elem_numbers * elem_size байт памяти, но в отличии от malloc, очищает память нулями. Возвращает NULL, если не удалось выделить участок памяти.

brp_free_null

void brp_free_null(void *storage, void *ptr)

Освобождение участка памяти ptr, с предварительным обнулением участка памяти

brp_realloc_null

void *brp_realloc_null(void *storage, void *ptr, long new_size)

realloc с заполнением нулями старого участка памяти ptr.

Группа отладочных функций

  • void *brp_get_next_region_info(void *storage, void *ptr, void **info) - получение следующего указателя за ptrю
  • void brp_return_allocation_picture(void *storage, char *buffer_for_picture, long size) - вывод в буфер информации о статусе и количестве участков памяти: F - свободный участок, U - занятый. Результат выглядит так: UUFFUUFUUUю
  • void *brp_get_next_elem(void *storage) - получить указатель, указывающий на участок последнего выделения памяти.
  • long brp_get_free(void *storage) - размер свободного места
  • long brp_get_inuse(void *storage) - размер занятого места
  • void brp_return_allocation_stdout(void *storage) - вывод полной информации о выделенных и свободных участках памяти во всем массиве. Информация выводится в виде:

    1. STATUS U SIZE 24 ST 40 DC 11
    2. STATUS U SIZE 7 ST 40 DC 11
    3. STATUS U SIZE 24 ST 40 DC 11
    4. STATUS U SIZE 8 ST 40 DC 11
    5. STATUS U SIZE 24 ST 40 DC 11

brp_make_pointers_table

int brp_make_pointers_table(void *storage, int pointers_number)

Функция создания таблицы указателей на участки памяти. Первый параметр - это массив куда будет сохраняться таблица, второй параметр(pointers_number) - максимальное число хранимых указателей. Возвращает 0 - когда таблица создана и 1 - когда нет. Таблица и указатели необходимы при работе нескольких процессов с одной общей памятью, например. Далее будет разобрана программа пример с использованием этой функции.

brp_get_pointer_with_number

void *brp_get_pointer_with_number(void *storage, int pointer_number)

Получить указатель с номером pointer_number из таблицы указателей, если такая есть и, номер не более максимального числа указателя. или NULL, если указателя нет

brp_set_pointer_to_number

void brp_set_pointer_to_number(void *storage, int pointer_number, void *value)

Записать значение указателя value в таблицу указателей под номером pointer_number. Пример использования ниже.

brp_strdup

char *brp_strdup(void *storage, const char *s)

Выделить память в хранилище storage для строки ''s'' и скопировать строку в выделенную область. Если не удалось выделить то вернуть NULL.

brp_nstrdup

char *brp_strndup(void *storage, const char *s, size_t n)

Выделить память в хранилище storage для строки ''s'', но не более ''n'' символов, и скопировать строку в выделенную область. Если не удалось выделить то вернуть NULL.

Функции и расширения

Библиотека и исходники снабжены доработанными функциями uthash библиотеки https://troydhanson.github.io/uthash/ с небольшими доработками, что вся служебная и дополнительная информация о данных для utstring, utarray, utringbuffer, uthash, utlist выделяется в указанном хранилище.

Использование

Библиотеку можно использовать:

  1. для обмена массивов данных(посредством utstring, utarray, utringbuffer, uthash, utlist) через общую память
  2. для работы в рамках одного процесса, но с ограничениями выделяемой памяти

Первый случай делает удобный обмен данными с использованием хэш-таблиц, динамических массивов, списков и прочее.

Пример 1

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

#include <stdio.h>

#include <stdlib.h>
#include <sys/mman.h>

#include <string.h>
#include <unistd.h>

#include <pthread.h>
#include "bayrepomalloc.h"

#include "uthash.h"

#define ARRAY_SIZE 100000

размер хэш массива

//Структура, хранящаяся в хэш-таблице
typedef struct _data_struct {
    char name[50]; //имя записи
    int counter;           // счетчик обращений
    UT_hash_handle hh;
} data_struct;

void* create_shared_memory(size_t size) {
    int protection = PROT_READ | PROT_WRITE;
    int visibility = MAP_ANONYMOUS | MAP_SHARED;
    return mmap(NULL, size, protection, visibility, 0, 0);
}

сама структура хранения данных и функция выделения разделяемой памяти

int main() {

    data_struct *storage = NULL; //будущий массив структур, обязательно должен быть обнулен
    pthread_mutex_t * pmutex = NULL; //мьютекс для синхронизации процессов будет расположен в этой же общей памяти
    pthread_mutexattr_t attrmutex;

    void *hash_array = NULL;

    /*

     * выделяем общую память разером под будущий массив с hash таблицей + область для mutex для
     * синхронизации
     */
    void* shmem = create_shared_memory(ARRAY_SIZE + sizeof(pthread_mutex_t));
    if (!shmem) {
        fprintf(stderr, "Error shared memeory allocation\n");
        exit(-1);
    }

    /*

     * Инициализируем мьютекс и положим его в начало общей памяти
     */
    pmutex = (pthread_mutex_t *) shmem;

    pthread_mutexattr_init(&attrmutex);
    pthread_mutexattr_setpshared(&attrmutex, PTHREAD_PROCESS_SHARED);

    pthread_mutex_init(pmutex, &attrmutex);

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

    /*

     * Массив хэш таблицы будет располагаться сразу после мьютекса
     */
    hash_array = (void *) ((char *) shmem + sizeof(pthread_mutex_t));
    pthread_mutex_lock(pmutex);
    brp_malloc_init(hash_array, ARRAY_SIZE);

    int pid = fork();

    if (pid == 0) {
        printf("Child: waiting for parent array initialization\n");
        pthread_mutex_lock(pmutex);

А вот здесь как раз и пример работы с таблицей указателей. Я условился, что мне нужен один указатель, содержащий адрес начала хэш-таблицы. Т.к на момент разветвления процессов ее еще нет и этот указатель появится лишь в родительском процессе. Вот здесь я его как раз получаю из общей памяти, он расположен в таблице указателей с индексом = 0.

        storage = brp_get_pointer_with_number(hash_array, 0);
        if (!storage) {
            fprintf(stderr, "Something wrong");
        } else {
            printf("Child: try to find 3 items TEST10, TEST15, TEST100\n");
            data_struct *item = NULL;

Далее я провожу манипуляции с найденной таблицей. Вношу изменения в нее в дочернем процессе.

            HASH_FIND_STR(storage, "TEST10", item);
            if (item) {
                printf("Child: TEST10 found. Change counter from %d to %d\n",
                        item->counter, item->counter + 1000);
                item->counter += 1000;
            }
            HASH_FIND_STR(storage, "TEST15", item);
            if (item) {
                printf("Child: TEST15 found. Change counter from %d to %d\n",
                        item->counter, item->counter + 2000);
                item->counter += 2000;
            }
            HASH_FIND_STR(storage, "TEST100", item);
            if (!item) {
                printf("Child: TEST100 not found. Going to create it\n");
                item = (data_struct*) brp_malloc(hash_array,
                        sizeof(data_struct));
                snprintf(item->name, 50, "TEST100");
                item->counter = 10000;
                HASH_ADD_STR(storage, name, item, hash_array);
            }

        }
        pthread_mutex_unlock(pmutex);
        pthread_mutexattr_destroy(&attrmutex);

    } else if (pid == -1) {
        fprintf(stderr, "Fork error\n");
    } else {

Заполнение таблицы значениями

        //Родитель инициализирует массив заполняет его и отпускает мьютекс
        int index = 0;
        data_struct *item, *tmp = NULL;
        printf("Parent: add data to hash array\n");
        for (index = 0; index < 20; index++) {
            item = (data_struct*) brp_malloc(hash_array, sizeof(data_struct));
            snprintf(item->name, 50, "TEST%d", index);
            item->counter = index;
            HASH_ADD_STR(storage, name, item, hash_array);
        }
        //Создадим в памяти один указатель на таблицу, чтоб в дочернем процессе знать
        //где hash таблица начигается

А вот момент создания таблицы указателей, создается один указатель и сохраняется адрес хеш-таблицы из переменной storage.

        brp_make_pointers_table(hash_array, 1);
        brp_set_pointer_to_number(hash_array, 0, storage);

        pthread_mutex_unlock(pmutex);
        printf("Parent: wait for children work\n");
        sleep(2);
        pthread_mutex_lock(pmutex);
        printf("Parent: read of children activity\n");
        index = 0;
        HASH_ITER(hh, storage, item, tmp)
        {
            printf("Parent: %d) %s=>%d\n", index++, item->name, item->counter);
            HASH_DEL(storage, item);
            brp_free(hash_array, item);
        }
        pthread_mutex_unlock(pmutex);

        //Освободим память
        pthread_mutex_destroy(pmutex);
        pthread_mutexattr_destroy(&attrmutex);
        munmap(shmem, ARRAY_SIZE + sizeof(pthread_mutex_t));
    }

}

вывод работы скрипта:

Parent: add data to hash array
Parent: wait for children work
Child: waiting for parent array initialization
Child: try to find 3 items TEST10, TEST15, TEST100
Child: TEST10 found. Change counter from 10 to 1010
Child: TEST15 found. Change counter from 15 to 2015
Child: TEST100 not found. Going to create it
Parent: read of children activity
Parent: 0) TEST0=>0
Parent: 1) TEST1=>1
Parent: 2) TEST2=>2
Parent: 3) TEST3=>3
Parent: 4) TEST4=>4
Parent: 5) TEST5=>5
Parent: 6) TEST6=>6
Parent: 7) TEST7=>7
Parent: 8) TEST8=>8
Parent: 9) TEST9=>9
Parent: 10) TEST10=>1010
Parent: 11) TEST11=>11
Parent: 12) TEST12=>12
Parent: 13) TEST13=>13
Parent: 14) TEST14=>14
Parent: 15) TEST15=>2015
Parent: 16) TEST16=>16
Parent: 17) TEST17=>17
Parent: 18) TEST18=>18
Parent: 19) TEST19=>19
Parent: 20) TEST100=>10000

Добавить комментарий

Следующая запись Предыдущая запись