Forum: Compiler & IDEs Problem mit malloc() und -O2


von Gast (Gast)


Lesenswert?

Hallo

Ich will einen aduc7026 programmieren. Dazu verwende ich Eclipse, gnuarm 
und OpenOCD.

Ich versuche mich gerade in der dynamischen Speicherverwaltung. Ich 
verwende dies für ein Menu bestehend aus verketteten Listen. Mit -O0 
funktioniert das ganze. Mit -O1 oder -O2 leider nicht mehr. Der Debugger 
bleibt an der ersten stelle stehen, an der malloc() aufgerufen wird. 
Leider habe ich keine Ahnung wieso.
Meine erste Idee war, dass vielleicht ein Fehler in den syscalls, vor 
allem sbrk, steckt. Ich habe daher ein kleines Projekt erzeugt, in dem 
fast nur der malloc() Aufruf drin ist. Natürlich funktioniert darin 
alles :(
In meinem eigentlichen Projekt sind noch ca. 30kb Flash und 4kb Ram laut 
map-File frei. Daher denke ich nicht, dass beim ersten malloc() Aufruf 
schon der Speicher voll ist.

Leider kenne ich mich mit allem, was die newlib betrifft, noch nicht so 
wirklich aus. Ich wüsste auch nicht, welche Variablen wegoptimiert 
werden könnten. Hat einer von euch ein paar Tips, wonach ich schauen 
könnte?

Viele Grüsse,

Tilo

von Gast (Gast)


Lesenswert?

Ich konnte das Problem jetzt deutlich eingrenzen. Bei -O2 wird mehr SRam 
verbraucht.

Ich verwende folgende sbrk Funktion:
1
void * _sbrk_r(
2
    struct _reent *_s_r, 
3
    ptrdiff_t nbytes)
4
{
5
  static char *heap_ptr = end;    /* Points to current end of the heap.  */
6
  char  *base;    /*  errno should be set to  ENOMEM on error  */
7
  base = heap_ptr;  /*  Point to end of heap.  */
8
  //heap_ptr += nbytes;  /*  Increase heap.  */
9
  /* Zero new allocated memory */
10
  for (i=0; i<nbytes; i++) {
11
    *heap_ptr = 0;
12
    heap_ptr++;
13
  }
14
  return base;    /*  Return pointer to start of new heap area.  */
15
}

Mit folgendem Code versuche ich nun, auf den Speicher zuzugreifen:
1
    int tmp = sizeof(*ListIter);
2
    Menu_Main = malloc(tmp);

tmp ist 12. malloc() ruft die sbrk funktion 2x auf. Bei ersten mal 
werden 32Bytes angefragt, beim zweiten mal werden ganze 3920Bytes 
angefordert. Laut der MAP-File liegt der letze belegte Speicherblock 
bei:
                0x00011090                PROVIDE (end, .)
Der Speicher geht bis Adresse 0x12000. Der gesammte "freie" Speicher 
sind also genau 3920+32Bytes.

Leider liegt der Stack bei 0x00011FFC. Versucht hier malloc Speicher zu 
reservieren, der vom Stack verwendet wird?

Wie kann ich das verhindern?

Warum versucht malloc den gesammten freien Speicher beim ersten aufruf 
zu reservieren? Warum nicht nur das, was wirklich gebraucht wird?

Vielen Dank,

Tilo

von Martin T. (mthomas) (Moderator) Benutzerseite


Lesenswert?

Gast wrote:

Zumindest einen wenig ideenreicheren Nickname würde man sich wünschen - 
egal.

> Ich konnte das Problem jetzt deutlich eingrenzen. Bei -O2 wird mehr SRam
> verbraucht.
>
> Ich verwende folgende sbrk Funktion:
>
1
> void * _sbrk_r(
2
>     struct _reent *_s_r,
3
>     ptrdiff_t nbytes)
4
> {
5
>   static char *heap_ptr = end;    /* Points to current end of the heap.
6
> */
7
>   char  *base;    /*  errno should be set to  ENOMEM on error  */
8
>   base = heap_ptr;  /*  Point to end of heap.  */
9
>   //heap_ptr += nbytes;  /*  Increase heap.  */
10
>   /* Zero new allocated memory */
11
>   for (i=0; i<nbytes; i++) {
12
>     *heap_ptr = 0;
13
>     heap_ptr++;
14
>   }
15
>   return base;    /*  Return pointer to start of new heap area.  */
16
> }
17
>
>
> Mit folgendem Code versuche ich nun, auf den Speicher zuzugreifen:
>
1
>     int tmp = sizeof(*ListIter);
2
>     Menu_Main = malloc(tmp);
3
>
>
> tmp ist 12. malloc() ruft die sbrk funktion 2x auf. Bei ersten mal
> werden 32Bytes angefragt, beim zweiten mal werden ganze 3920Bytes
> angefordert.

Nicht mehr ganz genau in Erinnerung aber so ungefähr: Der erste Aufruf 
dient dazu, den Anfang des Heaps zu bestimmen, dann kommt erst die 
eigentliche Anforderung für eine "Page".

> Laut der MAP-File liegt der letze belegte Speicherblock
> bei:
>                 0x00011090                PROVIDE (end, .)
> Der Speicher geht bis Adresse 0x12000. Der gesammte "freie" Speicher
> sind also genau 3920+32Bytes.

Soweit erinnert, ist die Standardgröße in der newlib für eine Page 
4kBytes, der ""freie" Speicher"" reicht also nach den gemachten Angaben 
nicht aus dafür. sbrk ist ja nur ein kleiner Teil der dynamischen 
Speicherverwaltung, der die Verbindung zur Hardware herstellt. Der Rest 
ist in der Newlib (newlib/libc/stdlib...), die nicht grade ideal für 
Kleinstrechner mit wenig RAM. ist aber es gibt wohl keine kostenlose 
Alternative mit ähnlich liberaler Lizenz.

> Leider liegt der Stack bei 0x00011FFC. Versucht hier malloc Speicher zu
> reservieren, der vom Stack verwendet wird?
>
> Wie kann ich das verhindern?

Wenn meine Vermutung stimmt: indem man im code der newlib etwas anpasst 
und sie neu zusammenbaut. Irgendwo im Code gibt es eine Einstellung für 
die pagesize. Wenn richtig erinnert ist da ein #if SMALLMEM o.ä., wenn 
definiert wird die Seitengröße 128(256?) Bytes gesetzt. Evtl. gibt es 
auch noch irgend einen config-Schalter. Nie selbst ausprobiert.

> Warum versucht malloc den gesammten freien Speicher beim ersten aufruf
> zu reservieren?

Denke, das ist nur augenscheinlich so, da der "gesammmte freie Speicher" 
kleiner ist als die Pagesize.

>Warum nicht nur das, was wirklich gebraucht wird?

Wird wohl innerhalb der 'Verwaltungseinheiten' auch so sein, nur soviel 
Platz muss man wohl zumindest bereitstellen.

Alles nur meine Vermutung/Halbwissen, hatte das Problem bis dato noch 
nicht, nutze malloc auch nur selten auf Controllern. Genaueres kann man 
evtl. in der newlib mailing-liste erfahren, wenn der Quellcode nicht 
informativ genug ist.

Falls es so knapp ist und eine Art zeitweise Speichernutzung angestrebt 
wird, kann man evtl. das Konzept der Memory-Bags nutzen. Ist ein Art 
Pseudo-Malloc, das aber auf Speicher in data-segment Zugreift 
(=byte/word/dword-arrays). Etwas weniger flexibel aber einfacher zu 
überschauen und mglw. auch sicherer. Vgl. z.B. DB101 Beispielcode von 
Atmel.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Martin Thomas wrote:

> ... ist aber es gibt wohl keine kostenlose
> Alternative mit ähnlich liberaler Lizenz.

2.11BSD könnte mal einen Blick Wert sein.  Das ist ja nun auch seit
einigen Jahren komplett frei erhältlich, und seine Zweckbestimmung
war einstmals, auf einer PDP-11 zu laufen.  Die dürfte speichermäßig
ähnlich limitiert gewesen sein wie ein ARM7 oder großer AVR.

von Tilo (Gast)


Lesenswert?

Guten morgen

Vielen Dank für eure Erläuterungen. Das bringt ein wenig Licht ins 
Dunkle. Es hat mich verwundert, dass der Speicher genau bis zum Ende 
genutzt werden sollte. Das muss aber wirklich ein Zufall gewesen sein, 
da Änderungen im Linker-Skript keine Auswirkungen haben.

Ich dachte, die Newlib würde mit einem kleinen arm etwas mehr "Spaß" 
machen. Bei meinen Versuchen mit printf() hat es sich schon angedeutet, 
dass dem nciht so ist. Mein eigener Code um eine Zeile per Uart 
auszugeben hatte 4kb, mit printf ohne Floats waren es 15kb.

@Martin:
Danke für deine Tips zu Newlib. Das Compilerersymbol lautet 
"SMALL_MEMORY". Damit werden bei den ersten durchläufen 32Bytes und 
danach 128Byte Blöcke reserviert. Mit diesem Parameter läuft es jetzt.

Ich habe meine Toolchain nach der Anleitung für OSX erzeugt:
http://embdev.net/wikisoftware_en/index.php?title=ARM_GCC_toolchain_for_Linux_and_Mac_OS_X

Ich habe den Wiki-Eintrag mit einem Hinweis ergänzt.


Ich brauche keine echte dynamische Speicherverwaltung. Ich benutze das 
ganze nur, um meine Menüs zu erzeugen. Der Speicher muss nicht mehr 
freigegeben werden. Ich habe daher die sbrk Funktion ein wenig angepasst 
und verwende jetzt die statt malloc(). Bisher funktioniert das gut.


Viele Grüsse, Tilo

von Martin T. (mthomas) (Moderator) Benutzerseite


Lesenswert?

Tilo wrote:
> Guten morgen
>
> Vielen Dank für eure Erläuterungen. Das bringt ein wenig Licht ins
> Dunkle. Es hat mich verwundert, dass der Speicher genau bis zum Ende
> genutzt werden sollte. Das muss aber wirklich ein Zufall gewesen sein,
> da Änderungen im Linker-Skript keine Auswirkungen haben.
>
> Ich dachte, die Newlib würde mit einem kleinen arm etwas mehr "Spaß"
> machen.

""Spaß"" mit einer libc...egal. Wenn man ungefähr weiss, was man besser 
mit Vorsicht benutzt, ist das ja auch so. In neueren Versionen ist sogar 
ein wenig "handoptimierter" ARM-Assemblercode für einige Funktionen 
enthalten.

> Bei meinen Versuchen mit printf() hat es sich schon angedeutet,
> dass dem nciht so ist. Mein eigener Code um eine Zeile per Uart
> auszugeben hatte 4kb, mit printf ohne Floats waren es 15kb.

Ja, stdio der newlib ist auch nicht so wirklich gut für die "Kleinen", 
selbst abgesehen von der Abhängigkeit zu malloc et al. Könnte man 
wahrscheinlich mit einem Ansatz, wie Ihn die avr-libc nutzt deutlich 
verbessern aber offensichtlich hatte noch niemand genug Leidensdruck, 
sich darum zu kümmern. Wobei sich ein Blick in den Quellcode der newlib 
und Fragen in der newlib-ML mglw. lohnt. Der ganze Code ist voll von 
config-Optionen, von denen einige auch nur im Code per Kommentar 
ansatzweise und knapp dokumentiert sind. Einer der Entwickler weiß 
vielleicht auf Anhieb mehr.

Ganz nette Alternative für stdio, allerdings nur mit recht 
eingeschränkter Funktionalität, findet man in den Softpacks von Atmel 
für AT91. Lassen sich relativ leicht portieren und Lizenz ist liberal.

> @Martin:
> Danke für deine Tips zu Newlib. Das Compilerersymbol lautet
> "SMALL_MEMORY". Damit werden bei den ersten durchläufen 32Bytes und
> danach 128Byte Blöcke reserviert. Mit diesem Parameter läuft es jetzt.

ah. SMALL_MEMORY, lag ich ja nicht allzu weit daneben.

> Ich habe meine Toolchain nach der Anleitung für OSX erzeugt:
> 
http://embdev.net/wikisoftware_en/index.php?title=ARM_GCC_toolchain_for_Linux_and_Mac_OS_X
>
> Ich habe den Wiki-Eintrag mit einem Hinweis ergänzt.

Prima.

> Ich brauche keine echte dynamische Speicherverwaltung. Ich benutze das
> ganze nur, um meine Menüs zu erzeugen. Der Speicher muss nicht mehr
> freigegeben werden. Ich habe daher die sbrk Funktion ein wenig angepasst
> und verwende jetzt die statt malloc(). Bisher funktioniert das gut.

Liest sich durch gut.

Weiterhin viel Erfolg.

von Martin T. (mthomas) (Moderator) Benutzerseite


Lesenswert?

Jörg Wunsch wrote:
> Martin Thomas wrote:
>
>> ... ist aber es gibt wohl keine kostenlose
>> Alternative mit ähnlich liberaler Lizenz.
>
> 2.11BSD könnte mal einen Blick Wert sein.  Das ist ja nun auch seit
> einigen Jahren komplett frei erhältlich, und seine Zweckbestimmung
> war einstmals, auf einer PDP-11 zu laufen.  Die dürfte speichermäßig
> ähnlich limitiert gewesen sein wie ein ARM7 oder großer AVR.

Ist damit das: http://www.freebsd.org/cgi/cvsweb.cgi/src/lib/libc/ 
gemeint? Wenn nicht, kannst Du bitte einen direkten Link angeben? 
Befragung von google mit 2.11BSD brachte overflow im der zweiten HDI 
Stufe.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Martin Thomas wrote:

> Befragung von google mit 2.11BSD brachte overflow im der zweiten HDI
> Stufe.

Beispielsweise hier:

http://unixarchive.medienfuzzis.com/PDP-11/Trees/2.11BSD/

Anlaufpunkt für diesen antiken Code ist "The Unix Heritage Society",
http://www.tuhs.org/ .

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.