GCC-4——编译属性和选项

1. packed 属性

packed 指定内存默认对其参数:

__attribute__((packed)):按一字节对齐
__attribute__((aligned(n))):从此之后默认按n字节对齐
例如:

struct stu
{
  int a;
  char b;
}__attribute__((packed));
struct stu
{
  int a __attribute__((aligned(16)));
  char b;
};

例子

#include <stdio.h>

struct ss
{
    char a __attribute__((aligned(16)));
    int b;//① __attribute__((aligned(16)));
};//②__attribute__((aligned(16)));

void main()
{
    int i;
    printf("%d\n", sizeof(struct ss));
    struct ss s1 = {0x11, 0x55443322};
    
    unsigned char *p = (unsigned char *)&s1;

    for(i=0; i<sizeof(s1); i++)
    {
        printf("0x%x\n", *(p+i));
    }
}
输出:
16
0x11
0xd1
0x98
0x0
0x22
0x33
0x44
0x55
0xe0
0xfc
0x98
0x0
0xf4
0xef
0x98
0x0

可以看出:

__attribute__((aligned(16)))在哪个域后面修饰哪个域(注意仅对此域有效,对其它域无效),更改了这个域的实际对齐参数,实际对齐参数决定了此域的起始存储位置,再结合结构体的总大小必须能整除每一个域的最大对齐参数,因此可以推出来结构体的大小和内存的存储关系。将结构体中注释掉掉的部分加上结构体大小就是32字节。

若只要③处,设定的只是结构体间对齐,结构体成员的存储顺序不变,只是结构体变长了。

align用来修饰主要是为了便于对齐访问,比如想通过struct foo *p; p++访问下一个foo结构,首先对齐上需要是以 sizeof(struct foo)对齐的。

 

1.1、结构体中含有位段的情况

#include <stdio.h>

struct S3_1
{
    char a;
    short b : 1;
    short c : 2;
    short d : 1;
    int e : 4;
    int f : 6;
}; 

struct S3_1 s3 = {0x11, 1, 3, 1, 0xf, 0x3f};

void main()
{
    int i;

    printf("sizeof(s3)= %d\n", sizeof(s3));

    unsigned char *p = (char *)&s3;

    for(i=0; i<sizeof(s3); i++)
    {
        printf("0x%x\n", *(p+i));
    }
}
①GNU的gcc编译器和Windows里的编译器的结果不一样,GNU的gcc输出:
sizeof(s3)= 4
0x11
0xff
0x3f
0x0
即最后面的int型的位段页归并到short里面一起用了。 

②在Window上gcc软件编译出输出: 
sizeof(s3)= 8
0x11
0x0 
0xf
0x0
0xff
0x3
0x0
0x0
和一般结构体对齐类似,只是其中的位是不是都占用而已。

 

2. weak 属性

__attribute__((weak))指定一个函数是若函数,消除链接时的multiple definition

(1) 修饰函数

例如:main.c
#include <stdio.h>
#define __weak __attribute__((weak))
void __weak func(void)
{
  printf("%s\n", "void __atribute__(weak) func(void)");
}
void main()
{
  func();
}
test.c中
#include <stdio.h>
void func(void)
{
  printf("%s\n", "void func(void)");
}

参考kernel中的 sched_clock(),内核是不允许有两个同名的全局符号的,一个使用了__weak宏进行修饰了。

(2) 修饰变量

weak_test.c

#include <stdio.h>

#define __weak __attribute__((weak))

extern int __weak i; //防止编译比较严格时报错,这里也要加__weak
int __weak i = 3;

void main()
{
    printf("i=%d\n", i);
}

another.c

int i = 2;

两个文件一起编译,打印 i=2,若只编译 weak_test.c,打印 i=3。不会报警告。

其次还可以到处弱引用符号,弱源文件中定义了,包含这个头文件也没问题,kernel中的使用见 kernel/sched/wait.h,其定义:

extern unsigned int __weak walt_rotation_enabled;

 

3. regparm 属性

__attribute__((regparm(0))):告诉gcc编译器该函数不需要通过任何寄存器来传递参数,参数只是通过堆栈来传递。
__attribute__((regparm(3))):告诉gcc编译器这个函数可以通过寄存器传递多达3个的参数,x86PC上,这3个寄存器依次为EAX、EDX 和 ECX。更多的参数才通过堆栈传递。这样可以减少一些入栈出栈操作,因此调用比较快。 ARM核默认是多于4个参数的会被保存在栈中

 

4. __unused 属性

函数没有使用的参数后加 __unused 可防止编译器报警告。

/*加上"__unused"可以防止编译器报警告:warning: unused parameter 'id' [-Wunused-parameter]*/
int led_module_open(const struct hw_module_t* module, const char* id __unused,  struct hw_device_t** device) {
    ......  
}

 

5. __maybe_unused 属性

定义的变量若不使用需要加 __maybe_unused 以防止编译器报警告

static const u32 runnable_avg_yN_inv[] __maybe_unused = {
    ...
};

例子:

my_debug.h:63:13: warning: unused function 'xxx' [-Wunused-function]

//头文件中定义的函数
static __maybe_unused unsigned long rcu_exp_gp_seq_endval(void) { ... } //tree_exp.h
static void __maybe_unused sync_exp_reset_tree(void) { ... }

//函数内使用:
nocb_gp_wait //tree_plugin.h
    int __maybe_unused cpu = my_rdp->cpu;
msgdma_tasklet //altera-msgdma.c
    u32 __maybe_unused size;

//头文件中修饰数组
static const u32 pelt8_runnable_avg_yN_inv[] __maybe_unused = { ... ); //sched-pelt.h

 

6. __percpu 属性

使用 __percpu 将结构体成员定义成 per-cpu 的

struct s_data {
    struct sched_domain * __percpu *sd;
    struct root_domain *rd;
};

 

7. noinline 属性

内核中对于不能inline的函数使用 noinline 宏修饰

//include/linux/compiler_attributes.h
#define   noinline    __attribute__((__noinline__))

static noinline int tracing_mark_write(const char *buf)
{
    ...
}

注:内核中的gcc编译宏定义在 compiler_attributes.h 中。

 

8. fallthrough 属性

fallthrough 属性在Linux-5.10 内核中开始使用。

gcc添加這個屬性原因是以免 switch-case语音漏写break, 使能 gcc 的 __fallthrough__ 属性后,没有写break的位置会报警告,在不需要break的位置需要加上 fallthrough 来显示的说明不需要break,这样就不会报警告了。

/*
 * 添加伪关键字“fallthrough”,因此 case 语句块必须以以下任何关键字结尾:
 *   break;
 *   fallthrough;
 *   goto <label>;
 *   return [expression];
 *
 *  gcc: https://gcc.gnu.org/onlinedocs/gcc/Statement-Attributes.html#Statement-Attributes
 */
//include/linux/compiler_attributes.h
#if __has_attribute(__fallthrough__)
# define fallthrough                    __attribute__((__fallthrough__))
#else
# define fallthrough                    do {} while (0)  /* fallthrough */
#endif

例子:

long do_futex(int cmd, ...) {
    ...
    switch (cmd) {
    case FUTEX_WAIT:
        val3 = FUTEX_BITSET_MATCH_ANY;
        fallthrough;
    case FUTEX_WAIT_BITSET:
        return futex_wait(uaddr, flags, val, timeout, val3);
    ...
}

注:linux内核中的gcc编译宏定义在文件 include/linux/compiler_attributes.h 中。

 

9. constructor/destructor 属性

constructor 和 destructor 属性用在C++中。

若函数被设定为 constructor 属性,则该函数会在 main() 函数执行之前被自动的执行。类似的,若函数被设定为 destructor 属性,则该函数会在 main() 函数执行之后或者 exit() 被调用后被自动的执行。当存在多个 __attribute__((constructor)) 修饰的函数时,也不用持锁,因为是单线程执行的。还可以使用 __attribute__((constructor(prio))) 指定优先级,从而决定调用次序,优先级prio取值范围是 0--65535,其中 0--100 是预留的,不能使用,优先级数值越小优先级越高,越优先被执行调用。只要指定了优先级,就算是指定的优先级是65535也还是会比没有指定优先级的先调用。

#include<stdio.h>

__attribute__((constructor)) void before_main()  
{  
   printf("before main.\n");  
}  
  
__attribute__((destructor)) void after_main()  
{  
   printf("after main.\n");  
}  
  
int main()  
{  
   printf("in main.\n");  
   return 0;  
}

/*
//执行结果:
$ ./pp
before main.
in main.
after main.
*/

使用此属性实现类似 module_init() 的设计模式,参考《利用 constructor 属性完成 module_init() 设计模式

 

10. section 属性

使用方式有两种:

__attribute__((__section__("section_name")))
__attribute__((section(".my.init")))

具体说明与使用见《attribute section 属性》

 

11. used 属性

__attribute__((__used__))

在gcc手册中找到了有关的解释:
used: This attribute, attached to a function, means that code must be emitted for the function even if it appears that the function is not referenced. This is useful, for example, when the function is referenced only in inline assembly.
这个属性,附加在一个函数上,意味着即使函数看起来没有被引用,也必须为该函数编译出代码。例如,当函数仅在内联汇编中被引用时,这很有用。也就是向编译器说明这段代码有用,即使在没有用到的情况下编译器也不会警告!

 

12. unused 属性

__attribute__((__unused__))

在gcc手册中找到了有关的解释:
unused:This attribute, attached to a function, means that the function is meant to be possibly unused. GCC will not produce a warning for this function.
表示该函数或变量可能不使用,这个属性可以避免编译器产生警告信息。

 

13. context 属性

这些符号是 Sparse工具做代码静态检查用的,仅用于代码的静态检查,用于编译阶段。在实际使用中,__acquires(x)和__releases(x)必须成对使用才算合法,否则编译时会有sparse工具报错,而spin_lock和spin_unlock正需要这样的检查,也必须成对使用。所以正是利用了这个”属性“,可以在内核编译期间发现spin_lock和spin_unlock未配对使用的情况。一般获取锁和释放锁不在同一个函数中时使用

# define __acquires(x)    __attribute__((context(x,0,1)))
# define __releases(x)    __attribute__((context(x,1,0)))
# define __acquire(x)    __context__(x,1)
# define __release(x)    __context__(x,-1)

内核中使用举例:

static inline struct futex_hash_bucket *queue_lock(struct futex_q *q) __acquires(&hb->lock) {...}
static inline void queue_unlock(struct futex_hash_bucket *hb) __releases(&hb->lock) {...}

用户空间测试例子:

#include <stdio.h>

#define __acquire(x) __context__(x,1)
#define __release(x) __context__(x,-1)

int main(int argc, char *argv[])
{
    int lock = 1;

    __acquire(lock);
    /* ... */
    if (lock != 1) { //改为大于/小于0测试,也是一样的效果
        __release(lock); /* 注释掉或执行不到这一句 sparse 就会报错 */
    }

    return 0;
}

/*
注释掉 __release(lock); 这行会报错,由于条件判断执行不到 __release(lock); 这行也会报错:
~/origin_tmp/6.sparse_test$ sparse -a sparse_test.c 
sparse_test.c:6:5: warning: context imbalance in 'main' - wrong count at exit
*/

 

14. __aligned 属性

通常用于缓存行对齐,如futex.c中:

/*
 * 桶数组的基数和它的大小总是一起使用,
 * 所以确保它们位于同一个缓存行中。
 */
static struct {
    struct futex_hash_bucket *queues;
    unsigned long            hashsize;
} __futex_data __aligned(2*sizeof(long));

 

15. __user 属性

__user 是一个类型修饰符,定义如下,它用于标识用户空间(user-space)的指针。提示该指针不得在内核中被解引用。


16. __private 属性

__private 通常用于表示某个变量或数据类型只能在同一 C 文件中使用,不应该被其他文件直接使用。__private 主要是为了强制实现信息隐藏,防止数据类型被误用或泄漏信息。

struct my_private_struct {
    __private int private_data;
};

 

 

 

 

 

 

其它属性

注:内核中的编译属性大都定义在 include/linux/compiler_attributes.h 中,里面还有每个属性的网页链接资料:

#define __alias(symbol)                 __attribute__((__alias__(#symbol)))
#define __aligned(x)                    __attribute__((__aligned__(x)))
#define __aligned_largest               __attribute__((__aligned__))
#define __always_inline                 inline __attribute__((__always_inline__))
#define __assume_aligned(a, ...)        __attribute__((__assume_aligned__(a, ## __VA_ARGS__)))
#define __cold                          __attribute__((__cold__))
#define __attribute_const__             __attribute__((__const__))
#define __copy(symbol)                   __attribute__((__copy__(symbol)))
#define __designated_init               __attribute__((__designated_init__))
#define __visible                        __attribute__((__externally_visible__))
#define __printf(a, b)                  __attribute__((__format__(printf, a, b)))
#define __scanf(a, b)                   __attribute__((__format__(scanf, a, b)))
#define __gnu_inline                    __attribute__((__gnu_inline__))
#define __malloc                        __attribute__((__malloc__))
#define __mode(x)                       __attribute__((__mode__(x)))
#define __no_caller_saved_registers        __attribute__((__no_caller_saved_registers__))
#define __noclone                         __attribute__((__noclone__))
#define fallthrough                     __attribute__((__fallthrough__))
#define   noinline                      __attribute__((__noinline__))
#define __nonstring                     __attribute__((__nonstring__))
#define __noreturn                      __attribute__((__noreturn__))
#define __packed                        __attribute__((__packed__))
#define __pure                          __attribute__((__pure__))
#define __section(section)              __attribute__((__section__(section)))
#define __always_unused                 __attribute__((__unused__))
#define __maybe_unused                  __attribute__((__unused__))
#define __used                          __attribute__((__used__))
#define __weak                          __attribute__((__weak__))

include/linux/compiler_types.h 中定义了如下属性:

#ifdef __CHECKER__
# define __user        __attribute__((noderef, address_space(1)))
# define __kernel    __attribute__((address_space(0)))
# define __safe        __attribute__((safe))
# define __force    __attribute__((force))
# define __nocast    __attribute__((nocast))
# define __iomem    __attribute__((noderef, address_space(2)))
# define __must_hold(x)    __attribute__((context(x,1,1)))
# define __acquires(x)    __attribute__((context(x,0,1)))
# define __releases(x)    __attribute__((context(x,1,0)))
# define __acquire(x)    __context__(x,1)
# define __release(x)    __context__(x,-1)
# define __cond_lock(x,c)    ((c) ? ({ __acquire(x); 1; }) : 0)
# define __percpu    __attribute__((noderef, address_space(3)))
# define __rcu        __attribute__((noderef, address_space(4)))
# define __private    __attribute__((noderef))
#endif /* __CHECKER__ */

 

 

 

参考:

GCC英文手册:https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/

其中属性相关章节:

https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Function-Attributes.html#Function-Attributes
https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Attribute-Syntax.html#Attribute-Syntax
https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Variable-Attributes.html#Variable-Attributes
https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Type-Attributes.html#Type-Attributes
https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/C_002b_002b-Attributes.html#C_002b_002b-Attributes

 

posted on 2017-08-03 14:33  Hello-World3  阅读(1427)  评论(0编辑  收藏  举报

导航