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)
- вывод полной информации о выделенных и свободных участках памяти во всем массиве. Информация выводится в виде:- STATUS U SIZE 24 ST 40 DC 11
- STATUS U SIZE 7 ST 40 DC 11
- STATUS U SIZE 24 ST 40 DC 11
- STATUS U SIZE 8 ST 40 DC 11
- 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 выделяется в указанном хранилище.
Использование
Библиотеку можно использовать:
- для обмена массивов данных(посредством utstring, utarray, utringbuffer, uthash, utlist) через общую память
- для работы в рамках одного процесса, но с ограничениями выделяемой памяти
Первый случай делает удобный обмен данными с использованием хэш-таблиц, динамических массивов, списков и прочее.
Пример 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