Comment fonctionne exactement __attribute__((constructeur)) de travail?
Il semble assez clair qu'il est censé mettre les choses en place.
- Quand exactement t-il fonctionner?
- Pourquoi il y a deux parenthèses?
- Est
__attribute__
une fonction? Une macro? Syntaxe? - Fait ce travail en C? C++?
- La fonction, il fonctionne avec le besoin d'être statique?
- Quand
__attribute__((destructor))
exécuter?
__attribute__((constructor))
static void initialize_navigationBarImages() {
navigationBarImages = [[NSMutableDictionary alloc] init];
}
__attribute__((destructor))
static void destroy_navigationBarImages() {
[navigationBarImages release];
}
Vous devez vous connecter pour publier un commentaire.
Donc, la façon dont les constructeurs et les destructeurs de travail est que le fichier objet partagé contient des sections spéciales (.ctors et .dtors sur ELF) qui contiennent les références aux fonctions marquées avec le constructeur et le destructeur attributs, respectivement. Lorsque la bibliothèque est chargé/à vide le chargeur dynamique de programme (ld.ou somesuch) vérifie si ces articles existent, et si oui, appelle les fonctions qui y sont référencés.
Venez pour penser à elle, il y a probablement une magie similaire à la normale statique de l'éditeur de liens, de sorte que le même code est exécuté au démarrage/d'arrêt, peu importe si l'utilisateur choisit statique ou dynamique de la liaison.
#define __attribute__(x)
). Si vous disposez de plusieurs attributs, par exemple,__attribute__((noreturn, weak))
, il serait difficile de "macro" à " si il y avait seulement une série de crochets..init/.fini
. (Vous pouvez valablement avoir plusieurs constructeurs et destructeurs dans une seule unité de traduction, passons à plusieurs dans une seule bibliothèque -- comment cela fonctionnerait-il?) Au lieu de cela, sur les plates-formes à l'aide de format binaire ELF (Linux, etc.), les constructeurs et les destructeurs sont référencés dans la.ctors
et.dtors
sections de l'en-tête. Certes, dans les vieux jours, les fonctions nomméinit
etfini
serait de fonctionner sur la dynamique de la bibliothèque de charger et décharger si elles existaient, mais c'est obsolète maintenant, remplacé par ce mécanisme amélioré.__attribute__
est si vous n'êtes pas à l'aide de gcc, depuis que, lui aussi, est un gcc extension.__attribute__
a d'abord été inventé, devinez qui C compilateurs ont dû être pris en charge..init
/.fini
n'est pas obsolète. C'est toujours une partie de l'ELFE de la norme et j'oserais dire que ce sera pour toujours. Code.init
/.fini
est géré par le chargeur/runtime-linker lorsque le code est chargé/déchargé. I. e. sur chaque ELFE de la charge (par exemple une bibliothèque partagée) du code de.init
sera exécuté. Il est encore possible d'utiliser ce mécanisme pour atteindre environ la même chose qu'avec les__attribute__((constructor))/((destructor))
. C'est de la vieille école, mais il a quelques avantages..ctors
/.dtors
mécanisme par exemple besoin d'un soutien par le système-rtl/chargeur/linker script. C'est loin d'être certain d'être disponible sur tous les systèmes, par exemple profondément les systèmes embarqués où le code s'exécute sur le métal nu. I. e. même si__attribute__((constructor))/((destructor))
est pris en charge par GCC, il n'est pas certain qu'il va exécuter en tant qu'il est à l'éditeur de liens à l'organiser et à le chargeur (ou, dans certains cas, du code de démarrage) pour l'exécuter. Pour utiliser.init
/.fini
au lieu de cela, le plus simple est d'utiliser l'éditeur de liens indicateurs: -init & -fini (c'est à dire à partir de GCC en ligne de commande, la syntaxe serait-Wl -init my_init -fini my_fini
).Sur le système de soutien les deux méthodes, l'une des possibles avantage est que le code dans
.init
est exécuté avant.ctors
et le code dans.fini
après.dtors
. Si la commande est pertinent, c'est au moins l'un brut, mais moyen facile de distinguer entre init/sortie de fonctions.Un inconvénient majeur est que vous ne pouvez pas facilement avoir plus d'un
_init
et un_fini
fonction pour chaque module chargeable et aurait probablement des fragments de code en plus.so
que jamais motivé. Une autre est que lors de l'utilisation de l'éditeur de liens, la méthode décrite ci-dessus, on remplace l'original _init et_fini
fonctions par défaut (fourni parcrti.o
). C'est là où toutes sortes d'initialisation se produisent habituellement (sur Linux c'est là global affectation de variable est initialisée). Un moyen de contourner cela est décrit iciAvis dans le lien ci-dessus qu'une cascade à l'origine
_init()
n'est pas nécessaire, car elle est toujours en place. Lecall
dans la ligne d'assemblage est cependant x86-mnémonique et l'appel d'une fonction à partir de l'assemblée serait un look complètement différent de beaucoup d'autres architectures (comme le BRAS par exemple). I. e. le code n'est pas transparent..init
/.fini
et.ctors
/.detors
mécanismes sont similaires, mais pas tout à fait. Code.init
/.fini
fonctionne "comme est". I. e. vous pouvez avoir plusieurs fonctions dans.init
/.fini
, mais c'est autant que je sache, la syntaxe est difficile de les mettre pleinement là de manière transparente dans le plus pur C sans en briser le code dans de nombreuses petites.so
fichiers..ctors
/.dtors
sont organisées de manière différente que.init
/.fini
..ctors
/.dtors
sections ne sont qu'à deux tables avec des pointeurs de fonctions, et le "visiteur" est fourni par le système de boucle qui appelle chaque fonction indirectement. I. e. la boucle de l'appelant peuvent être spécifiques à l'architecture, mais comme c'est une partie du système (si elle existe c'est à dire), il n'a pas d'importance.L'extrait de code suivant ajoute de nouveaux pointeurs de fonction à la
.ctors
fonction de la matrice de, essentiellement de la même manière que__attribute__((constructor))
n' (méthode peut coexister avec__attribute__((constructor)))
.On peut aussi ajouter les pointeurs de fonction pour un de complètement différent de soi-inventé la section. Une modification de linker script et une fonction supplémentaire imitant le chargeur
.ctors
/.dtors
boucle est nécessaire dans de tels cas. Mais avec lui, on peut obtenir un meilleur contrôle sur l'exécution de la commande, ajouter en argument et le code de retour de la manipulation de l'e.t.un. (Dans un projet C++ par exemple, il serait utile si besoin de quelque chose en cours d'exécution avant ou après le mondial des constructeurs).Je préfère
__attribute__((constructor))/((destructor))
si possible, c'est une solution simple et élégante même, il se sent comme de la tricherie. Pour bare-metal programmeurs comme moi, ce n'est tout simplement pas toujours une option.Quelques bonnes références dans le livre Linkers & chargeurs.
Cette page offre une grande compréhension à propos de la
constructor
etdestructor
attribut de mise en œuvre et les sections à l'intérieur de l'intérieur de l'ELFE qui leur permettent de travailler. Après digestion les informations fournies ici, j'ai compilé un peu d'informations supplémentaires et (emprunt à la section exemple de Michael Ambrus ci-dessus) a créé un exemple pour illustrer les concepts et les aider dans mon apprentissage. Ces résultats sont fournis ci-dessous avec l'exemple de la source.Comme expliqué dans ce fil, le
constructor
etdestructor
attributs de créer des entrées dans le.ctors
et.dtors
section du fichier de l'objet. Vous pouvez placer des références à des fonctions soit dans la section de l'une des trois façons. (1) à l'aide de lasection
attribut; (2)constructor
etdestructor
attributs ou (3) avec un inline-assemblée appel (tel que référencé le lien dans Ambrus de réponse).L'utilisation de
constructor
etdestructor
attributs vous permettent en outre d'attribuer une priorité pour le constructeur/destructeur pour le contrôle de son ordonnance d'exécution avantmain()
est appelé ou après qu'il renvoie. La baisse de la valeur de priorité donné est élevé, plus la priorité d'exécution (baisse des priorités d'exécution des priorités avant le main () - et à des priorités plus importantes après le main() ). Les valeurs de priorité de vous donner doit être supérieure à100
que le compilateur réserve les valeurs de priorité entre 0-100 pour la mise en œuvre. Unconstructor
oudestructor
spécifié avec la priorité est exécuté avant unconstructor
oudestructor
spécifié sans priorité.Avec la section " attribut ou avec inline-assemblage, vous pouvez également placer des références de fonction dans le
.init
et.fini
ELFE de la section de code qui s'exécute avant de tout constructeur et après tout destructeur, respectivement. Toutes les fonctions appelées par la fonction de référence placé dans le.init
section, exécute avant que la fonction de référence lui-même (comme d'habitude).J'ai essayé d'illustrer chacun de ceux qui, dans l'exemple ci-dessous:
de sortie:
L'exemple aidé à cimenter le constructeur/destructeur de comportement, j'espère que ce sera utile à d'autres.
MAX_RESERVED_INIT_PRIORITY
), et qu'ils étaient les mêmes que C++ (init_priority
) 7.7 C++Spécifique de la Variable, une Fonction, et le Type des Attributs. Ensuite, j'ai essayé avec99
:warning: constructor priorities from 0 to 100 are reserved for the implementation [enabled by default] void construct0 () __attribute__ ((constructor (99)));
.section
attribut ou assembly en ligne à la place d'une fonction de référence dans le.init
section de l'ELFE exécutable, alors que la fonction sera appelée avant tout fonction placés dans un fichier de l'objet par la méthode ou avecconstructor
attribut. Si vous écrivez plus d'un appel dans le.init
section, les fonctions seraient appelés dans l'ordre placé. Un deuxième appel ajoute simplement uncall somefunction
entrée dans le ELF.init
section.int elf_init2 (void)
fonction en dessous de l'originalint elf_init(void)
il sera appelé après l'originalelf_init
mais avant tout les fonctions déclarées avec leconstructor
attribut.Ici est "concrète" (et peut-être utile) exemple de comment, pourquoi, et quand à l'utilisation de ces pratiques mais disgracieux constructions...
Xcode utilise un "global", "utilisateur par défaut" pour décider qui
XCTestObserver
classe vomit c'est coeur à la assiégée console.Dans cet exemple... quand je implicitement charge de cette pseudo-bibliothèque, nous allons l'appeler...
libdemure.a
, par l'intermédiaire d'un drapeau dans mon test de la cible à la..Je veux..
À charge (c'est à dire. lorsque
XCTest
charges de mon test bundle), remplacer le "par défaut"XCTest
"observateur" de la classe... (via leconstructor
fonction) PS: aussi loin Que je peux dire.. tout ce qui est fait ici pourrait être fait d'effet équivalent à l'intérieur de ma classe+ (void) load { ... }
méthode.exécuter mes tests.... dans ce cas, avec moins inepte de verbosité dans les journaux (mise en œuvre sur demande)
Retour du "global"
XCTestObserver
classe à l'état primitif.. afin de ne pas encrasser les autresXCTest
pistes qui n'ont pas pris le train en marche (aka. lié àlibdemure.a
). Je suppose que c'historiquement a été fait dansdealloc
.. mais je ne suis pas sur le point de commencer à jouer avec cette vieille sorcière.Donc...
Sans l'éditeur de liens drapeau... (de la Fashion-police de l'essaim de Cupertino exigeants châtiment, mais par défaut d'Apple l'emporte, que souhaité, ici)
AVEC le
-ldemure.a
de l'éditeur de liens drapeau... (Compréhensible résultats, gasp... "merciconstructor
/destructor
"... acclamations de la Foule)Voici un autre exemple concret.C'est une bibliothèque partagée. La bibliothèque partagée est la fonction principale est de communiquer avec un lecteur de carte à puce. Mais il peut également recevoir de l'information de configuration' lors de l'exécution sur udp. L'udp est gérée par un thread qui DOIT être commencé au moment de l'initialisation.
La bibliothèque a été écrit en c.