Commit a59502e8 authored by Jules Sang's avatar Jules Sang
Browse files

debut_malloc

parent f355a1a4
cmake_minimum_required(VERSION 2.6)
if(COMMAND cmake_policy)
cmake_policy(SET CMP0003 NEW)
endif(COMMAND cmake_policy)
project(emalloc)
enable_testing()
set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall -Werror -std=gnu11")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Werror -std=gnu++11")
#########
# Vous devez editer la ligne suivante pour y mettre vos logins
#########
set(VARIANTE_LOGINS sangj plichonl)
###
list(SORT VARIANTE_LOGINS)
if (VARIANTE_LOGINS MATCHES "login[123]")
message(FATAL_ERROR "** ERREUR **: Vous devez modifier CMakeLists.txt pour y mettre vos logins")
endif()
#######
# Compilation standard avec googletest
#######
###
# sur Debian et al, les googletests sont à recompiler
# sur centOS6, c'est une bibliothèque avec son équivalent de pkg-config
# sinon, on utilise l'autodetection de Cmake
###
if(EXISTS /usr/src/gtest)
add_subdirectory(/usr/src/gtest ./gtest)
else(EXISTS /usr/src/gtest)
if(EXISTS /usr/bin/gtest-config)
execute_process(COMMAND gtest-config --cppflags --cxxflags OUTPUT_VARIABLE CENTOS_GTEST_CFLAGS)
execute_process(COMMAND gtest-config --ldflags --libs OUTPUT_VARIABLE CENTOS_GTEST_LDFLAGS)
string(STRIP "${CENTOS_GTEST_CFLAGS}" CENTOS_GTEST_CFLAGS)
string(STRIP "${CENTOS_GTEST_LDFLAGS}" CENTOS_GTEST_LDFLAGS)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CENTOS_GTEST_CFLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${CENTOS_GTEST_LDFLAGS} -lgtest_main")
else(EXISTS /usr/bin/gtest-config)
find_package(GTest REQUIRED)
include_directories(${GTEST_INCLUDE_DIRS})
endif(EXISTS /usr/bin/gtest-config)
endif(EXISTS /usr/src/gtest)
##
# Si vous utilisé plusieurs fichiers, en plus de mem.c et les autres,
# pour votre allocateur il faut les ajouter ici
##
add_library(emalloc SHARED src/mem.c src/mem_internals.c src/mem_small.c src/mem_medium.c src/mem_large.c)
##
# Bibliothèque pour faire des tests en python
##
add_library(mempy SHARED tests/mempymodule.c)
target_link_libraries(mempy emalloc)
##
# Construction du programme de tests unitaires
##
add_executable(alloctest tests/alloctest.cc tests/test_mark.cc tests/test_generic.cc tests/test_buddy.cc tests/test_run_cpp.cc)
target_link_libraries(alloctest gtest gtest_main emalloc)
add_test(AllTestsAllocator alloctest)
##
# Ajout d'une cible pour lancer les tests de manière verbeuse
##
add_custom_target(check alloctest)
##
# Construction du shell
##
add_executable(memshell src/memshell.c)
target_link_libraries(memshell emalloc)
##
# Construction de l'archive
# inclu le .git si il est là
##
string(REPLACE ";" "_" LOGINS_SANS_POINTVIRGULE "${VARIANTE_LOGINS}")
set(CPACK_PACKAGE_VERSION_MAJOR "2")
set(CPACK_PACKAGE_VERSION_MINOR "0")
set(CPACK_PACKAGE_VERSION_PATCH ${LOGINS_SANS_POINTVIRGULE})
set(CPACK_SOURCE_GENERATOR "TGZ")
set(CPACK_SOURCE_IGNORE_FILES
"~$"
"\\\\.o$"
"^${PROJECT_SOURCE_DIR}/build/"
)
include(CPack)
This diff is collapsed.
* TP Allocateur mémoire: malloc
Ce squelette permet d'implanter un allocateur mémoire virtuelle. Le
sujet de TP expliquant le travail est diponible sur
http://ensiwiki.ensimag.fr dans [[https://ensiwiki.ensimag.fr/index.php?title=SEPC-S1][la page SEPC Semestre 1]]
** TP antécédents
- le tp mémoire allocateur physique: first fit, buddy, chainage
- le tp mmap
** Nouveauté par rapport aux TPs précédents
- allocateur virtuelle en trois tailles, plus progressif.
- une arena par thread
- croissance de l'arena par thread en utilisant mmap et une
structure =_Thread_local=
- récursive doubling pour l'allocation des chunk et du pool principal
- très gros (plus 128ko): mmap en direct
- très petit pour alloc de moins de 64o, par chunk de 96o
- buddy pour le pool principal
- marquage des blocs (avant après) pour avoir sa vrai taille
- un magic entier
- utiliser LCG MMIX de Knuth (64 bits) pour générer la valeur
magique à partir de l'addresse utilisée
https://en.wikipedia.org/wiki/Linear_congruential_generator
* Idées d'améliorations
** TODO En faire une bibliothèque dynamique
- capable d'être utilisée à la place du malloc de la bibliothèque C
** TODO Utiliser git bundle au lieu de Cpack
- Rendu complet, compact et avec toutes les informations de
contributions.
- Risque de rendu vide accru, si les étudiants n'arrivent pas à
faire un commit
- peut-être plus difficile pour les Phelma qui doivent apprendre
git en plus pour pouvoir rendre quelque chose ? C'est peut-être
le bon TP pour commencer.
** TODO discussion avec Vincent, si il est intéréssé ?
This diff is collapsed.
/*****************************************************
* Copyright Grégory Mounié 2008-2018 *
* This code is distributed under the GLPv3+ licence.*
* Ce code est distribué sous la licence GPLv3+. *
*****************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/mman.h>
#include "mem.h"
#include "mem_internals.h"
/** squelette du TP allocateur memoire */
MemArena arena = {};
/* ecrire votre code ici */
void *
emalloc(unsigned long size)
{
/* ecrire votre code ici */
if (size == 0)
return NULL;
if (size >= LARGEALLOC)
return emalloc_large(size);
else if (size <= SMALLALLOC)
return emalloc_small(size); // allocation de taille CHUNKSIZE
else
return emalloc_medium(size);
}
void
efree(void *ptr)
{
/* ecrire votre code ici */
Alloc a = mark_check_and_get_alloc(ptr);
switch( a.kind ) {
case SMALL_KIND:
efree_small(a);
break;
case MEDIUM_KIND:
efree_medium(a);
break;
case LARGE_KIND:
efree_large(a);
break;
default:
assert(0);
}
}
/*****************************************************
* Copyright Grégory Mounié 2008-2018 *
* This code is distributed under the GLPv3+ licence.*
* Ce code est distribué sous la licence GPLv3+. *
*****************************************************/
#ifndef MEM_H
#define MEM_H
/* ENTETE DU TP1. NE RIEN INSERER OU EDITER DANS CE FICHIER ! */
/* TP1 HEADERS. DO NOT EDIT THIS FILE ! */
#ifdef __cplusplus
extern "C" {
#endif
void *emalloc(unsigned long size);
void efree(void *ptr);
#ifdef __cplusplus
}
#endif
#endif
/******************************************************
* Copyright Grégory Mounié 2018 *
* This code is distributed under the GLPv3+ licence. *
* Ce code est distribué sous la licence GPLv3+. *
******************************************************/
#include <sys/mman.h>
#include <assert.h>
#include <stdint.h>
#include "mem.h"
#include "mem_internals.h"
unsigned long knuth_mmix_one_round(unsigned long in)
{
return in * 6364136223846793005UL % 1442695040888963407UL;
}
void *mark_memarea_and_get_user_ptr(void *ptr, unsigned long size, MemKind k)
{
unsigned long magic_number = knuth_mmix_one_round((unsigned long) ptr);
magic_number = magic_number & ~(0b11UL);
magic_number = magic_number | k;
*((unsigned long*) ptr) = size;
*((unsigned long*) (ptr + 1)) = magic_number;
*((unsigned long*) (ptr + size/8 - 1)) = magic_number;
*((unsigned long*) (ptr + size/8 - 2)) = size;
return ptr + 2;
}
Alloc
mark_check_and_get_alloc(void *ptr)
{
unsigned long size = *((unsigned long*) (ptr - 2));
unsigned long magic_number = *((unsigned long*) (ptr - 1));
MemKind k = (MemKind) magic_number;
unsigned long expected_magic_number = (knuth_mmix_one_round(size) & ~(0b111UL)) | k;
assert(magic_number == expected_magic_number);
// assertion of marquage coherence for magic_number & size
assert( magic_number == *((unsigned long*) ptr + (size % 8) + 1) );
assert( size == *((unsigned long*) ptr + (size % 8) + 2) );
Alloc a = { ptr, k, size };
return a;
}
unsigned long
mem_realloc_small() {
assert(arena.chunkpool == 0);
unsigned long size = (FIRST_ALLOC_SMALL << arena.small_next_exponant);
arena.chunkpool = mmap(0,
size,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS,
-1,
0);
if (arena.chunkpool == MAP_FAILED)
handle_fatalError("small realloc");
arena.small_next_exponant++;
return size;
}
unsigned long
mem_realloc_medium() {
uint32_t indice = FIRST_ALLOC_MEDIUM_EXPOSANT + arena.medium_next_exponant;
assert(arena.TZL[indice] == 0);
unsigned long size = (FIRST_ALLOC_MEDIUM << arena.medium_next_exponant);
assert( size == (1 << indice));
arena.TZL[indice] = mmap(0,
size*2, // twice the size to allign
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS,
-1,
0);
if (arena.TZL[indice] == MAP_FAILED)
handle_fatalError("medium realloc");
// align allocation to a multiple of the size
// for buddy algo
arena.TZL[indice] += (size - (((intptr_t)arena.TZL[indice]) % size));
arena.medium_next_exponant++;
return size; // lie on allocation size, but never free
}
// used for test in buddy algo
unsigned int
nb_TZL_entries() {
int nb = 0;
for(int i=0; i < TZL_SIZE; i++)
if ( arena.TZL[i] )
nb ++;
return nb;
}
/******************************************************
* Copyright Grégory Mounié 2018 *
* This code is distributed under the GLPv3+ licence. *
* Ce code est distribué sous la licence GPLv3+. *
******************************************************/
#ifndef MEM_INTERNALS_H
#define MEM_INTERNALS_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdlib.h>
#define handle_fatalError(msg) \
do { char c[1048] = {}; snprintf(c, 1048, "%s %s %d",msg, \
__FILE__, __LINE__); \
perror(c); exit(EXIT_FAILURE); } while(0)
#define SMALLALLOC 64
// SMALLALLOC + 2 MAGIC + 2 Tailles sur 8o == 96o
#define CHUNKSIZE 96
// 128 Kio == 128 * 1024 == 2**17 == (1<<17)
#define LARGEALLOC (1<<17)
// 2**13o == 16Kio
#define FIRST_ALLOC_SMALL (CHUNKSIZE <<7) // 96o * 128
#define FIRST_ALLOC_MEDIUM_EXPOSANT 17
#define FIRST_ALLOC_MEDIUM (1<<FIRST_ALLOC_MEDIUM_EXPOSANT)
// values from 0 to 3 fit in 2 bit
typedef enum _MemKind { SMALL_KIND, MEDIUM_KIND, LARGE_KIND } MemKind;
#define TZL_SIZE 48
typedef struct _MemArena {
void *chunkpool;
void *TZL[TZL_SIZE];
int small_next_exponant;
int medium_next_exponant;
} MemArena;
typedef struct _Alloc {
void *ptr;
MemKind kind;
unsigned long size;
} Alloc;
extern MemArena arena;
unsigned long knuth_mmix_one_round(unsigned long in);
void *mark_memarea_and_get_user_ptr(void *ptr, unsigned long size, MemKind k);
Alloc mark_check_and_get_alloc(void *ptr);
unsigned int nb_TZL_entries();
unsigned long mem_realloc_small();
unsigned long mem_realloc_medium();
void *emalloc_small(unsigned long size);
void *emalloc_medium(unsigned long size);
void *emalloc_large(unsigned long size);
void efree_small(Alloc a);
void efree_medium(Alloc a);
void efree_large(Alloc a);
#ifdef __cplusplus
}
#endif
#endif
/******************************************************
* Copyright Grégory Mounié 2018 *
* This code is distributed under the GLPv3+ licence. *
* Ce code est distribué sous la licence GPLv3+. *
******************************************************/
#include <sys/mman.h>
#include "mem.h"
#include "mem_internals.h"
void *
emalloc_large(unsigned long size)
{
unsigned long taille= size + 32;
void *newmem = mmap(0,
taille,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS,
-1,
0);
if (newmem == MAP_FAILED)
handle_fatalError("large alloc fails");
return mark_memarea_and_get_user_ptr(newmem, taille, LARGE_KIND);
}
void efree_large(Alloc a) {
int ret = munmap(a.ptr, a.size);
if (ret == -1)
handle_fatalError("large free fails");
}
/******************************************************
* Copyright Grégory Mounié 2018 *
* This code is distributed under the GLPv3+ licence. *
* Ce code est distribué sous la licence GPLv3+. *
******************************************************/
#include <stdint.h>
#include <assert.h>
#include "mem.h"
#include "mem_internals.h"
unsigned int puiss2(unsigned long size) {
unsigned int p=0;
size = size -1; // allocation start in 0
while(size) { // get the largest bit
p++;
size >>= 1;
}
if (size > (1 << p))
p++;
return p;
}
void *
emalloc_medium(unsigned long size)
{
assert(size < LARGEALLOC);
assert(size > SMALLALLOC);
/* ecrire votre code ici */
return (void *) 0;
}
void efree_medium(Alloc a) {
/* ecrire votre code ici */
}
/******************************************************
* Copyright Grégory Mounié 2018 *
* This code is distributed under the GLPv3+ licence. *
* Ce code est distribué sous la licence GPLv3+. *
******************************************************/
#include <assert.h>
#include "mem.h"
#include "mem_internals.h"
void *
emalloc_small(unsigned long size)
{
/* ecrire votre code ici */
return (void *)0;
}
void efree_small(Alloc a) {
/* ecrire votre code ici */
}
/*****************************************************
* Copyright Grégory Mounié 2013,2018 *
* Simon Nieuviarts 2008-2012 *
* This code is distributed under the GLPv3 licence. *
* Ce code est distribué sous la licence GPLv3+. *
*****************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "mem.h"
/*
===============================================================================
Macros
===============================================================================
*/
/*
#define DEBUG
*/
/*
* Nombre max de blocs alloues en meme temps
*/
#define NB_MAX_ALLOC 5000
/*
* Nombre de commandes differentes pour l'interpreteur
* (sans inclure les commandes erronees (ERROR))
*/
#define NB_CMD 8
/*
* Nombre de caracteres maximal pour une ligne de commande
* (terminateur inclus A VERIFIER)
*/
#define MAX_CMD_SIZE 64
/* prompt */
#define PROMPT ">"
/*
===============================================================================
Types
===============================================================================
*/
/*
* Identification de la commande tapee
* On rajoute ERROR pour les commandes erronees
*/
typedef enum {INIT=0, SHOW, USED, ALLOC, FREE, DESTROY, HELP, EXIT, ERROR} COMMAND;
/*
* type des identificateurs de blocs
*/
typedef unsigned long ID;
/*
* Identification des parametres tapes
*/
typedef struct {
ID id;
size_t size;
} ARG;
/*
* Structure de donnees contenant les informations sur un bloc alloue
*/
typedef struct {
/* identificateur du bloc alloue, vaut 0 si pas de bloc alloue*/
ID id;
/* adresse du bloc alloue, vaut NULL si pas de bloc alloue*/
void *address;
/* taille du bloc */
size_t size;
} BLOCINFO;
/*
===============================================================================
Variables globales
===============================================================================
*/
/*
* Compteur pour determiner les Ids des blocs alloues.
* Doit etre (re)initialise a 1
*/
unsigned long id_count;
/*
* Liste des commandes reconnues
*/
static char* commands[NB_CMD] = {"init", "show", "used","alloc", "free", "destroy", "help", "exit"};
/*
* Tableau stockant les infos sur les blocs alloues
* Les recherches dans cette table se font sur le champ id
* des structures BLOCINFO
*/
BLOCINFO bloc_info_table[NB_MAX_ALLOC];
static void *zone_memoire;
/*
===============================================================================
Fonctions
===============================================================================
*/
/*
* Fonction d'affichage de la mémoire occupée
*/
void used()
{
unsigned long i;
for(i=0; i<NB_MAX_ALLOC; i++) {
if ((bloc_info_table[i]).id != 0) {
printf("%ld 0x%lX 0x%lX\n",
bloc_info_table[i].id,
(unsigned long)(bloc_info_table[i].address - (void*)zone_memoire),
(unsigned long)bloc_info_table[i].size);
}
}
}
/*
* Affichage de l'aide
*/