attribute属性
__attribute__介绍
attribute的格式.
__attribute__((attribute-list))
attribute属性可以在编译的时候告诉编译器函数, 结构体的属性是什么, 进行某些编译优化, 也可以提供更加准确的错误检查.
attribute是GNU特有的特性
这里主要说明attribute的4个属性, 分别是packed, aligned, constructor, destructor.
packed属性
设置结构体和类的对齐方式, 它使得变量和结构体,类按最小对齐方式来对齐变量.
我们直接写
struct Temp
{
char j;
int i;
}temp;
struct Attribute_t
{
char i;
int j;
}__attribute__((packed))attribute_t;
可以看出来, attribute是最小对齐, 中间没有任何空的内存, 而没有使用的结构体是严格按照规定的对齐方式来对齐数据的. 这种对齐在内核代码里面最常见, 很多的结构体希望尽量的小, 节约内存空间, 就会用到packed属性, x86源码就特别常见的.
aligned属性
aligned
属性跟packed有些相反, aligned是可以自己设置对齐的大小, 格式为
__attribute__((aligned)); // 默认属性, 编译器自动设置对齐大小
__attribute__((aligned(n))); // n为自己设置的对齐大小
编写例子来看
struct Temp
{
char i;
int j;
}__attribute__((aligned)) temp;
struct Attribute_t
{
char i;
int j;
int t;
}__attribute__((aligned(8))) attribute_t;
可以看到temp的默认属性, 设置的是16字节对齐, 我们设置aligned(8)是按照8字节进行对齐. 之所有attribute多了一个变量是为了字节大小超过8字节, 对齐后才是16字节.
而设置对齐的意义在于
- aligned后面不紧跟一个指定的数字值,编译器将依据你的目标机器情况使用最大最有益的对齐方式。选择针对目标机器最大的对齐方式,可以提高拷贝操作的效率.
- 自己手动设置对齐方式可以调整内存空间的位置
constructor属性
设置了这个属性的函数将会在main函数开始之前执行.
我们可以来尝试一下 , 设置了这个属性是不是真的在main函数开始执行之前执行的.
__attribute__((constructor)) void print()
{
printf("first\n");
}
int main()
{
printf("last\n");
exit(0);
}
事实就是这样. 当然, 我们探索也不会这样简单的结束啊, 要看看在那里调用了设置了属性的函数
objdump a.out
上面是设置属性的ATT汇编, 下面是去掉设置属性的ATT汇编
可以看出来, 设置属性的在编译期间设置将其函数加入到 .init的.init_array
数组中. 其实是加入了ELF中的.ctors
和dtors
中, 并且对齐函数设置了优先级, 此优先级比main函数的优先级高, 程序开始的时候是先执行优先级高的, 所以是从在main函数开始之前就执行了.
destructor属性
destructor属性是在exit()函数执行之后执行. 这个是exit调用之后运行, 可以来做一个实验来验证是exit之后执行.
void exit_()
{
printf("exit\n");
}
__attribute__((destructor)) void fun()
{
printf("attributor\n");
}
int main()
{
printf("main\n");
atexit(exit_);
exit(0);
}
可以看出来设置属性的要在exit之后才执行, 至于atexit()函数有兴趣的可以了解一下, 这里的exit(0)不能替换成_exit函数, 想尝试的也可以试一下, 这里大概讲一下为什不能用_exit函数.
exit函数在执行的时候会先执行atexit再执行其他清理步骤, 而设置属性的相当于析构函数, 在exit执行完清理进入内核之前执行, 而_exit函数则是不进行任何清理处理, 而是直接的进入内核, 不会调用析构函数, 所以根本不会触发设置属性的函数.
参考于 :