Eine kurze Frage: Wenn ich die main Funktion per return 0 verlasse, werden dann auch alle Ausgänge zurückgesetzt (auf low) und alle Interrupts angehalten?
Kommt auf den Compiler, bzw. die Library an. Wenn du einen Debuger hast - einfach im Assember-Modus weiter steppen.
Erstmal vielen Dank für deine Antwort! Also würde im Allgemeinen ein "return 0;" nicht reichen, um alles zu stoppen und alle Ausgänge auf 0 zu setzen? PS: Ich benutzte den AVR/GNU C++ Compiler (mit AtmelStudio). Kann man da eine Aussage treffen?
Natürlich interessiert niemanden um welches Betriebssystem es sich handelt! Fenster interpretiert den Rückgabewert 0 als alles palletti. Räumt dann auf (Ressourcen) und wartet auf besseres Wetter. Bei Mikroprozessoren, mit Betriebssystem (Linux und Konsorten) ist ein ähnliches Verhalten zu erwarten, wie bei Fenster. Bei Mikroprozessoren, ohne Betriebssystem (Linux und Konsorten) ist dies nicht vorgesehen. Ich könnte mir aber zwei Szenarien vorstellen. 1. Da kein Rücksprung und damit keine Rücksprungadresse auf dem Stack vorhanden ist, erfolgt ein Ausflug ins Nirwana. 2. Der Compiler hat eine Dummy-Adresse auf dem Stack abgelegt, so gibt es zwei weitere Möglichkeiten: a) Ein Sprung zum Reset-Punkt - meist 0000 b) Ein Sprung in eine Endlosschleife um irgendwelche "Irrtümer" zu vermeiden. Noch etwas zum Ausflug ins Nirwana. Oft landet der Prozessor in Bereichen oberhalb des genutzten Speichers. Manchmal stehen hier Reste von früheren Programmierversuchen. Was dann passiert, ist nicht vorhersehbar. Auf jeden Fall wird im Nirwana nichts gemacht. Also ein Port behält seinen Status, ein Hardware-Timer tickt weiter. Es kann sogar sein, dass aus dem Nirwana heraus, via Interrupt, etwas Sinnvolles gemacht wird, um dann wieder "zurückzuspringen". FLASH-Speicher enthält, wenn er nicht programmiert wird, den Wert 0xFF. Was der Prozessor hieraus macht hängt von der jeweiligen Interpretation dieses Wertes ab. Ist diese "harmlos", so durchläuft der Prozessor diesen Bereich ohne viel Unsinn anzustellen und landet, wenn er "hinter" das Ende gerät, wieder bei 0000. Letzteres ist oft der Reset-Punkt.
Hallo, Schau dir mal den startup-code an. Denn die main() Funktion ist garnicht das erste was auf dem controller läuft. Irgendwo gibt es sicherlich ein bisschen Asm Code der dir deinen Controller initialisiert und anschließend erst in dein eigentliches Programm springt. Und wenn du daraus wieder rausspringst, bist du wieder im startup code... Grüße
Wenn Du wirklich den Prozessor final anhalten willst (Er würde dann erst nach einem Ab- und Einschalten wieder loslaufen) wäre es besser alle Timer, Interupts und den Watchdog sauber zu stoppen, die Ausgänge auf 0 zu setzen und danach in einen entsprechenden Sleepmode (Falls vorhanden) oder eine Endlosschleife zu springen.
Andreas schrieb: > Eine kurze Frage: Wenn ich die main Funktion per return 0 verlasse, > werden dann auch alle Ausgänge zurückgesetzt (auf low) und alle > Interrupts angehalten? Nein. Und nein. Bei einem embedded System gibt es nichts, was nach main() noch passieren könnte. Die meisten Umgebungen die ich gesehen habe, packen nach den Aufruf von main() einfach eine Endlosschleife. Andreas schrieb: > Also würde im Allgemeinen ein "return 0;" nicht reichen, um alles zu > stoppen und alle Ausgänge auf 0 zu setzen? Warum sollte es das? Wenn du genau weißt, daß du das haben willst, dann schreib es doch einfach explizit an das Ende von main(). Schreib eine 0 auf alle Ports, die Ausgangspins haben. Schalte die Interrupts entweder global oder per individuellem Freigabebit ab. Und dann pack da eine Endlosschleife hin.
"Auf jeden Fall wird im Nirwana nichts gemacht. Also ein Port behält seinen Status, ein Hardware-Timer tickt weiter. " Wenn im Nirvana nicht grade der Opcode steht, der die Ports auf Ausgang bratzt oder den Timer anhält.....
Bei avr-gcc kommt nach main() exit(), was aus cli() und Endlosschleife besteht.
Beim avr is es so: Die Ports behalten ihren Zustand, und der Compiler erzeugt "exit"-Code. D.h. - Er baut ein cli ein, schaltet also die Interrupts ab - Er erzeugt eine Endlosschleife Der AVR mach also nichts mehr außer sich langweilen... PS: ich weiß noch wie das mit dem watchdog gemacht wird...
Bastler schrieb: > Bei avr-gcc kommt nach main() exit(), was aus cli() und Endlosschleife > besteht. Vielen Dank! Genau das wollte ich wissen.
@Martin >Wenn im Nirvana nicht grade der Opcode steht, der die Ports auf Ausgang >bratzt oder den Timer anhält..... Es kann manchmal nicht schaden einen Post ganz zu lesen... ... sonst hättest Du bemerkt, dass ich auch das folgende geschrieben habe: "Manchmal stehen hier Reste von früheren Programmierversuchen. Was dann passiert, ist nicht vorhersehbar".
Bastler schrieb: > Bei avr-gcc kommt nach main() exit(), was aus cli() und Endlosschleife > besteht. Nur der Vollständigkeit halber (falls der TE einen uralten GCC benutzen sollte): Das CLI vor der Endlosschleife kam erst mit GCC 4.3. Seit wann es die Endlosschleife gibt, weiß ich nicht, aber mindestens seit GCC 4.2.
>Was passiert, wenn die main Funktion verlassen wird?
Nun, die Funktion wird erst mal tief traurig sein -so wie jeder Andere
auch, wenn er verlassen wird.
MfG Paul
Gibt es ein Leben nach der main()? Im Computer mit OS, ja. Da kontrolliert ja das OS was davor und danach passiert. Im Endeffekt wird (halbwegs gutes OS vorausgesetzt) einfach aufgeräumt und gut is. Im µC kommt's auf den Compiler an. Ist der compiler clever, wird er am Ende einen jmp auf die eigene Adresse legen. Ist er nicht ganz so clever endet das Programm eben und läuft in den "leeren" Programmspeicher dahinter, was je nach Prozessor eben etwas anderes ist. Meines Wissens ist leer bei AVR ein nop (no opcode), entsprechend läuft das Ding dann einfach leer bis zum Ende des Speichers, woraufhin entweder der IP überläuft und eh wieder bei Null (=Reset) anfängt oder auf eine ungültige Adresse zeigt und dann eben per Absturz zum Reset kommt. Um jetzt auch die Frage zu beantworten: Zurückgesetzt wird da per default nix. Nur wenn das Ding halt "unkontrolliert" rumrennt wird irgendwann ein Reset erfolgen.
:
Bearbeitet durch User
Andreas schrieb: > Bastler schrieb: >> Bei avr-gcc kommt nach main() exit(), was aus cli() und Endlosschleife >> besteht. > > Vielen Dank! Genau das wollte ich wissen. Und was ist mit dem Watchdog falls der an ist, da passiert dann doch noch etwas denn der kümmert sich nicht um cli(). Es ist somit völlig sinnlos aus der main() hearuszuspringen ohne vorher gezielt alle Funktionen in den gwünschten Zustand zu bringen. Genaugenommen ist es überhaupt sinnlos aus main() herauszuspringen.
> Und was ist mit dem Watchdog falls der an ist, Der macht sein Ding! Was soll er sonst tun ..... Und wenn der auf "Reset" konfiguriert ist, dann gibts ein Leben nach der main(). Quasi eine Wiedergeburt. > Genaugenommen ist es überhaupt sinnlos aus main() herauszuspringen. Jau! Und falls es wirklich Fälle gibt, wo das einen Sinn geben mag, dann sollte man überlegen, ob man nicht besser ein Netzteil mit Shutdown Funktion verwendet. Zweckes Stromsparen. Die PCs und ihre Netzteile machen es vor.
Wird auf einem AVR die main() beendet, werden alle Interrupte deaktiviert und eine Endlosschleife (eine JMP-Anweisung auf sich selbst) läuft bis zum Hardwarereset weiter. Ist der Watchdog aktiviert, erzeugt der Watchdog nach Ablauf seines Timers ein Reset.
Helmut S. schrieb: > Es ist somit völlig sinnlos aus der main() hearuszuspringen ohne vorher > gezielt alle Funktionen in den gwünschten Zustand zu bringen. > Genaugenommen ist es überhaupt sinnlos aus main() herauszuspringen. Genau so ist das. Ich würde sogar so weit gehen und es einen Bug nennen, wenn main() in einer embedded Anwendung verlassen werden kann.
Andreas schrieb: > Eine kurze Frage: Wenn ich die main Funktion per return 0 verlasse, > werden dann auch alle Ausgänge zurückgesetzt (auf low) und alle > Interrupts angehalten? Der C-Standard sagt:
1 | 5.1.2.2.3 Program termination |
2 | If the return type of the main function is a type compatible with |
3 | int, a return from the initial call to the main function is |
4 | equivalent to calling the exit function with the value returned |
5 | by the main function as its argument; reaching the } that terminates |
6 | the main function returns a value of 0. If the return type is not |
7 | compatible with int, the termination status returned to the host |
8 | environment is unspecified. |
Die Aussage, nach main lande ein C- oder C++-Progamm im "Nirvana", entbehrt also jeder Grundlage. Bei Verwendung der avr-libc fällt _exit auf exit. Bei avr-gcc sind _exit und exit in der libgcc definiert, und exit ist dort weak:
1 | .section .fini9,"ax",@progbits |
2 | DEFUN _exit |
3 | .weak exit |
4 | exit: |
5 | ENDF _exit |
6 | ;; Code from .fini8 ... .fini1 sections inserted by ld script. |
7 | .section .fini0,"ax",@progbits |
8 | cli |
9 | __stop_program: |
10 | rjmp __stop_program |
main wiederum wird per [R]CALL vom Startup-Code aufgerufen. Im Standard-Layout werden die .finiN-Section — so vorhanden — innerhalb von exit abgearbeitet, und dort werden dann Dinge erledigt wie: - Ausführung von statischen C++ Destruktoren - Ausführung von C-Destruktoren - Ausführung von mit per atexit() registrierten Funktionen Zugegebenermaßen wurde die Behandlung von statischen C++ Destruktoren und atexit-Funktionen erst kürzlich überarbeitet und vervollständigt, denn diese Features werden wohl kaum verwendet. Bei Verwendung eines Simulators kann es aber ganz praktisch sein, und es gibt auch Testsuites wie die von GCC welche entsprechende Features testen. Hier zum Beispiel die Implementierungen von exit und abort von avrtest, dem Simulator, mit dem avr-gcc getestet wird:
1 | /* This defines never returning functions exit() and abort() */
|
2 | |
3 | /* Postpone raising EXIT until .fini1 below so that higher .fini dtors,
|
4 | destructors and functions registered by atexit() won't be bypassed. */
|
5 | |
6 | static int avrtest_exit_code; |
7 | |
8 | /* libgcc defines exit as weak. */
|
9 | |
10 | void exit (int code) |
11 | {
|
12 | __asm volatile ("sts %0+0,%A1" "\n\t" |
13 | "sts %0+1,%B1" "\n\t" |
14 | "%~jmp _exit"
|
15 | : /* no outputs */ |
16 | : "i" (&avrtest_exit_code), "r" (code)); |
17 | for (;;); |
18 | }
|
19 | |
20 | |
21 | static void __attribute__ ((naked, used, section(".fini1"))) |
22 | avrtest_fini1 (void) |
23 | {
|
24 | /* sycall 30 */
|
25 | avrtest_exit (avrtest_exit_code); |
26 | }
|
27 | |
28 | void abort (void) |
29 | {
|
30 | /* sycall 31 */
|
31 | avrtest_abort (); |
32 | for (;;); |
33 | }
|
Johann L. schrieb: > Die Aussage, nach main lande ein C- oder C++-Progamm im "Nirvana", > entbehrt also jeder Grundlage. Ach so ist das also. Also, mein Lieber, deine Aussage ist es, die jeder Grundlage entbehrt. Es kommt auch nicht auf den Compiler drauf an. Das was im Falle eines Beendens von main kommt, ist alleinige Sache des Startup-Codes. Ich kenne genügend Startup-Codes, die mit diversen grandiosen IDE's mitgeliefert werden, die z.B. bei ARM einfach main mit BX starten. Das ist ein Sprung OHNE Rückkehr. Zum Rückkehren bräuchte es nen BLX. Und einfach mit der CPU durch einen Wald von NOP's oder anderem Gemüse zu rasen, in der Hoffnung, beim Überlauf bei 0 anzukommen und dort wieder tritt zu fassen, ist sinnlos auf ganz vielen Maschinen. Bei ARM landest du in den Vektoren und der Stackpointer ist ohnehin vesaut. Es ist durchaus eine ernste (aber andere) Frage, ob es klug und vorausschauend ist, main ohne Rückkehr anzuspringen, es ist ebenso eine Frage, ob es weise ist, bei eventuellem Rücksprung oder bei anderen unbeabsichtigten Exceptions den Prozessor in eine Schleife zu legen. Immerhin kann er dort ja - falls es mal bloß eine Störung von außen war - nix mehr tun, nicht mal neu anfangen. W.S.
Klar ist es Sache des Startup-Codes, sich DEFINITIONSGEMÄSS zu verhalten. Und wird waren bei AVR, der kennt kein BX.
W.S. schrieb: > Ich kenne genügend Startup-Codes, die mit diversen grandiosen > IDE's mitgeliefert werden, [...] Gut. Dann kennst du viele Implementationen die sich nicht an den Standard halten. Wenn sich eine Implementation — und zu der gehört auch das CRT — nicht an den Standart hält, dann ist es müßig, sich darüber zu echauffieren, dass die entsprechende Implementation nicht standardkonform ist... Was von einer nicht-konformen Implementation zu erwarten ist, d.h. wie und wo sie nicht konform ist, sollte zwar aus deren Fineprint zu entnehmen sein, was aber nichts daran ändert, dass sie nicht konform ist.
Johann L. schrieb: > Andreas schrieb: >> Eine kurze Frage: Wenn ich die main Funktion per return 0 verlasse, >> werden dann auch alle Ausgänge zurückgesetzt (auf low) und alle >> Interrupts angehalten? > > Der C-Standard sagt: > >
1 | 5.1.2.2.3 Program termination |
2 | > If the return type of the main function is a type compatible with |
3 | > int, a return from the initial call to the main function is |
4 | > equivalent to calling the exit function with the value returned |
5 | > by the main function as its argument; reaching the } that terminates |
6 | > the main function returns a value of 0. If the return type is not |
7 | > compatible with int, the termination status returned to the host |
8 | > environment is unspecified. |
> > Die Aussage, nach main lande ein C- oder C++-Progamm im "Nirvana", > entbehrt also jeder Grundlage. IBTD Der zitierte Abschnitt des Standards sagt lediglich aus, daß ein Verlassen von main() äquivalent zum Aufruf von exit() ist. Er sagt aber gar nichts darüber aus, wer oder was denn den Returnwert (oder das Argument von exit()) entgegennimmt und was er damit anstellt. Das ist undefiniert. Aka Nirvana. Und das ist sicher auch beabsichtigt. Aus Sicht der virtuellen C-Maschine gehören argc, argv und der Returnwert von main() zur Außenwelt. Sie sind schlicht nicht Bestandteil der Definition der Maschine. In einem hosted environment ist die Sache einigermaßen klar, denn da läuft main() nicht von allein los, sondern wird von irgendeiner Instanz gestartet, die dann ihrerseits den Returnwert auswerten und irgendwas damit anstellen kann. Aber in einem freestanding environment kommt vor main() lediglich die Initialisierung der C Runtime und nach main() die De-Initialisierung. Beide gehören streng genommen noch zu main. Aber was nach der De-Initialisierung geschieht ist halt unspezifiziert. Und deswegen ist es IMNSHO auch ein Bug, wenn man ein bestimmtes Verhalten für diesen Fall voraussetzt.
exit() übergibt die Kontrolle an die "Übergeordnete Instanz". In Unix der Parentprozess, auf Reiner Hardware eben diese. Das "Nirvana" ist eine Endlosschleife mit gesperrten INT's. Für gezieltes Runterfahren der benutzten Geräte gibt es atexit(). Natürlich kann man das alles "nicht verstehen", aber dadurch wird nicht automatisch undefiniert, sondern nur, auf die Menschheit bezogen, partiell unverstanden.
Johann L. schrieb: > W.S. schrieb: >> Ich kenne genügend Startup-Codes, die mit diversen grandiosen >> IDE's mitgeliefert werden, [...] > > Gut. Dann kennst du viele Implementationen die sich nicht an den > Standard halten. Du argumentierst mit dem Standard ohne ihn wirklich zu kennen. Genauer, du argumentierst mit den Anforderungen für ein Hosted Environment, obwohl es sich beim dieser Diskussion um Free Standing Environments handelt. Der Standard enthält, Kapitel 4, eine Liste von Ausnahmen von sich selbst für sogenannte Free Standing Environments. Embedded Anwendungen ohne Betriebssystem sind ein Free Standing Environment. Da hat ein Return von main() oder ein exit()-Aufruf eben kein definiertes Verhalten. > Wenn sich eine Implementation — und zu der gehört auch das CRT — nicht > an den Standart hält, dann ist es müßig, sich darüber zu echauffieren, > dass die entsprechende Implementation nicht standardkonform ist... Der Einzige der sich her echauffieren bist du - ohne den Standard zu kennen. > Was von einer nicht-konformen Implementation zu erwarten ist, d.h. wie > und wo sie nicht konform ist, Undefiniertes Verhalten eines return von main() oder exit() ist standardkonform für Free Standing Environments. Also mach mal nicht so viel Wind.
:
Bearbeitet durch User
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.