Forum: Mikrocontroller und Digitale Elektronik Eigenes Malloc


von Matthias (Gast)


Lesenswert?

Hallo liebe Leute.

auf Basis dieser Seite
http://tharikasblogs.blogspot.com/p/how-to-write-your-own-malloc-and-free.html

Wollte ich das malloc mal auch für meinen AVR nachprogrammieren.

nun bin ich auf das Problem gestoßen das obwohl ich genau soviel 
myMallocs wie my rees habe, ich irgendwann kein Speicher mehr anfordern 
kann.

Meine kleinen Blocks Funktionen sagen es gäbe wohl blocks die noch 
belegt sein.

was meint ihr dazu ?

MFG
Matthias
1
#include <stdint.h>
2
#include <stdlib.h>
3
#include <stdbool.h>
4
#include <avr/io.h>
5
#include <avr/interrupt.h>
6
#include <avr/pgmspace.h>
7
#include <util/atomic.h>
8
#include <util/delay.h>
9
10
11
struct block{
12
 size_t size;
13
 bool free;
14
 struct block *next;
15
};
16
17
18
typedef struct
19
{
20
    volatile uint8_t* memory_;
21
    uint16_t size_;
22
    struct block *freeList_;
23
}MyHeap;
24
25
26
27
28
MyHeap myHeapInitialize(uint16_t heap_size);
29
void myHeapSplit(struct block *fitting_slot,uint16_t size);
30
void* myHeapMalloc(MyHeap *heap, uint16_t size);
31
void myHeapMerge(MyHeap *heap);
32
void myHeapFree(MyHeap *heap,void* ptr);
33
34
uint16_t myHeapCountBlocks(MyHeap *heap);
35
uint16_t myHeapCountFreeBlocks(MyHeap *heap);
36
37
uint16_t myHeapCountBlockedBlocks(MyHeap *heap);
38
39
40
41
MyHeap myHeapInitialize(uint16_t heap_size)
42
{
43
44
    void* heap_buffer  = (void *) calloc(heap_size,sizeof(uint8_t));
45
    MyHeap heap =
46
    {
47
        .memory_ = heap_buffer,
48
        .size_ = heap_size,
49
        .freeList_ = heap_buffer,
50
    };
51
    heap.freeList_->size=heap_size - sizeof(struct block);
52
    heap.freeList_->free=true;
53
    heap.freeList_->next=NULL;
54
    return heap;
55
}
56
57
58
void myHeapSplit(struct block *fitting_slot,uint16_t size)
59
{
60
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
61
    {
62
    struct block *new=(void*)((void*)fitting_slot+size+sizeof(struct block));
63
    new->size=(fitting_slot->size)-size-sizeof(struct block);
64
    new->free=true;
65
    new->next=fitting_slot->next;
66
    fitting_slot->size=size;
67
    fitting_slot->free=false;
68
    fitting_slot->next=new;
69
    }
70
}
71
72
73
void* myHeapMalloc(MyHeap *heap, uint16_t size)
74
{
75
    void *result;
76
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
77
    {
78
79
    struct block *curr;
80
    curr=heap->freeList_;
81
82
    while((((curr->size)<size)||((curr->free)==false))&&(curr->next!=NULL)){
83
        //prev=curr;
84
        curr=curr->next;
85
        //printf("One block checked\n");
86
    }
87
88
    if((curr->size)==size){
89
        curr->free=false;
90
        result=(void*)(++curr);
91
        //printf("Exact fitting block allocated\n");
92
        return result;
93
    }
94
    else if((curr->size)>(size+sizeof(struct block))){
95
        myHeapSplit(curr,size);
96
        result=(void*)(++curr);
97
        //printf("Fitting block allocated with a split\n");
98
        return result;
99
    }
100
    else{
101
        result=NULL;
102
        PORTA.OUTSET = PIN4_bm;
103
        //printf("Sorry. No sufficient memory to allocate\n");
104
        return result;
105
106
    }
107
    }
108
    return result;
109
}
110
111
112
void myHeapMerge(MyHeap *heap)
113
{
114
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
115
    {
116
    struct block *curr;
117
    curr=heap->freeList_;
118
    while((curr->next)!=NULL){
119
        if((curr->free) && (curr->next->free))
120
        {
121
            curr->size+=(curr->next->size)+sizeof(struct block);
122
            curr->next=curr->next->next;
123
        }
124
        else
125
        {
126
            //prev=curr;
127
            curr=curr->next;
128
        }
129
        //curr=curr->next;
130
    }
131
    }
132
}
133
134
uint16_t myHeapCountBlocks(MyHeap *heap)
135
{
136
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
137
    {
138
    uint16_t blocks = 0;
139
    struct block *curr;
140
    curr=heap->freeList_;
141
    do
142
    {
143
        blocks++;
144
        curr=curr->next;
145
        if (blocks > heap->size_)
146
            return blocks;
147
    }
148
    while((curr->next)!=NULL);
149
    return blocks;
150
    }
151
    return 0;
152
}
153
154
uint16_t myHeapCountFreeBlocks(MyHeap *heap)
155
{
156
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
157
    {
158
    uint16_t blocks = 0;
159
    struct block *curr;
160
    curr=heap->freeList_;
161
    do{
162
        if (curr->free)
163
            blocks++;
164
        curr=curr->next;
165
        if (blocks > heap->size_)
166
            return blocks;
167
    }
168
    while((curr->next)!=NULL);
169
    return blocks;
170
    }
171
    return 0;
172
}
173
174
uint16_t myHeapCountBlockedBlocks(MyHeap *heap)
175
{
176
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
177
    {
178
    uint16_t blocks = 0;
179
    struct block *curr;
180
    curr=heap->freeList_;
181
    do{
182
        if (!curr->free)
183
            blocks++;
184
        curr=curr->next;
185
        if (blocks > heap->size_)
186
            return blocks;
187
    }while((curr->next)!=NULL);
188
    return blocks;
189
    }
190
    return 0;
191
}
192
193
void myHeapFree(MyHeap *heap,void* ptr)
194
{
195
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
196
    {
197
    if(((void*)heap->memory_<=ptr)&&(ptr<=(void*)(heap->memory_ + heap->size_))){
198
        struct block* curr=ptr;
199
        --curr;
200
        curr->free=true;
201
        myHeapMerge(heap);
202
        ptr = NULL;
203
    }
204
    else
205
    {
206
        PORTA.OUTSET = PIN3_bm;
207
    }
208
    }
209
}

von g457 (Gast)


Lesenswert?

> was meint ihr dazu ?

Langen Code anhängen, nicht inlinen. Abgesehen davon: überflüssig.

Nix für ungut.

von P. S. (namnyef)


Lesenswert?

Srsly?

Beitrag #6225735 wurde von einem Moderator gelöscht.
von Sebastian S. (amateur)


Lesenswert?

Ich fände es toll, wenn einer endlich mal das Rad erfinden würde.
So geht das doch nicht weiter.

Ansonsten schau mal nach, was das Netz zum Thema Speicherfragmentierung 
hergibt.
Hast Du lauter kleine Bereiche belegt und dazwischen ein paar Blöcke 
freigegeben, in der Summe also "viel Platz", so heißt das aber immer 
noch nicht, dass da auch ein großer Block Platz findet. Ist der größte, 
zusammenhängende Block um ein Byte zu klein, so geht halt nichts. Auch 
wenn in der Summe noch jede Menge Platz vorhanden ist.

von Matthias (Gast)


Lesenswert?

Naja das Problem scheint mir doch nicht die Speicherfragmentierung zu 
sein,
wenn nach 10 alloc und 10 free immer noch Blöcke belegt sind scheint ja 
was im Code nicht zu stimmen. Sinn und zweck ist Übung probieren und 
lernen.

Strategie is übrigens first fit so am Rande, soll nicht das mega 
effizienteste sein soll einfach nur mal richtig tun

von c-hater (Gast)


Lesenswert?

Matthias schrieb:

> wenn nach 10 alloc und 10 free immer noch Blöcke belegt sind scheint ja
> was im Code nicht zu stimmen.

Ja, natürlich. Das passiert z.B. genau dann, wenn free nicht in der Lage 
ist, benachbarte Blöcke ehemals belegten Speichers wieder zu einem Block 
freien Speichers zu vereinen...

Es verbleiben einfach die Informationen der Speicherverwaltung als 
Artefakt...

von Markus M. (adrock)


Lesenswert?

Auf die Schnelle ist kein Fehler zu sehen. Sicher das Deine Testfunktion 
i.O. ist?

Haben die übriggebliebenen Blöcke eine bestimmte Größe?

Ansonsten hilft nur evtl. die Testfunktion so zu verändern, dass sie 
nach JEDEM free Aufruf die Anzahl der Blöcke auf Plausibilität prüft und 
bei einer Abweichung sofort Meldung gibt bzw. Du einen Breakpoint im 
Debugger setzt und dann schaust was genau passiert.

@c-hater: Ja, aber das wird bei seiner Implementierung eigentlich nicht 
passieren. Er geht bei einem free ja die ganze Speicherliste durch und 
checkt ob etwas gemerged werden kann.

von Matthias (Gast)


Lesenswert?

Genau das macht ja mein free, setzt jeden block auf free und zwar 
sofort.

Mh ja als der test Code ist momentan eine Queue, die hat intern ein 
counter wenn ich was hinzufüge und entferne.

Dieser ist tatsächlich auf 0 also Queue ist leer.
in diesem Fall sollte ich ja bedenkenlos beliebig solange ich die Größe 
meines heaps nicht verletzte was ein die Queue packen können....

werde den block check mal gleich nach dem merge im free ansetzen ....

danke
Matthias

von Pandur S. (jetztnicht)


Lesenswert?

Mein Gratulation zur Erkenntnis, dass dynamischer Speicher auf so einem 
Controller ein No-go ist.
Was soll dynamischer Speicher denn bringen ?  Man kann C++ auch ohne 
dynamischer Speicher laufenlassen.

von Matthias (Gast)


Lesenswert?

@Markus M ich hab jetzt mal schnell counter für malloc und free 
eingeführt.
und es wird zwar nur hochgezählt wenn malloc speicher vergeben konnte 
und wenn free ihn auch wieder leeren konnte.

die zählen synchron hoch  also "scheint" der darüberlegende Code somit 
I.o

also gehe ich momentan stark davon aus das beim spliten oder mergen 
etwas schiefgeht

MFG
Matthias

von Stefan A. (king-crash)


Lesenswert?

Ich würde dir empfehlen das Ganze auf deinem PC mit einem größeren Array 
als heapersatz zu testen. Das ist dank Debugger wesentlich einfacher.

von Stefan F. (Gast)


Lesenswert?

Stefan A. schrieb:
> Ich würde dir empfehlen das Ganze auf deinem PC mit einem größeren Array
> als heapersatz zu testen. Das ist dank Debugger wesentlich einfacher.

Ja, oder mit dem AVR Simulator im AVR Studio (bzw. Atmel Studio) 
durchsteppen.

von Matthias (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

Also ich kann im nachhinein nicht mehr sagen wo der Fehler lag,

Ich vermute aber im malloc,

Denn das habe ich in der neuen Version etwas verbessert mit dem tipp von 
C-hater
im malloc aufzuräumen und nicht im free.

Des weiteren wurde das first fit durch eine best /first fit als 
Strategie geändert.

läuft jetzt seit 24H durchgängig, ist natürlich kein beweiß das es 
fehlerfrei ist aber ein Indiz :)

von Theor (Gast)


Lesenswert?

Matthias schrieb:
> Hallo,
>
> Also ich kann im nachhinein nicht mehr sagen wo der Fehler lag,
>
> Ich vermute aber im malloc,
>
> Denn das habe ich in der neuen Version etwas verbessert [...]

Ich würde Dir aber doch sehr empfehlen, den Fehler zu "finden" und Dich 
nicht damit zufrieden zu geben, dass er "irgendwie" verschwunden ist.

Es muss nämlich gleichgültig sein, ob Du das aufräumen in malloc, vor 
der Rückgabe eines neuen, leeren Blocks oder in free, nach der Freigabe 
erledigst.

Man kann durchaus auf verschiedene Arten testen und es mag u.U. sein, 
dass Du nach der Änderung lediglich für eine bestimmten Fall (oder für 
einen Teil der Fälle) den Fehler nicht mehr siehst.

Keiner weiß es, und nach Murphy fallen einem diese Art Hoffnungen 
irgendwann auf die Füsse.

von Heiko L. (zer0)


Lesenswert?

Matthias schrieb:
> nun bin ich auf das Problem gestoßen das obwohl ich genau soviel
> myMallocs wie my rees habe, ich irgendwann kein Speicher mehr anfordern
> kann.
> ...
1
while((((curr->size)<size)||((curr->free)==false))&&(curr->next!=NULL))
2
...
3
if((curr->size)==size){
4
 ...
5
}
6
else if((curr->size)>(size+sizeof(struct block))){
7
 ...
8
} else{
9
 ...
10
}

So wie das da steht kann man den OutOfMemory Path auslösen auch wenn 
noch Speicher da wäre. Und zwar, wenn curr->size zwar größer als size 
aber kleiner als size+sizeof(struct block) ist.

von Matthias (Gast)


Lesenswert?

vermutlich war das auch das Problem...
Das ist aber in der neuen malloc aus dem letzten Post nicht mehr der 
Fall,

@ Theor Da hast du vollkommen Recht test Funktionen die die Blocks 
auswerten laufen ja noch mit,

Bis jetzt verhält sich ja alles wie erwartet...

Das neue malloc hat ja nicht einfach nur merge übernommen ...

[c]
void* myHeapMalloc(MyHeap *heap, uint16_t size)
{
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
    {
    uint16_t min_size = 65535-sizeof(struct block);
    struct block *current = heap->freeList_;
    struct block *best_fit_block = NULL;

    do
    {
        if(current->free && current->size == size) //exact fit
        {
           current->free = false;
           return (void*)++current;
        }

        if((current->free == true) && (current->next !=NULL) && 
(current->next->free == true)) //merge
        {
            current->size+=(current->next->size)+sizeof(struct block);
            current->next=current->next->next;
        }

        if((current->free) && (current->size > size + sizeof(struct 
block)) && (current->size < min_size + sizeof(struct block) )) //best 
fit
        {
            best_fit_block = current;
            min_size = current->size;
        }
        current = current->next;
    }while (current != NULL);

    //we neeeeeeeed split step
    if(best_fit_block != NULL)
    {
        myHeapSplit(best_fit_block,size);
        return (void*) ++best_fit_block;
    }
    }
    return NULL;
}

MFG
Matthias
[c/]

von S. R. (svenska)


Lesenswert?

Du solltest über eine Testsuite nachdenken, mit der du deine 
Implementation testest. Da malloc/free in der libc implementiert werden, 
kannst du z.B. hier schauen: https://wiki.musl-libc.org/libc-test.html

Ansonsten ist auch OpenBSD ziemlich absurd, was den Allocator angeht. 
Ich bin mir sicher, die haben auch einen guten Stresstest rumliegen. Hab 
aber spontan nichts gefunden.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.