Forum: Compiler & IDEs Verständnisfrage: SystemCalls und µC


von Jens Langecker (Gast)


Lesenswert?

Hallo zusammen,

ich hab mal kurz eine Frage zum Verständnis.
Angenommen ich habe einen µC ohne Betriebssystem und entwickle mit GCC.
Nun kann ich ja problemlos libc-Funktionen wie sprintf() verwenden.
Was ist denn aber mit libc-Funktionen, die eigentlich einen SystemCall 
ausführen würden, also z.B. fopen() oder fread()? Es gibt ja kein 
Betriebssystem, das den SystemCall entgegennehmen könnte.
Wie entscheidet der GCC, was er mit diesen Aufrufen machen soll?

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Probiere es doch aus! ;-)

Ich würde erwarten, dass der Linker Dir einfach ein "undefined symbol" 
präsentiert.

mfg Torsten

von Jens Langecker (Gast)


Lesenswert?

Torsten Robitzki schrieb:
> Ich würde erwarten, dass der Linker Dir einfach ein "undefined symbol"
> präsentiert.

Nein, läßt sich einwandfrei builden. Es passiert nur anscheinend nichts.

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


Lesenswert?

Jens Langecker schrieb:
> Was ist denn aber mit libc-Funktionen, die eigentlich einen SystemCall
> ausführen würden, also z.B. fopen() oder fread()?

Nein, fopen() und fread() sind ebenfalls keine Syscalls, sondern
Bibliotheksfunktionen, genau wie printf().

Was darunter benutzt wird, hängt dann von der jeweiligen Implementierung
der Bibliothek ab.  Bei der Newlib (wie sie beim ARM-GCC gern benutzt
wird) sind das Posix-typische Syscalls (open(), close(), read(),
write()), die du selbst beisteuern musst, oder für die es ggf. auch
Dummies in der Lib gibt.  Bei der avr-libc gibt es fdevopen() als
Pendant zum klassischen open(), oder auch statische Varianten davon,
bei denen man einen Stream zur Compilezeit vorkonfiguriert.  Die
Funktionen zum Lesen und Schreiben eines Zeichens muss man natürlich
auch da selbst liefern.

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


Lesenswert?

Jens Langecker schrieb:
> Nein, läßt sich einwandfrei builden. Es passiert nur anscheinend nichts.

Das deutet daraufhin, dass die Bibliothek vorgefertigte Dummies
enthält.

ARM + Newlib?

von Jens Langecker (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Jens Langecker schrieb:
>> Nein, läßt sich einwandfrei builden. Es passiert nur anscheinend nichts.
>
> Das deutet daraufhin, dass die Bibliothek vorgefertigte Dummies
> enthält.
>
> ARM + Newlib?

Korrekt. Hab mich inzwischen durch die Doku gewühlt und die Stubs 
gefunden.
Danke!

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?


von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jörg Wunsch schrieb:
> Bei der Newlib (wie sie beim ARM-GCC gern benutzt wird) sind das
> Posix-typische Syscalls (open(), close(), read(), write()),
> die du selbst beisteuern musst, oder für die es ggf. auch
> Dummies in der Lib gibt.  Bei der avr-libc gibt es fdevopen() als
> Pendant zum klassischen open(),

Vor einiger Zeit wollte ich open etc. auf Syscalls abbilden, quasi als 
Backend für fopen, fprintf, etc.  Ich hätte gedacht dafür gäbe es weak 
Stubs in der avr-libc, die ich dann auf Syscalls hätte abbilden können. 
Aber solche Stubs scheint es in der avr-libc nicht zu geben...

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


Lesenswert?

Johann L. schrieb:
> Aber solche Stubs scheint es in der avr-libc nicht zu geben...

Nee, die tickt ein bisschen anders.

Als ich das stdio dort geschrieben habe, wollte ich deutlich
sparsamer mit den Ressourcen umgehen, als das newlib & Co. machen.
Selbst mit fdevopen() wurde ich noch ausreichend kritisiert (und
habe dann die statischen Varianten nachgeliefert), da es in diesem
Umfeld genügend „malloc() ist absolut böse“-Fetischisten gibt.

Das, was man schon zu PDP-11-Zeiten in stdio gepuffert hat (in
hässlichsten Makros dazumals), ist eben für einen ATmega8 schon mal
deutlich zu viel SRAM-Bedarf.  Pro Stream eine Funktion zum Lesen
eines Zeichens und eine zum Schreiben ist gerade so OK.

von web (Gast)


Lesenswert?

coocox zB fügt eine syscalls.c zum projekt hinzu

für einfache malloc oder printf funktionen muss dann zumindest die 
_sbrk() geschrieben werden.

1
__attribute__ ((used))
2
caddr_t _sbrk ( int incr )
3
{
4
  static unsigned char *heap = NULL;
5
  unsigned char *prev_heap;
6
7
  if (heap == NULL) {
8
    heap = (unsigned char *)&_end;
9
  }
10
  prev_heap = heap;
11
12
  heap += incr;
13
14
  return (caddr_t) prev_heap;
15
}
16
17
__attribute__ ((used))
18
int link(char *old, char *new) {
19
return -1;
20
}
21
22
__attribute__ ((used))
23
int _close(int file)
24
{
25
  return -1;
26
}
27
28
__attribute__ ((used))
29
int _fstat(int file, struct stat *st)
30
{
31
  st->st_mode = S_IFCHR;
32
  return 0;
33
}
34
35
__attribute__ ((used))
36
int _isatty(int file)
37
{
38
  return 1;
39
}
40
41
__attribute__ ((used))
42
int _lseek(int file, int ptr, int dir)
43
{
44
  return 0;
45
}
46
__attribute__ ((used))
47
int _read(int file, char *ptr, int len)
48
{
49
  return 0;
50
}
51
__attribute__ ((used))
52
int _write(int file, char *ptr, int len)
53
{
54
  return len;
55
}
56
57
__attribute__ ((used))
58
void abort(void)
59
{
60
  /* Abort called */
61
  while(1);
62
}

von Ben (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Pro Stream eine Funktion zum Lesen
> eines Zeichens und eine zum Schreiben ist gerade so OK.

Aus aktuellem Anlass interessiert mich das Thema sehr.

Ich dachte mehr bräucht ein Stream auch nicht? Also eine getc() und eine 
putc(). Inwiefern wird da in der stdio noch gepuffert, bzw. wo würde das 
malloc() ins Spiel kommen?

Gruß,
Ben

von Jens Langecker (Gast)


Lesenswert?

Kaj G. schrieb:
> Beitrag "ARM - Probleme mit sprintf (Linkerfehler)"

Nachdem ich diesen Thread gelesen habe, hab ich aber noch eine Frage:
Ich benutze Atollic TrueSUTDIO (GCC 4.8.3) für einen STM32 und habe kein 
Betriebssystem.
Ich mache exzessiven Gebrauch von vsnprintf() und das funktioniert 
problemlos.
Wenn ich es richtig verstehe, verwendet vsnprintf() malloc() bzw. 
sbrk().
sbrk() habe ich nicht selbst beigesteuert.
Wer könnte denn in meinem Setup so freundlich sein, mir sbrk() zur 
Verfügung zu stellen?

von Karl H. (kbuchegg)


Lesenswert?

Jens Langecker schrieb:

> Wenn ich es richtig verstehe, verwendet vsnprintf() malloc() bzw.
> sbrk().

Warum sollte es?
die 's' Versionen der printf Familie schreiben in einen Buffer, den der 
Aufrufer zur Verfügung stellen muss. Da gibt es keinen Bedarf an 
dynamisch allokierten Speicher bzw. das ist so wenig, dass er mit 
funktionslokalen Variablen abzudecken ist. Ein long hat eine 
überschaubare und im Vorfeld bekannte maximale Textrepräsentierung. 
Dafür den Speicher dynamisch mittels malloc zu allokieren, wäre völliger 
Overkill.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ben schrieb:

> Ich dachte mehr bräucht ein Stream auch nicht?

Braucht er auch nicht.

> Also eine getc() und eine
> putc().

Yep, als Methoden, die im Stream-Objekt hinterlegt werden.

> Inwiefern wird da in der stdio noch gepuffert,

Gar nicht.

> bzw. wo würde das
> malloc() ins Spiel kommen?

Nur beim fdevopen(), um den Platz für das Stream-Objekt selbst zu
bekommen.  Das ist überhaupt nicht tragisch, wenn an diesen Objekten
nie mehr was geändert wird, dann fragmentiert davon auch kein Speicher
und nichts, das malloc() beißt auch nicht den Programmierer in den
Zeh. :-))  Der einzige wirkliche Nachteil ist, dass man sich natürlich
zusätzlichen Code (für malloc() selbst) mit auflädt.

Daher habe ich dann als Alternativen FDEV_SETUP_STREAM() bzw.
fdev_setup_stream() nachgereicht, bei denen der Nutzer den Speicher
selbst bereitstellen muss.

Jens Langecker schrieb:
> Wenn ich es richtig verstehe, verwendet vsnprintf() malloc() bzw.
> sbrk().

In der Newlib?  Nur für Gleitkommaoperationen, da wird ein
Zwischenspeicher alloziert.

Da haben sie noch einen bösen Bug drin, sind wir erst vor paar Tagen
drüber gestolpert (binutils bug #18212), da wird das Ergebnis eines
malloc() ungeprüft weiterbenutzt.  Gab malloc() einen Nullzeiger
zurück, so versuchen sie anschließend, auf Adresse 0 zu schreiben
(was uns in unserem Falle einen HardFault eingebrockt hat).

von Karl H. (kbuchegg)


Lesenswert?

Ben schrieb:

> Ich dachte mehr bräucht ein Stream auch nicht? Also eine getc() und eine
> putc(). Inwiefern wird da in der stdio noch gepuffert, bzw. wo würde das
> malloc() ins Spiel kommen?

Zb brauchst du für jeden fopen eine FILE Struktur. Will dein 
Anwendungsprogramm gleichzeitig 20 Files öffnen, müssen die irgendwo 
herkommen.
Du hast die Wahl zwischen statischer Allokierung einer entsprechenden 
Anzahl x im Vorfeld (mit entsprechendem Speicherplatzverbrauch) oder 
einer dynamischen Allokierung. In dem einen Fall ist nach x Files damit 
automatisch Schluss mit lustig bzw. je nach x sind es zu wenige oder du 
investierst zuviel Speicher. Oder eben im anderen Fall musst du die 
dynamische Allokierung bemühen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jörg Wunsch schrieb:
> zu PDP-11-Zeiten

Da war ich noch flüssig ;o)

> für einen ATmega8 schon mal deutlich zu viel SRAM-Bedarf.
> Pro Stream eine Funktion zum Lesen eines Zeichens und eine zum
> Schreiben ist gerade so OK.

War in einem Simulator-Umgebung, da wäre es ganz nett wenn die 
Standardfunktionen verfügbar werden, etwa in der avr-gcc Testsuite.  Da 
kann man ja nicht einfach fdevopen verwenden.

Vor einiger Zeit hatte ich den AVR-Simulator von magischen SFRs auf 
Syscalls umgestellt.  Im Gegensatz zu magischen SFRs haben Syscalls 
keinen Overhead für den Simulator, d.h. der Simulator könnte so 
Scherze bieten wie vom avr-Programm auf das Host-Dateisystem zuzugreifen 
ohne die Simulation zu verlangsamen :-)

: Bearbeitet durch User
von Haro (Gast)


Lesenswert?

Weils grad thematisch hier passt:

Wenn ich so wie im Tutorial hier beschrieben stdout auf meine UART 
Funktionen umleite dann kann ich per printf/scanf Daten lesen und 
schreiben, was oft komfortabler ist als die typischen uart_putc() oder 
uartputs().

Was aber wenn ich Binärdaten lesen/schreiben will?
Also z.B. ein UART Protokoll parsen will dass nicht nuur ASCII enthält?
Welche Funktionen würd ich da benutzen?

Ich tippe mal auf fread und fwrite, denen ich dann stdin und stdout 
mit-übergebe, richtig?
Was mach ich mit dem __size-Parameter? Einfach immer auf 1 setzen?

Grüße,
Haro

von Rolf M. (rmagnus)


Lesenswert?

Haro schrieb:
> Ich tippe mal auf fread und fwrite, denen ich dann stdin und stdout
> mit-übergebe, richtig?
> Was mach ich mit dem __size-Parameter? Einfach immer auf 1 setzen?

Auf die Größe des zu lesenden Elements.

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.