Hallo, ich habe ein modul "foo" und möchte, dass die funktion "foo_init()" immer ausgeführt wird, wenn das Programm startet. Und zwar möglichst noch vor "main()", damit das Modul seine Variable zurücksetzten kann. Gibt es da einen Trick (oder spezielles Funktionsattribut)? Zur Zeit rufe ich die funktion "foo_init()" im "main()" explizit auf, manchmal auch zweimal. Ich benutze den AVR-GCC Danke, der Adib. --
bestehe nicht warum es vor main sein soll. Das macht eigentlich keinen sinn. Wenn du es als ersten in main aufrufst, dann ist das doch das gleiche.
ich möchte Abhängigkeiten in der Reihenfolge der Initialisierung vermeiden und möchte, dass die Module einen zuverlässigen "Reset-Zustand" haben, wie man das auch von Mikrokotrollern her kennt. Gibt es da irgendeine Sektion zwischen startup und main, wo man die funktionen reinsetzen kann, dass sie quasi automatisch (und nur einmal) aufgerufen werden? Gruß, Adib.
immer noch noch klar, du kannst doch die main als zwischen sektion verwenden. int main2() { //alles was du sonst in main machst. } int main() { modul1_init(); modul2_init();M modul2_init();M return main2(); }
Schau mal hier bei den "The .initN Sections": http://www.nongnu.org/avr-libc/user-manual/mem_sections.html (Ganz unten ist ein Beispiel)
wenn es c++ ist, könnte man in jedem modul eine globale instanz ein klasse anlegen und damit wird der Konstruktor vor main aufgerufen.
>ich möchte Abhängigkeiten in der Reihenfolge der Initialisierung >vermeiden und möchte, dass die Module einen zuverlässigen >"Reset-Zustand" haben, wie man das auch von Mikrokotrollern her kennt. Nun. Aus unserer Sicht ist dies unnötig. Aber es mag sein, das wir da was nicht verstehen. Kannst Du ein Beispiel angeben, bei 1. eine Abhängigkeit in der Reihenfolge der Initialisierung besteht und 2. ein unzuverlässiger "Reset-Zustand" bestehen könnte und den Du gerade und ausschliesslich mit einer Initialisierung vor main vermeiden könntest?
Du kannst deinen Code in eine der .initN sections stecken. Die Auswahl der section hängt davon ab, was deine foo_init() vor hat. Es gibt ein Beispiel dazu in der avr-libc beim Watchdog um MCUSR zu lesen und den Watchdog vor dem Aufruf von main() zu disablen. http://www.nongnu.org/avr-libc/user-manual/mem_sections.html
Adib schrieb: > Gibt es da irgendeine Sektion zwischen startup und main, wo man die > funktionen reinsetzen kann, dass sie quasi automatisch (und nur einmal) > aufgerufen werden? In GCC kannst du Konstruktoren schreiben:
1 | #include <avr/io.h> |
2 | |
3 | static void __attribute__((constructor,used)) |
4 | init (void) |
5 | {
|
6 | PORTC = 0; |
7 | }
|
Konstruktiren werden nach dem Initialisieren der globalen Variablen in (.data, .bss, .rodata) jedoch vor main aufgerufen. Eine witere Möglichkeit im avr-gcc sind naked-Funktionen, die allerdings keinen Frame haben dürfen und explizit in einer .initN Section plaziert werden müssen. Konstruktoren in avr-gcc laufen in .init6, Destruktoren in .fini6. http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html Damit kann sich ein Modul selbst initialisieren ohne daß dies von Hand in main geschehen muss. main sieht davon nix. Somit kann man auch mit avr-gcc ein "normales" Hallo-Welt Programm schreiben, bei dem main.c so aussieht:
1 | #include <stdio.h> |
2 | |
3 | int main() |
4 | {
|
5 | printf ("Hallo Welt\n"); |
6 | return 0; |
7 | }
|
Und das eine Ausgabe auf UART zB macht oder den Text rausmorst — ja nach Gusto.
> bestehe nicht warum es vor main sein soll. Das macht eigentlich keinen > sinn. Wenn du es als ersten in main aufrufst, dann ist das doch das > gleiche. Ich habe mal eine Implementierung zur Erzeugung von Zufallszahlen gesehen, welche "vor main" bestimmte Bereiche des Hauptspeichers abgeklappert hat, um ein "random seed" zu bekommen. Der Aufruf erfolgt automatisch. Ob aber die vier Sektionen 5-8 zur Abbildung von Abhängigkeiten ausreichen, das könnte eng werden. Dafür würde ich eher mit "weak" arbeiten oder mit einem #define pro Modul. Aufruf der jeweiligen Init-Funktionen dann in einer zentralen Funktion - dies dann aber tatsächlich am Anfang von main().
Krapao schrieb: > Du kannst deinen Code in eine der .initN sections stecken. Johann L. schrieb: > In GCC kannst du Konstruktoren schreiben: Allerdings bleibt die Frage, welchen Vorteil diese Verrenkungen gegenüber einem ganz normalen Aufruf am Anfang von main() hätten.
Rolf Magnus schrieb: > Allerdings bleibt die Frage, welchen Vorteil diese Verrenkungen > gegenüber einem ganz normalen Aufruf am Anfang von main() hätten. Es geschieht automatisch. Kann man nicht vergessen, was im Falle von Zufallszahlen fatal sein könnte. Oder zu spät aufrufen: Im Falle der Zufallszahlen könnte es sein, dass im RAM besserer Inhalt für den "random seed" steht. Wobei ich Dir wg. der Verrenkungen zustimme. Richtig transparent ist es nicht. So richtig portabel sieht es auch nicht aus. Wenn ich das richtig sehe, nicht einmal zwischen den GCC-Varianten (AVR, ARM, ...), da die "linker scripts" bzw. startups abweichen. Die Gegenfrage wäre, warum rufen wir nicht selbst "copy from flash to RAM for initialized data section" oder __libc_init_array() bzw. alle statische Konstruktoren einzeln auf.
Roland H. schrieb: > Es geschieht automatisch. Kann man nicht vergessen, Na ja, ob du nun vergisst, die Funktion am Anfang von main() aufzurufen, oder es in eine der init-Sektionen zu platzieren, der Effekt ist der selbe ;) Oliver
Roland H. schrieb: > Rolf Magnus schrieb: >> Allerdings bleibt die Frage, welchen Vorteil diese Verrenkungen >> gegenüber einem ganz normalen Aufruf am Anfang von main() hätten. > > Es geschieht automatisch. Kann man nicht vergessen, was im Falle von > Zufallszahlen fatal sein könnte. Bei der Variante mit der init-Sektion muß er es ganz genauso von Hand aufrufen. Und bei der Konstruktor-Variante muß er eben von Hand hinter die Funktion schreiben, daß sie automatisch aufgerufen werden soll. Beides kann man genauso einfach vergessen, wie einen Aufruf aus main() heraus. Bei der Konstruktor-Variante kommt noch dazu, daß man nicht weiß, in welcher Reihenfolge sie aufgerufen werden, wenn es mehrere sind, was ein Problem ist, wenn es wie oben erwähnt Abhängigkeiten gibt. Beim manuellen Aufruf kann ich die Reihenfolge passend festlegen. > Kann man nicht vergessen, was im Falle von Zufallszahlen fatal sein könnte. Dafür ist der Aufruf irgendwo versteckt, was das Debugging erschwert. Wenn ich wissen will, ob was richtig initialisiert ist, schaue ich mir zuerst mal main() an und wenn ich da nichts finde, suche ich nach dem Aufruf. Keins davon bringt mir was, weil der Aufruf mit einer der oben angegebenen Methoden quasi verschleiert wird. > Oder zu spät aufrufen: Im Falle der Zufallszahlen könnte es sein, dass im RAM > besserer Inhalt für den "random seed" steht. Das wäre eher keine gute Idee. Aber es kann natürlich allgemein spezielle Fälle geben, in denen es nicht anders geht. > Wobei ich Dir wg. der Verrenkungen zustimme. Richtig transparent ist es > nicht. So richtig portabel sieht es auch nicht aus. Wenn ich das richtig > sehe, nicht einmal zwischen den GCC-Varianten (AVR, ARM, ...), da die > "linker scripts" bzw. startups abweichen. Kann sein, daß die Verwendung der init-Sektionen je nach Target-Architektur unterschiedlich ist. > Die Gegenfrage wäre, warum rufen wir nicht selbst "copy from flash to > RAM for initialized data section" oder __libc_init_array() bzw. alle > statische Konstruktoren einzeln auf. Weil C festlegt, daß das alles schon beim Start von main() gemacht ist. Darauf kann man sich bei einem halbwegs konformen Compiler verlassen.
Oliver schrieb: > Na ja, ob du nun vergisst, die Funktion am Anfang von main() aufzurufen, > oder es in eine der init-Sektionen zu platzieren, der Effekt ist der > selbe ;) Ich meinte den anderen Fall: Entwickler A erstellt die Bibliothek, Entwickler B setzt diese ein. Und vergisst den Aufruf und bemerkt dies nicht, weil es dennoch funktioniert. Und es ist sogar noch praktisch fürs Debuggen, da es immer die gleichen Zufallszahlen gibt ;-)
Rolf Magnus schrieb: > Bei der Variante mit der init-Sektion muß er es ganz genauso von Hand > aufrufen. Der Entwickler der Funktion deklariert es, derjenige der die Funktion verwendet, muss sich um nichts kümmern. Aufrufen muss und darf es keiner, da die Funktion "naked" ist:
1 | /** Bestimme seed aus zufälligem RAM-Inhalt |
2 | * !!! FUNKTION NICHT AUFRUFEN !!! |
3 | * !!! FUNKTION WIRD AUTOMATISCH !!! |
4 | * !!! IN .init8 vor main AUSGEFÜHRT !!! |
5 | */ |
6 | void randomInitSeed(void) __attribute__ ((naked, section (".init8"))); |
Habe ich dort gefunden: Beitrag "Miniprojekt: Lagerfeuer-LED (ATtiny25)", wurde von Johann eingestellt. > Bei der Konstruktor-Variante kommt noch dazu, daß man nicht weiß, in > welcher Reihenfolge sie aufgerufen werden, wenn es mehrere sind, was ein > Problem ist, wenn es wie oben erwähnt Abhängigkeiten gibt. Beim > manuellen Aufruf kann ich die Reihenfolge passend festlegen. Hatte ich schon geschrieben, dass ich es nicht für die beste Methode halte, Abhängigkeiten abzubilden. > Dafür ist der Aufruf irgendwo versteckt, was das Debugging erschwert. > Wenn ich wissen will, ob was richtig initialisiert ist, schaue ich mir > zuerst mal main() an und wenn ich da nichts finde, suche ich nach dem > Aufruf. Keins davon bringt mir was, weil der Aufruf mit einer der oben > angegebenen Methoden quasi verschleiert wird. Auch das hatte ich geschrieben: Es ist nicht transparent. Aus dem Grund mag ich es auch nicht, wenn in den Cortex-Startups SystemInit() aufgerufen wird. > Aber es kann natürlich allgemein > spezielle Fälle geben, in denen es nicht anders geht. Darum ging es mir: Ein Beispiel aufzuzeigen, wo es Sinn machen kann, etwas immer und vor main auszuführen. > Kann sein, daß die Verwendung der init-Sektionen je nach > Target-Architektur unterschiedlich ist. mips-elf-gcc meldet bei obigem Code: warning: 'naked' attribute directive ignored. Einträge im "linker script" für init8 gib es nicht. arm-none-eabi-gcc kompiliert es, mangels Einträgen im "linker script" wird es nie aufgerufen.
Roland H. schrieb: > Ich meinte den anderen Fall: Entwickler A erstellt die Bibliothek, > Entwickler B setzt diese ein. Automatisch, ohne das Entwickler B da Hand anlegen muß, geht das in C nunmal nicht. Aber für solche Fälle hat man dann irgendwann mal C++ und andere objektorientierte Sprachen erfunden. Da gibt es deine nicht-vergessbaren Konstruktoren. Oliver
Oliver schrieb: > Automatisch, ohne das Entwickler B da Hand anlegen muß, geht das in C > nunmal nicht. Wenn Entwickler B xxx.o linkt, weil er eine dort definierte Funktion nutzen will, dann wird im Falle AVR GCC der Code in den dort enthaltenen "naked"-Funktionen ausgeführt, die in eine init-Sektion gelegt wurden. Das entspricht einem "Aufruf" dieser Funktionen, technisch ist es anders. Der "Aufruf" oder besser das Ausführen geschieht implizit, ob er es will oder nicht. Ohne das "linker script" zu verändern, kann er es m. W. nach auch nicht verhinden. Das verstehe ich als "logischen automatischen Aufruf", um den sich B nicht kümmern muss. Eine Art "default constructor" für C auf Ebene einer Objekt-Datei. Hat Johann weiter oben schon beschrieben: Johann L. schrieb: > Eine witere Möglichkeit im avr-gcc sind naked-Funktionen, die allerdings > keinen Frame haben dürfen und explizit in einer .initN Section plaziert > werden müssen. nebst Johann L. schrieb: > Damit kann sich ein Modul selbst initialisieren ohne daß dies von Hand > in main geschehen muss. main sieht davon nix. Meinetwegen ist das nicht C. Es ist eine Möglichkeit beim AVR GCC im Zusammenspiel mit dem Linker.
Constructors werden in GCC unterstützt, siehe obigen Link auf die GCC Dokumentation. Und: Bitte nicht constructor mit naked-Funktionen in init-Sections verwechseln! Letztere sind nicht zu anderen Architekturen portabel, auch nicht innerhalb von GCC. constructor ist hingegen portabel und es wird für alle von GCC unterstützten Maschinen angeboten. Wie das konkret für eine Hardware gelöst wirst, ist egal: Man braucht wohl kaum Binärkompatibilität. i++ wird ja auch anders übersetzt und ist im Binärresultat nicht portierbar... Falls man "early Initializing" einsetzen will, dann würde ich constructor vorziehen vor naked wenn es nicht auf Teufel-komm-raus darum geht, das letzte Byte an Codegröße rauszuquetschen. Jeder der ein C++ Programm schreibt, würde ein Feature wie constructor einer Hand-Initialisierung in main vorziehen, warum sollte man das nicht auch in C so handhaben: Die Module sind self-contained und es muss nicht eine Quelle angefasst werden die nix damit zu tun hat. Wenn Entwickler nicht miteinander kommunizieren oder keinen Plan von dem bekommen, was andere machen, dann wird das schwerlich durch Verwendung anderer Design-Pattern lösbar sein. Hier hilft nur, das Übel an der Wurzel zu packen. Das Debugger-Argument ist mir nicht nachvollziehbar. Vielleicht weil ich keine Degugger verwende. Und weil ich nicht sehe, weil sich das Design eines Programmes nach einem Debugger richten soll anstsatt nach den Erfordernissen der zu lösenden Aufgaben. Wenn Debugging das Kriterium ist, dann müsste man gleich ohne Optimierung arbeiten.
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.