Forum: PC-Programmierung RAM überlauf nach einiger Zeit?


von Jonas E. (jonas_e43)


Lesenswert?

Hallo,

Führt dieser Code:
1
char* UART_gets()
2
{
3
  uint8_t length = 1;
4
  char* line = malloc(sizeof(char) * length);
5
  memset(line, 0, length);
6
  line[0] = -1;
7
  
8
  while ( 1 )
9
  {
10
    line[length - 1] = UART_getc();
11
    if ( line[length - 1] == '\0')
12
      break;
13
    else
14
    {
15
      length++;
16
      
17
      char* newLine = realloc(line, sizeof(char) * length);
18
      if ( newLine != NULL )
19
        line = newLine;
20
      else
21
        break;
22
      
23
      line[length - 1] = 0;
24
    }
25
  }
26
  
27
  return line;
28
}

zu einem Überlaufen des RAMs nach mehrmaligem Ausführen wenn man nur 256 
bytes hat?

Gruß
Jonas

von LolBaer (Gast)


Lesenswert?

Ja.

von Jonas E. (jonas_e43)


Lesenswert?

Gibt es ne Möglichkeit das anders zu Lösen?

von Alexander F. (alexf91)


Lesenswert?

Ja, da du immer mehr Speicher anforderst, aber nie welchen freigibst

von Jonas E. (jonas_e43)


Lesenswert?

Ja das wird außerhalb der Funktion mit free(...) gemacht.
Könnte es an der Fragmentierung liegen?

von Andreas B. (andreasb)


Lesenswert?

Die Antwort hast du schon gekriegt.

Ich sags dir noch mit begründung:
>  char* line = malloc(sizeof(char) * length);

Reserviert Speicher, den musst du irgendwann freigeben.

Dieses vorgehen ist auf PCs durchaus üblich, aber auf einem 
Mikrocontroller würde ich den Speicher nicht dynamisch verwalten, 
sondern statisch.

Also du empfängst 16 Bytes, dann verarbeitest du die, dann wider 16 
Bytes empfange etc.

Dann weisst du, du brauchst 16 Bytes, und du brauchst nie mehr, den du 
sowieso nicht hast.

16 Bytes ist als Beispiel anzusehen.



mfg Andreas

von Alexander F. (alexf91)


Lesenswert?

Ich kann hier Andreas B. nur zustimmen.
Besonders bei kleinen Controllern ist es bedeutend einfacher und 
effizienter, auf dynamische Arrays zu verzichten.

Irgendwo vergisst du mal ein free() und schon hast du Speicherbereiche, 
die nicht mehr benutzt werden können. Das kann auf einem PC natürlich 
auch passieren, da sind die Auswirkungen aber im Allgemeinen nicht so 
gravierend.

von mar IO (Gast)


Lesenswert?

Wenn Du auf einen AVR (od. einen anderen µC) programmierst, dann findest 
Du z.B. in der Codesammlung genug Beispiele, wie man das löst.

Du hast nur eine gewisse Größe vom Speicher zur Verfügung. Dein Code 
berücksichtigt das aber nicht. Iwie. ist es auch seltsam, dass Du immer 
nur 1 Byte zusätzlich allokierst. Verwende halt einen Ringspeicher und 
Interrupts, der bei Empfang eines Zeichens beschrieben wird. Ist dieser 
voll (Überschreitung) bzw. wurde die Null-Termierung erkannt, so wird 
ein Flag gesetzt.

Ringspeicher http://www.mikrocontroller.net/articles/FIFO
UART http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Der_UART
UART + Ringspeicher Beitrag "AVR-GCC: UART mit FIFO"

von Yalu X. (yalu) (Moderator)


Lesenswert?

Ganz abgesehen davon, ob du den Speicher statisch oder dynamisch 
reservierst: Der Speicherüberlauf ist garantiert, wenn aus irgendeinem 
Grund (Fehler in der Gegenseite oder Übertragungsfehler) das Endebyte 
('\0') nicht empfangen wird.

In 99% aller Fälle kann die empfangene Nachricht Byte für Byte, ohne 
Zwischenspeicherung der gesamten Nachricht verarbeitet werden. Das 
solltest du dann auch tun. Wenn das nicht geht, musst du den Lesevorgang 
ab einer bestimmten Byteanzahl (die einen Fehlerfall signalisiert) 
abbrechen.

von Sebastian H. (sebihepp)


Lesenswert?

Der empfangene String darf maximal 128 Bytes groß sein. Realloc fordert 
zuerst den neuen Speicher an, kopiert die Daten und gibt erst dann den 
alten frei. Wenn du also bei Zeichen 127 bist, hast du 127 Bytes belegt 
und dann werden zusätzlich die 128 Bytes für das letzte Zeichen 
reserviert. Das führt zu einem Speicherverbrauch von 255 Bytes.

Und dein Program braucht anderweitig sicher auch noch Speicher und schon 
wird es knapp.

von Modern Embedde Design (Gast)


Lesenswert?

std::string

Wir sind nicht mehr in der Steinzeit.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Modern Embedde Design schrieb:
> Wir sind nicht mehr in der Steinzeit.

Ja. Mach das mal auf einem µC mit 1 kiB RAM.

Nur zu.

von Modern Embedded Design (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Modern Embedde Design schrieb:
>> Wir sind nicht mehr in der Steinzeit.
>
> Ja. Mach das mal auf einem µC mit 1 kiB RAM.
>
> Nur zu.

Funktioniert hier wunderbar. :-P

von Jonas E. (jonas_e43)


Angehängte Dateien:

Lesenswert?

Hallo,
um nochmal auf das Ram Problem zurückzukommen:
Wenn der Microcontroller einige male etwas über Uart empfangen hat (ca 
50-200) mal stürzt er ab. Dies zeigt sich darin, dass er immer an einem 
bestimmten Punkt sich aufhängt. Ich würde mich sehr darüber freuen wenn 
sich jemand mal meinen Code ansehen könnte und mir vielleicht ein paar 
Probleme nennen könnte. Bedanke mich schon mal im Foraus.
Gruß Jonas

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Jonas E. schrieb:
> Ich würde mich sehr darüber freuen wenn

... die Leute endlich mal kapieren würden das .rar ein denkbar 
unglückliches Format ist wenn man etwas einer breiten Öffentlichkeit zur 
Verfügung stellen möchte.

Zip kann inzwischen nahezu jedes OS sogar nativ erstellen und lesen...

von Jonas E. (jonas_e43)


Angehängte Dateien:

Lesenswert?

Okay dann als *.zip...

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Seh ich das richtig, das du im RX interrupt blockierend liest?
Damit müllst du dir im blödestem Fall den Stack voll, du sollstest 
wenigstens das Interrupt-Flag löschen, oder noch besser, ohne Interrupt 
in der Main pollen...

Außerdem ist es extrem unschön das deine Funktion keinen Fehlerwert 
zurückliefert wenn es einen Overflow gab.
Auch die Prüfung > 0 ist etwas unsinnig. Du hast ja nun gerade per 
interrupt mitbekommen, das wenigstens ein Zeichen vorliegt.

von Jonas E. (jonas_e43)


Lesenswert?

Wie meinst du das, dass ich blockieren lese?
Könntest du das im Code einmal zetieren?
Das Interrupt-Flag wird doch automatisch gelöscht, wenn ich die Daten 
aus dem UART Register lese?
Mit dem > 0 habe ich nun verbessert.
Gruß Jonas

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Hier
1
ISR(USART_RX_vect)
2
3
{
4
5
    if ( UART_gets(uart_string, 20) > 0 )
6
7
    {
8
9
      uart_receive_complete = true;
10
11
    }
12
13
}
 rufst du doch die UART_gets Funktion auf, welche wiederum 20x
1
uint8_t UART_getc(void)
2
3
{
4
5
  while (!(UCSRA & (1<<RXC)))   // warten bis Zeichen verfuegbar
6
7
  ;
8
9
  return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben
10
11
}
aufruft!

Jonas E. schrieb:
> Das Interrupt-Flag wird doch automatisch gelöscht, wenn ich die Daten
> aus dem UART Register lese?

Das kann sein, solltest du aber im Datenblatt des Prozessor 
verifizieren, trotz allem ist das sehr unschön im Interrupt irgendwelche 
länglichen UART Strings zu empfangen, das ginge in der Hauptschleife 
sehr viel einfacher und stressfreier.

Im blödestem Falle wird nämlich der Interrupt sofort wieder aufgerufen, 
du hast der Hauptschleife aber gerade signalisiert das sie lesen kann 
und bretzelst die Daten im nächstem Moment wieder über...

von Jonas E. (jonas_e43)


Lesenswert?

Stimm. Darüber habe ich noch gar nicht nachgedacht.
Du würdest also ohne RXC Interrupt arbeiten?
Gruß Jonas

von Reinhard Kern (Gast)


Lesenswert?

Jonas E. schrieb:
> Du würdest also ohne RXC Interrupt arbeiten?

Das nicht unbedingt, aber wenn dann holt man im Interrupt nur das eine 
Zeichen, das ihn ausgelöst hat, und wartet nicht auf weitere.

Gruss Reinhard

von Jonas E. (jonas_e43)


Lesenswert?

Reinhard Kern schrieb:
> Das nicht unbedingt, aber wenn dann holt man im Interrupt nur das eine
> Zeichen, das ihn ausgelöst hat, und wartet nicht auf weitere.

Wie kann ich denn mit dieser Vorgehensweise einen String vernünftig 
empfangen? Hättest du einen Beispielcode, mit dem ich mein Vorgehen 
realisieren kann?

Gruß Jonas

von Reinhard Kern (Gast)


Lesenswert?

Jonas E. schrieb:
> Wie kann ich denn mit dieser Vorgehensweise einen String vernünftig
> empfangen?

Man richtet einen Empfangspuffer ein (fest) und hängt immer das neueste 
Zeichen hinten an, bis der String komplett ist - z.B. weil ein CR 
gekommen ist. Dann setzt man ein Flag fürs Hauptprogramm, dass eine neue 
Message vorliegt.

Gruss Reinhard

von Robert L. (lrlr)


Lesenswert?

>Man richtet einen Empfangspuffer ein (fest)

ich würde mal behaupten, man richtet ZWEI ein..

wenn man nämlich nur:
>Dann setzt man ein Flag fürs Hauptprogramm,

macht, hat man ja weiterhin das Problem, dass weiterhin Daten auflaufen, 
während das Hauptprogramm mit den "alten" Daten irgendwas macht..



PS: "PC-Programmierung" ist der falsche Forums-Bereich, oder ???

von Reinhard Kern (Gast)


Lesenswert?

Robert L. schrieb:
> ich würde mal behaupten, man richtet ZWEI ein..

das kommt halt drauf an, ob weiter Daten kommen können, oder ob der 
Gegner erst mal auf Antwort wartet. Aber erst mal so einfach wie 
möglich, wir wollen Jonas ja nicht gleich ganz verschrecken.

Gruss Reinhard

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.