Forum: Compiler & IDEs implizites modul init


von Adib (Gast)


Lesenswert?

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.
--

von Peter II (Gast)


Lesenswert?

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.

von Adib (Gast)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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();
}

von SF (Gast)


Lesenswert?

Schau mal hier bei den "The .initN Sections": 
http://www.nongnu.org/avr-libc/user-manual/mem_sections.html (Ganz unten 
ist ein Beispiel)

von Peter II (Gast)


Lesenswert?

wenn es c++ ist, könnte man in jedem modul eine globale instanz ein 
klasse anlegen und damit wird der Konstruktor vor main aufgerufen.

von Noname (Gast)


Lesenswert?

>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?

von Krapao (Gast)


Lesenswert?

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

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

von Roland H. (batchman)


Lesenswert?

> 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().

von Rolf M. (rmagnus)


Lesenswert?

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.

von Roland H. (batchman)


Lesenswert?

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.

von Oliver (Gast)


Lesenswert?

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

von Rolf M. (rmagnus)


Lesenswert?

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.

von Roland H. (batchman)


Lesenswert?

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 ;-)

von Roland H. (batchman)


Lesenswert?

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.

von Oliver (Gast)


Lesenswert?

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

von Roland H. (batchman)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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
Noch kein Account? Hier anmelden.