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.
--
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();
}
>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
staticvoid__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
intmain()
4
{
5
printf("Hallo Welt\n");
6
return0;
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:
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.