__attribute __((构造函数))如何工作?
这似乎很清楚,它应该设置的东西。
- 什么时候运行?
- 为什么有两个括号?
-
__attribute__
是一个函数吗? macros? 句法? - 这在C中工作吗? C ++?
- 它的function需要是静态的吗?
- 什么时候
__attribute__((destructor))
运行?
目标C中的示例 :
__attribute__((constructor)) static void initialize_navigationBarImages() { navigationBarImages = [[NSMutableDictionary alloc] init]; } __attribute__((destructor)) static void destroy_navigationBarImages() { [navigationBarImages release]; }
- 它在加载共享库时运行,通常在程序启动过程中。
- 这就是GCC的所有属性。 大概是为了区别于函数调用。
- GCC特定的语法。
- 是的,这也适用于C和C ++。
- 不,该function不需要是静态的。
- 卸载共享库时,通常在程序出口处运行析构函数。
因此,构造函数和析构函数的工作方式是共享目标文件包含特殊部分(ELF中的.ctors和.dtors),它们分别包含对构造函数和析构函数属性标记的函数的引用。 当加载/卸载程序库时,dynamic加载程序(ld.so或somesuch)会检查这些段是否存在,如果存在,调用其中引用的函数。
想想看,在普通的静态链接器中可能有一些类似的魔术,所以无论用户select静态链接还是dynamic链接,都会在启动/closures时运行相同的代码。
.init
/ .fini
不被弃用。 它仍然是ELF标准的一部分,我敢说这将是永恒的。 当代码被加载/卸载时, .init
/ .fini
代码由加载器/运行时链接器运行。 即在每个ELF加载(例如一个共享库)代码在.init
将运行。 仍然有可能使用该机制来实现与__attribute__((constructor))/((destructor))
。 这是老派,但它有一些好处。
.ctors
/ .dtors
机制,例如需要system-rtl / loader / linker-script的支持。 这在所有系统上都是不可靠的,例如代码在裸机上执行的深度embedded式系统。 即使__attribute__((constructor))/((destructor))
被GCC支持,也不能肯定它会运行,因为它由链接器来组织和加载器(或在某些情况下,启动代码)运行。 要使用.init
/ .fini
代替,最简单的方法是使用链接器标志:-init&-fini(即从GCC命令行,语法将是-Wl -init my_init -fini my_fini
)。
在支持这两种方法的系统上,一个可能的好处是.init
中的代码在.fini
之前运行,在.fini
之后在.fini
之后.dtors
。 如果顺序是相关的,至less有一个原始的,但简单的方法来区分init / exit函数。
一个主要的缺点是你不能轻易地为每个可载入模块使用多个_init
和一个_fini
函数,并且可能必须将代码片段.so
成更多的动机。 另一个是,当使用上面描述的链接器方法时,将replace原始的_init和_fini
默认函数(由crti.o
提供)。 这是通常发生各种初始化的地方(在Linux上这是全局variables赋值初始化的地方)。 这里介绍一种解决方法
注意在上面的链接中,不需要级联到原始的_init()
,因为它仍然在位。 然而,内联程序集中的call
是x86助记符,从程序集中调用函数对于许多其他体系结构(例如ARM)看起来完全不同。 即代码不透明。
.init
/ .fini
和.ctors
/ .detors
机制类似,但不完全相同。 .init
/ .fini
代码按“原样”运行。 也就是说,你可以在.init
/ .fini
有几个函数,但是AFAIK语法上很难在纯C中完全透明地将它们放在那里,而不会破坏许多小的.so
文件中的代码。
.ctors
/ .dtors
与.init
/ .fini
。 .ctors
/ .dtors
部分都只是指向函数的表,“调用者”是系统提供的循环,间接调用每个函数。 也就是说,循环调用者可以是体系结构特定的,但是因为它是系统的一部分(如果它全部存在的话)并不重要。
下面的片段为.ctors
函数数组添加了新的函数指针,主要和__attribute__((constructor))
一样(方法可以和__attribute__((constructor)))
共存__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;
也可以将函数指针添加到一个完全不同的自创部分。 在这种情况下,需要修改链接器脚本和模拟加载器.ctors
/ .dtors
循环的附加函数。 但是用它可以更好地控制执行顺序,添加in-argument和返回代码处理eta(例如,在一个C ++项目中,如果需要在全局构造函数之前或之后运行的东西,这将是有用的)。
我更喜欢__attribute__((constructor))/((destructor))
,这是一个简单而优雅的解决scheme,即使它看起来像是作弊。 对于像我这样的裸机编码器来说,这并不总是一种select。
在书籍连接器和装载机一些很好的参考。
本页面提供了有关constructor
和destructor
属性实现以及ELF内允许它们工作的部分的很好理解。 在提供了这里提供的信息之后,我汇编了一些额外的信息(借用上面的Michael Ambrus的部分示例)创build了一个例子来说明概念并帮助我学习。 下面提供了这些结果以及示例源。
正如此线程中所解释的, constructor
和destructor
属性在对象文件的.ctors
和.dtors
部分中创build条目。 您可以通过三种方法之一将参考放在任一部分的函数中。 (1)使用section
属性; (2) constructor
和destructor
属性,或(3)内联程序集调用(参考Ambrus的答案中的链接)。
constructor
和destructor
的使用允许你在调用main()
之前或返回之后,为构造函数/析构函数额外地分配一个优先级来控制它的执行顺序。 给定的优先级值越低,执行优先级越高(在main()之前的较高优先级之前执行的较低优先级 – 在main()之后的较高优先级之后执行)。 您给出的优先级值必须大于100
因为编译器会保留0-100之间的优先级值以供实施。 用优先级指定的constructor
或destructor
没有优先级指定的constructor
或destructor
之前执行。
通过'section'属性或者inline-assembly,你也可以把函数引用放在.init
和.fini
ELF代码段中,它们分别在任何构造函数之前和任何析构函数之后执行。 放在.init
部分中的函数引用所调用的任何函数都将在函数引用之前执行(如往常一样)。
我试图说明下面的例子中的每一个:
#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; }
输出:
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
这个例子帮助巩固了构造器/析构器的行为,希望对其他人也有用。
这是一个“具体的”(也可能是有用的 )例子, 说明如何,为什么以及何时使用这些方便而又难看的结构。
Xcode使用“全局”“用户默认”来决定哪个XCTestObserver
类将它的心脏传递给处于困境的控制台。
在这个例子中…当我隐式加载这个伪文库时,我们把它称为… libdemure.a
,通过我的testing目标中的一个标志。
OTHER_LDFLAGS = -ldemure
我要..
-
在加载时(即,当
XCTest
加载我的testing包时),重写“默认”XCTest
“观察者”类…(通过constructor
函数)PS:据我所知,在这里完成的任何事情都可以用等价效果在我的class级+ (void) load { ... }
方法。 -
运行我的testing….在这种情况下,在日志中更less的冗长(根据请求实现)
-
将“全局”
XCTestObserver
类返回到原始状态,以免其他XCTest
运行(没有链接到libdemure.a
)。 我想这在历史上是dealloc
完成的..但我不会开始搞乱那个老巫婆。
所以…
#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, ie "@"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
没有链接标志…(时尚警察群体库比蒂诺要求报复 ,但苹果的默认优势, 如所期望的,在这里 )
用-ldemure.a
链接器标志…(可理解的结果, 喘气 …“谢谢constructor
/ destructor
”… 人群欢呼声 )
这是另一个具体的例子。它是一个共享库。 共享库的主要function是与智能卡读卡器通信。 但它也可以通过udp在运行时接收“configuration信息”。 udp由一个线程处理,它必须在init时启动。
__attribute__((constructor)) static void startUdpReceiveThread (void) { pthread_create( &tid_udpthread, NULL, __feigh_udp_receive_loop, NULL ); return; }
该库是用c ++编写的。