volatile 和 sig_atomic_t

本文介绍了volatile关键字的作用,即防止编译器对特定变量进行优化,并解释了何时及为何需要使用volatile。此外,还详细阐述了sig_atomic_t类型的特点及其应用场景,确保变量操作的原子性。

1 volatile—影响编译器编译的结果

volatile 变量是随时可能发生变化的,每次使用时都需要去内存里重新读取它的值,与volatile变量有关的运算,不要进行编译优化,以免出错(VC++ 在产生release版可执行码时会进行编译优化,加volatile关键字的变量有关的运算,将不进行编译优化)。

例如:

   volatile int i=10;

   int j = i;

   ...

   int k = i;

volatile 告诉编译器i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的可执行码会重新从i的地址读取数据放在k中。

而优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在k中。而不是重新从i里面读。这样一来,如果i是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问,不会出错。

建议使用volatile变量的场所:

   (1) 并行设备的硬件寄存器

   (2) 一个中断服务子程序中会访问到的非自动变量(全局变量)

   (3) 多线程应用中被几个任务共享的变量

2 sig_atomic_t:

当把变量声明为该类型是,则会保证该变量在使用或赋值时,无论是在32位还是64位的机器上都能保证操作是原子的,它会根据机器的类型自动适应。

这个类型是定义在signal.h文件中,下面来说说这个类型。

在处理信号(signal)的时候,有时对于一些变量的访问希望不会被中断,无论是硬件中断还是软件中断,这就要求访问或改变这些变量需要在计算机的一条指令内完成

通常情况下,int类型的变量通常是原子访问的,也可以认为sig_atomic_t就是int类型的数据,因为对这些变量要求一条指令完成,所以sig_atomic_t不可能是结构体,只会是数字类型。

   在linux里这样定义: typedef int __sig_atomic_t;

另外gnu c的文档也说比int短的类型通常也是具有原子性的,例如short类型。同时,指针(地址)类型也一定是原子性的。该类型在所有gnu c库支持的系统和支持posix的系统中都有定义。

`volatile sig_atomic_t` 是C语言中用于信号处理程序中的一种数据类型,确保在多线程或多信号环境下变量的修改能够被正确同步观察。 ### 解释 1. **`sig_atomic_t`**: - `sig_atomic_t` 是一种整数类型,定义在 `<signal.h>` 中。它是专门设计用来在信号处理函数中使用的。 - 该类型的目的是保证在信号处理函数中对它的读写操作是原子的(即不会被中断)。 - 在信号处理函数中,只有 `volatile sig_atomic_t` 类型的变量可以被安全地修改读取。 2. **`volatile`**: - `volatile` 关键字告诉编译器不要对该变量进行优化,并且每次访问该变量时都必须从内存中读取,而不是使用寄存器中的缓存值。 - 这在信号处理中非常重要,因为信号可能在任何时候中断正常程序流,如果编译器对变量进行了优化,可能会导致信号处理函数中看到的值与实际值不一致。 3. **结合使用**: - 在信号处理函数中,通常会用 `volatile sig_atomic_t` 来声明一个全局变量,这个变量可以在信号处理函数中被修改,也可以在主程序中被读取。 - 这种方式确保了信号处理函数主程序之间的通信是安全的。 ### 示例代码 以下是一个使用 `volatile sig_atomic_t` 的简单例子: ```c #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> volatile sig_atomic_t keep_running = 1; void handle_sigint(int sig) { if (sig == SIGINT) { printf("\nCaught SIGINT, stopping...\n"); keep_running = 0; // 修改标志位 } } int main() { signal(SIGINT, handle_sigint); // 注册SIGINT信号处理函数 while (keep_running) { printf("Running... Press Ctrl+C to stop.\n"); sleep(1); } printf("Program stopped.\n"); return 0; } ``` ### 给出解释 - **`keep_running`**: 这个变量是一个全局变量,用于控制主循环是否继续运行。 - **`handle_sigint`**: 这是一个信号处理函数,当捕获到 `SIGINT` 信号(通常是用户按下 `Ctrl+C` 产生的)时,它会被调用。 - **`volatile sig_atomic_t`**: 确保 `keep_running` 变量在信号处理函数中被修改后,主程序能够立即感知到变化。 - **`signal` 函数**: 用于注册信号处理函数,将 `SIGINT` 信号与 `handle_sigint` 函数关联起来。 ### 注意事项 - 在信号处理函数中,只能调用异步信号安全的函数(如 `printf`、`write` 等),避免调用非安全的库函数。 - 使用 `volatile sig_atomic_t` 是为了确保信号处理函数主程序之间的通信安全。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值