Jak dokładnie działa atrybut ((konstruktor))?
Wydaje się całkiem jasne, że to ma wszystko ustawić.
- kiedy dokładnie działa?
- Dlaczego są dwa nawiasy?
- Czy
__attribute__
jest funkcją? Makro? Składnia? - czy to działa w C? C++?
- czy funkcja, z którą pracuje, musi być statyczna?
- Kiedy
__attribute__((destructor))
ucieka?
__attribute__((constructor))
static void initialize_navigationBarImages() {
navigationBarImages = [[NSMutableDictionary alloc] init];
}
__attribute__((destructor))
static void destroy_navigationBarImages() {
[navigationBarImages release];
}
5 answers
- jest uruchamiany po załadowaniu biblioteki współdzielonej, zazwyczaj podczas uruchamiania programu.
- tak wyglądają wszystkie atrybuty GCC; prawdopodobnie, aby odróżnić je od wywołań funkcji.
- składnia specyficzna dla GCC.
- tak, to działa również w C i C++.
- nie, funkcja nie musi być statyczna.
- Destruktor jest uruchamiany, gdy biblioteka współdzielona jest rozładowana, zazwyczaj przy wyjściu programu.
Więc sposób pracy konstruktorów i destruktorów polega na tym, że wspólne plik obiektu zawiera sekcje specjalne (.ctors i .dtors na ELF), które zawierają odniesienia do funkcji oznaczonych odpowiednio atrybutami constructor i destructor. Po załadowaniu / rozładowaniu biblioteki program dynamic loader (ld.so lub somesuch) sprawdza, czy takie sekcje istnieją, a jeśli tak, wywołuje funkcje w nich wymienione.
Jeśli się nad tym zastanowić, prawdopodobnie jest jakaś podobna magia w normalnym linkerze statycznym, więc ten sam kod jest uruchamiany podczas uruchamiania / zamykania niezależnie od tego, czy użytkownik wybiera łącza statyczne czy dynamiczne.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-12 13:54:07
.init
/.fini
nie jest przestarzały. To wciąż część standardu elfów i ośmielę się powiedzieć, że będzie na zawsze. Kod w .init
/.fini
jest uruchamiany przez loader / runtime-linker, gdy kod jest ładowany/rozładowywany. Tzn. przy każdym załadowaniu ELF (na przykład biblioteka współdzielona) zostanie uruchomiony kod .init
. Nadal można użyć tego mechanizmu, aby osiągnąć mniej więcej to samo, co w przypadku __attribute__((constructor))/((destructor))
. To stara szkoła, ale ma pewne zalety.
.ctors
/.dtors
mechanizm wymaga np. wsparcia przez system-rtl/loader / linker-script. Nie jest pewne, czy będzie to dostępne we wszystkich systemach, na przykład głęboko osadzonych systemach, w których kod wykonuje się na gołym metalu. Np. nawet jeśli {[6] } jest wspierane przez GCC, nie jest pewne, czy będzie działać, ponieważ to do linkera należy uporządkowanie go i do loadera (lub w niektórych przypadkach, boot-code), aby go uruchomić. Do użycia .init
/.fini
zamiast tego najprostszym sposobem jest użycie FLAG linkera: - INIT & - fini(tzn. z linii poleceń GCC składnia byłaby -Wl -init my_init -fini my_fini
).
O wspieraniu systemu obie metody, jedną z możliwych korzyści jest to, że kod w .init
jest uruchamiany przed .ctors
, a kod w .fini
po .dtors
. Jeśli kolejność jest istotna, jest to co najmniej jeden prosty, ale łatwy sposób na rozróżnienie funkcji init / exit.
Główną wadą jest to, że nie można łatwo mieć więcej niż jedną funkcję _init
i jedną funkcję _fini
na każdy moduł ładowalny i prawdopodobnie musielibyśmy fragmentować kod w Więcej .so
niż motywować. Innym jest to, że przy użyciu opisanej powyżej metody linkera zastępuje się oryginalne funkcje _init i _fini
domyślne (dostarczone przez crti.o
). W tym miejscu zwykle występują wszelkiego rodzaju inicjalizacje (w Linuksie jest to miejsce, w którym inicjowane jest globalne przypisanie zmiennych). Sposób, który jest opisany tutaj
Zauważ w linku powyżej, że kaskadowe do oryginału {[22] } nie jest potrzebne, ponieważ jest nadal na miejscu. call
w inline assembly jest jednak x86-mnemonic i wywołanie funkcji z assembly wyglądałoby zupełnie inaczej dla wielu innych architektury (np. ARM). Kod nie jest przezroczysty.
.init
/.fini
oraz .ctors
/.detors
mechanizmy są podobne, ale nie do końca. Kod w .init
/.fini
działa "jak jest". Czyli można mieć kilka funkcji w .init
/.fini
, ale trudno jest umieścić je tam w pełni transparentnie w czystym C bez rozbijania kodu w wielu małych plikach .so
.
.ctors
/.dtors
są inaczej zorganizowane niż .init
/.fini
. .ctors
/.dtors
sekcje to tylko tabele z wskaźnik do funkcji, a "caller" jest pętlą dostarczaną przez system, która wywołuje każdą funkcję pośrednio. Tzn. loop-caller może być specyficzny dla architektury, ale ponieważ jest częścią systemu (jeśli w ogóle istnieje), nie ma to znaczenia.
Poniższy fragment dodaje nowe wskaźniki funkcji do tablicy funkcji .ctors
, zasadniczo tak samo jak robi to __attribute__((constructor))
(metoda może współistnieć z __attribute__((constructor)))
.
#define SECTION( S ) __attribute__ ((section ( S )))
void test(void) {
printf("Hello\n");
}
void (*funcptr)(void) SECTION(".ctors") =test;
void (*funcptr2)(void) SECTION(".ctors") =test;
void (*funcptr3)(void) SECTION(".dtors") =test;
Można też dodać Wskaźniki funkcji do zupełnie innego wymyślonego przez siebie sekcja. Zmodyfikowany skrypt linkera i dodatkowa funkcja naśladująca loader .ctors
/.dtors
W takim przypadku potrzebna jest pętla. Ale dzięki niemu można uzyskać lepszą kontrolę nad kolejnością wykonania, dodać in-argument i zwracać kod obsługi e. t. a. (na przykład w projekcie C++, byłoby to przydatne, jeśli potrzebujesz czegoś działającego przed lub po globalnych konstruktorach).
Wolałbym __attribute__((constructor))/((destructor))
tam, gdzie to możliwe, jest to proste i eleganckie rozwiązanie, nawet jeśli wydaje się to oszustwem. Dla koderów bare-metalowych takich jak nie zawsze jest to opcja.
Niektóre dobre odniesienia w książce Linkers & loaders.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-10 14:57:30
Ta strona zapewnia doskonałe zrozumienie implementacji atrybutów constructor
i destructor
oraz sekcji wewnątrz ELF, które pozwalają im działać. Po przetrawieniu podanych tutaj informacji, zebrałem trochę dodatkowych informacji i (zapożyczając przykład sekcji od Michaela Ambrusa powyżej) stworzyłem przykład, aby zilustrować pojęcia i pomóc w nauce. Wyniki te przedstawiono poniżej wraz z przykładowym źródłem.
Jak wyjaśniono w tym wątku, constructor
i destructor
atrybuty tworzą wpisy w sekcji .ctors
i .dtors
pliku obiektowego. Odniesienia do funkcji można umieszczać w obu sekcjach na jeden z trzech sposobów. (1) używając atrybutu section
; (2) constructor
i destructor
atrybutów lub (3) z wywołaniem inline-assembly (zgodnie z odnośnikiem w odpowiedzi Ambrusa).
Użycie atrybutów constructor
i destructor
pozwala dodatkowo przypisać priorytet konstruktorowi / destruktorowi, aby kontrolować jego kolejność wykonania przed wywołaniem main()
lub po powrocie. Im niższa podana wartość priorytetu, tym wyższy priorytet wykonania (niższe priorytety wykonują się przed wyższymi priorytetami przed main () -- i po wyższych priorytetach po main ()). Wartości priorytetowe, które podajesz muszą być większe niż100
ponieważ kompilator rezerwuje priorytetowe wartości między 0-100 dla implementacji. A constructor
lub destructor
określone z priorytetem wykonuje przed constructor
lub destructor
określone bez priorytetu.
Z atrybutem "section" lub za pomocą inline-assembly można również umieszczać odwołania do funkcji w sekcji kodu ELF .init
i .fini
, które będą wykonywane odpowiednio przed dowolnym konstruktorem i po dowolnym destruktorze. Wszelkie funkcje wywołane przez odwołanie do funkcji umieszczone w sekcji .init
, będą wykonywane przed samym odwołaniem do funkcji (jak zwykle).
Starałem się zilustrować każdy z nich w poniższym przykładzie:
#include <stdio.h>
#include <stdlib.h>
/* test function utilizing attribute 'section' ".ctors"/".dtors"
to create constuctors/destructors without assigned priority.
(provided by Michael Ambrus in earlier answer)
*/
#define SECTION( S ) __attribute__ ((section ( S )))
void test (void) {
printf("\n\ttest() utilizing -- (.section .ctors/.dtors) w/o priority\n");
}
void (*funcptr1)(void) SECTION(".ctors") =test;
void (*funcptr2)(void) SECTION(".ctors") =test;
void (*funcptr3)(void) SECTION(".dtors") =test;
/* functions constructX, destructX use attributes 'constructor' and
'destructor' to create prioritized entries in the .ctors, .dtors
ELF sections, respectively.
NOTE: priorities 0-100 are reserved
*/
void construct1 () __attribute__ ((constructor (101)));
void construct2 () __attribute__ ((constructor (102)));
void destruct1 () __attribute__ ((destructor (101)));
void destruct2 () __attribute__ ((destructor (102)));
/* init_some_function() - called by elf_init()
*/
int init_some_function () {
printf ("\n init_some_function() called by elf_init()\n");
return 1;
}
/* elf_init uses inline-assembly to place itself in the ELF .init section.
*/
int elf_init (void)
{
__asm__ (".section .init \n call elf_init \n .section .text\n");
if(!init_some_function ())
{
exit (1);
}
printf ("\n elf_init() -- (.section .init)\n");
return 1;
}
/*
function definitions for constructX and destructX
*/
void construct1 () {
printf ("\n construct1() constructor -- (.section .ctors) priority 101\n");
}
void construct2 () {
printf ("\n construct2() constructor -- (.section .ctors) priority 102\n");
}
void destruct1 () {
printf ("\n destruct1() destructor -- (.section .dtors) priority 101\n\n");
}
void destruct2 () {
printf ("\n destruct2() destructor -- (.section .dtors) priority 102\n");
}
/* main makes no function call to any of the functions declared above
*/
int
main (int argc, char *argv[]) {
printf ("\n\t [ main body of program ]\n");
return 0;
}
Wyjście:
init_some_function() called by elf_init()
elf_init() -- (.section .init)
construct1() constructor -- (.section .ctors) priority 101
construct2() constructor -- (.section .ctors) priority 102
test() utilizing -- (.section .ctors/.dtors) w/o priority
test() utilizing -- (.section .ctors/.dtors) w/o priority
[ main body of program ]
test() utilizing -- (.section .ctors/.dtors) w/o priority
destruct2() destructor -- (.section .dtors) priority 102
destruct1() destructor -- (.section .dtors) priority 101
Przykład pomógł cementować zachowanie konstruktora/destruktora, mam nadzieję, że przyda się również innym.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-23 08:09:16
Oto "konkretny" (i prawdopodobnie przydatny) przykład Jak, dlaczego i kiedy używać tych poręcznych, ale nieestetycznych konstrukcji...
Xcode używa "globalnego" "domyślnego użytkownika", aby zdecydować, która klasa XCTestObserver
wypluwa serce do oblężonej konsoli.
W tym przykładzie... kiedy domyślnie załaduję tę bibliotekę psuedo, nazwijmy ją... / Align = "center" bgcolor = "# e0ffe0 " / cesarz Chin / / align = center / .
OTHER_LDFLAGS = -ldemure
Chcę za..
Przy obciążeniu (tj. kiedy
XCTest
ładuje mój pakiet testowy), nadpisuje klasę" default "XCTest
" observer"... (poprzez funkcjęconstructor
) PS: z tego co wiem.. wszystko, co tu zrobiono, może być wykonane z równoważnym efektem w metodzie+ (void) load { ... }
mojej klasy.-
Zróbcie testy.... w tym przypadku, z mniejszą szczegółowością w logach (implementacja na żądanie)
Zwraca klasę" global "
XCTestObserver
do jej nieskazitelnego stanu.. aby nie zepsuć innych / Align = "left" / linked tolibdemure.a
). Myślę, że historycznie było to zrobione wdealloc
.. ale nie zamierzam zadzierać z tą starą wiedźmą.
Więc...
#define USER_DEFS NSUserDefaults.standardUserDefaults
@interface DemureTestObserver : XCTestObserver @end
@implementation DemureTestObserver
__attribute__((constructor)) static void hijack_observer() {
/*! here I totally hijack the default logging, but you CAN
use multiple observers, just CSV them,
i.e. "@"DemureTestObserverm,XCTestLog"
*/
[USER_DEFS setObject:@"DemureTestObserver"
forKey:@"XCTestObserverClass"];
[USER_DEFS synchronize];
}
__attribute__((destructor)) static void reset_observer() {
// Clean up, and it's as if we had never been here.
[USER_DEFS setObject:@"XCTestLog"
forKey:@"XCTestObserverClass"];
[USER_DEFS synchronize];
}
...
@end
Bez flagi linkera... (Moda-policyjny Rój Cupertino żądający zemsty, jednak domyślne Apple przeważa, Jak jest pożądane, tutaj )
Z flagą -ldemure.a
linkera... (Zrozumiałe wyniki, gasp ... "dzięki constructor
/destructor
"... Wiwat tłumu )
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-21 19:34:20
Oto kolejny Beton example.It jest dla wspólnej biblioteki. Główną funkcją biblioteki współdzielonej jest komunikacja z czytnikiem kart inteligentnych. Ale może również odbierać "informacje konfiguracyjne" w czasie wykonywania przez udp. Udp jest obsługiwane przez wątek, który musi być uruchomiony w czasie początkowym.
__attribute__((constructor)) static void startUdpReceiveThread (void) {
pthread_create( &tid_udpthread, NULL, __feigh_udp_receive_loop, NULL );
return;
}
Biblioteka została napisana w c.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-17 15:07:42