ARM/x86中全局变量(静态与非静态)对比

本文对比了在ARM和x86平台上全局变量(静态与非静态)的编译表现。ARM中,全局变量使用EXPORT标识,初始化的变量位于.data段,未初始化的在.bss段。而x86中,全局变量用PUBLIC标识,同样初始化的在_DATA段,未初始化的在_BSS段,未使用的静态全局变量不分配空间。

使用VS2005 编译,enable /TP 编译选项(Compile as C++ Code)/FA 编译选项. 文中描述的全局变量为非静态全局变量

C++ 代码:

         static int nsInitGlobal = 1;

static int nsUninitGlobal ;

 

int nInitGlobal = 1;

int nUninitGlobal ;

 

int _tmain (int argc , _TCHAR * argv [])

{

     return 0;

}

ARM4 对应汇编代码:

EXPORT     |?nUninitGlobal @@3HA| [ DATA ]       ; nUninitGlobal

  EXPORT    |?nInitGlobal @@3HA| [ DATA ]         ; nInitGlobal

 

  AREA |.bss|, NOINIT

|?nUninitGlobal @@3HA| % 0x4                 ; nUninitGlobal

 

  AREA |.data|, DATA

|nsInitGlobal | DCD 0x1

|?nInitGlobal @@3HA| DCD 0x1                 ; nInitGlobal

  EXPORT   |wmain|

 

  AREA |.pdata|, PDATA

|$T31408| DCD   |$LN5@wmain|

  DCD  0x40000904

  AREA |.text|, CODE, ARM

 

|wmain|     PROC

|$LN5@wmain|

  mov         r12, sp

  stmdb       sp!, {r0, r1}

  stmdb       sp!, {r12, lr}

  sub         sp, sp, #4

|$M31405|

  mov         r3, #0

  str         r3, [sp]

  ldr         r0, [sp]

  add         sp, sp, #4

  ldmia       sp, {sp, pc}

|$M31406|

  ENDP  ; |wmain|

  END

1.        可以看出全局变量在ARM 汇编下被用EXPORT 关键字标示,

EXPORT     |?nUninitGlobal @@3HA| [ DATA ]       ; nUninitGlobal

  EXPORT    |?nInitGlobal @@3HA| [ DATA ]         ; nInitGlobal

别的编译单元可以看见。静态全局变量不会用EXPORT 标示。

2.        初始化的全局变量存在于.data 段中,未初始化的全局变量存在于.bss 段中

3.        初始化的静态全局变量也在于.data 段中,但未初始化的静态全局变量没被使用,不会被分配空间

  X86 对应汇编代码:

PUBLIC    ?nUninitGlobal @@3HA                  ; nUninitGlobal

PUBLIC    ?nInitGlobal @@3HA                ; nInitGlobal

_BSS SEGMENT

?nUninitGlobal @@3HA DD 01H DUP (?)             ; nUninitGlobal

_BSS ENDS

_DATA    SEGMENT

_nsInitGlobal DD 01H

?nInitGlobal @@3HA DD 01H                  ; nInitGlobal

_DATA    ENDS

PUBLIC   _wmain

EXTRN    __RTC_Shutdown:PROC

EXTRN    __RTC_InitBase:PROC

;    COMDAT rtc$TMZ

rtc$TMZ  SEGMENT

__RTC_Shutdown.rtc$TMZ DD FLAT:__RTC_Shutdown

rtc$TMZ  ENDS

;    COMDAT rtc$IMZ

rtc$IMZ  SEGMENT

__RTC_InitBase.rtc$IMZ DD FLAT:__RTC_InitBase

; Function compile flags: /Odtp /RTCsu /ZI

rtc$IMZ  ENDS

;    COMDAT _wmain

_TEXT    SEGMENT

_argc$ = 8                           ; size = 4

_argv$ = 12                          ; size = 4

_wmain   PROC                        ; COMDAT

; Line 13

     push ebp

     mov  ebp, esp

     sub  esp, 192               ; 000000c0H

     push ebx

     push esi

     push edi

     lea  edi, DWORD PTR [ebp-192]

     mov  ecx, 48                     ; 00000030H

     mov  eax, -858993460                  ; ccccccccH

     rep stosd

; Line 14

     xor  eax, eax

; Line 15

     pop  edi

     pop  esi

     pop  ebx

     mov  esp, ebp

     pop  ebp

     ret  0

_wmain   ENDP

_TEXT    ENDS

END

1.        可以看出全局变量在x86 汇编下被用PUBLIC 关键字标示,

PUBLIC     ?nUninitGlobal @@3HA                  ; nUninitGlobal

PUBLIC     ?nInitGlobal @@3HA                ; nInitGlobal 别的编译单元可以看见。静态全局变量不会用 PUBLIC 标示。

2.        初始化的全局变量存在于.ata 段中,未初始化的全局变量存在于.bss 段(只是变量的声明, 不占用模块空间的

3.        初始化的静态全局变量也在于.data 段中,但未初始化的静态全局变量没被使用,不会被分配空间

 

<think>嗯,用户询问Linux C++开发中static bool全局变量是否需要加锁。这个问题看似简单,但涉及多线程安全的核心机制,需要仔细梳理。 用户提供的引用材料很有价值。引用[1][2]指出static变量作用域限制在编译单元内,但没解决多线程问题;引用[3]特别强调不同编译单元的初始化顺序未定义;引用[5]说明静态变量存储在全局数据区且只初始化一次。 关键点在于:这个bool变量是否被多个线程同时读写?如果是纯粹的只读变量(比如配置开关),根据引用[5]描述的静态变量特性,确实可以不加锁。但若涉及写操作,就必须考虑原子性——bool虽是基本类型,但C++标准不保证其操作的原子性。 突然想到个易错点:用户可能混淆了"全局静态"和"局部静态"。根据引用[4][5],static放在全局作用域和局部作用域效果不同,但线程安全问题本质相同。另外引用[3]提到的C++17 inline static特性也值得注意,不过对bool类型影响不大。 用户的问题背景是Linux开发,那还要考虑编译器实现。x86架构下bool读写可能是原子的,但ARM可能就不是。最稳妥的做法还是用atomic<bool>,这样连内存序问题都能规避。 最后用户可能没意识到的风险:看似简单的bool赋值,编译器可能拆解成多条指令。比如在32位系统写64位bool就不是原子的。这个细节必须提醒,否则会在高并发场景埋下难以调试的隐患。 </think>在Linux C++开发中,对于`static bool`修饰的静态全局变量是否需要加锁,需根据**访问场景**判断: --- ### **1. 只读访问(无需加锁)** 若变量仅在初始化时赋值,之后被所有线程**只读访问**(例如配置标志),则无需加锁。 **原因**: - `static bool`存储在全局静态区,内存地址固定 - 单个`bool`值的读取操作本身是原子的(在x86/ARM等架构上) - 只读操作不会引发数据竞争 **示例**: ```cpp // 声明(头文件) extern bool g_configEnabled; // 定义(源文件) static bool g_configEnabled = true; // 初始化后不变 // 线程安全读取 if (g_configEnabled) {...} // 无需加锁 ``` --- ### **2. 写操作(必须加锁或原子操作)** 若变量会被**多个线程修改**,则必须通过以下方式保证安全: #### **(1) 互斥锁(通用场景)** ```cpp #include <mutex> static bool g_flag; static std::mutex g_mutex; void set_flag(bool val) { std::lock_guard<std::mutex> lock(g_mutex); g_flag = val; } bool get_flag() { std::lock_guard<std::mutex> lock(g_mutex); return g_flag; } ``` #### **(2) 原子操作(性能敏感场景)** **C++11起**直接使用`std::atomic<bool>`: ```cpp #include <atomic> static std::atomic<bool> g_atomicFlag(false); // 线程安全读写 g_atomicFlag.store(true); // 写操作 bool val = g_atomicFlag.load(); // 读操作 ``` --- ### **关键依据** 1. **C++标准要求** 对非原子类型的并发写操作是**未定义行为**(UB)。即使`bool`大小通常为1字节,标准不保证其操作的原子性(除非使用`std::atomic`)[^3][^5]。 2. **硬件原子性局限** - x86架构:对齐的1字节读写通常是原子的 - ARM架构:可能需要额外指令保证原子性 **结论**:依赖硬件实现不可移植,应优先使用`std::atomic`。 3. **初始化安全性** `static`全局变量的初始化在`main()`前完成,天然线程安全。但若需**延迟初始化**(如函数内静态变量),C++11起保证其线程安全[^3]。 --- ### **总结建议** | 场景 | 方案 | |---------------------|--------------------------| | **仅初始化+只读** | 不加锁(安全) | | **单线程写多线程读**| 用`std::atomic<bool>` | | **多线程读写** | 互斥锁 或 `std::atomic` | > ⚠️ 避免直接对非原子`bool`进行并发写操作。**高性能场景首选`std::atomic<bool>`,通用场景用互斥锁**。 --- ### 相关问题 1. `std::atomic<bool>` 与普通`bool`在性能上有何差异? 2. 如何验证多线程环境下`static`变量的线程安全性? 3. C++11中的静态局部变量初始化如何保证线程安全? 4. 不同编译单元中`static`全局变量的初始化顺序问题如何解决? [^1]: Static关键字在C/C++中限定作用域与存储方式 [^2]: 跨编译单元的全局静态变量存在多份副本 [^3]: C++标准未定义跨编译单元初始化顺序,需避免静态初始化依赖 [^4]: `const`修饰的全局变量具有静态存储期 [^5]: 静态局部变量与自动变量的存储期及初始化差异
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值