确保信号处理函数可重入性的方法
- 全局变量:
- 尽量避免在信号处理函数中使用全局变量。如果必须使用,要考虑加锁机制。例如,使用互斥锁(
pthread_mutex_t
)来保护对全局变量的访问。在进入信号处理函数时加锁,离开时解锁。
- 对于可重入的全局变量,确保其在信号处理函数中的操作是原子的,例如简单的整数增减操作(在支持原子操作的系统上)。
- 标准I/O函数:
- 标准I/O函数(如
printf
、fopen
等)通常不是可重入的。在信号处理函数中应避免使用这些函数。
- 如果需要输出信息,可以考虑使用
write
函数,它是可重入的。例如,write(STDOUT_FILENO, "message", strlen("message"));
代码示例
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <pthread.h>
// 全局变量
int global_var = 0;
pthread_mutex_t mutex;
// SIGUSR1信号处理函数
void sigusr1_handler(int signum) {
pthread_mutex_lock(&mutex);
global_var++;
printf("SIGUSR1 received, global_var: %d\n", global_var);
pthread_mutex_unlock(&mutex);
// 模拟嵌套调用,发送SIGALRM信号
alarm(1);
}
// SIGALRM信号处理函数
void sigalrm_handler(int signum) {
pthread_mutex_lock(&mutex);
global_var--;
printf("SIGALRM received, global_var: %d\n", global_var);
pthread_mutex_unlock(&mutex);
}
int main() {
// 初始化互斥锁
pthread_mutex_init(&mutex, NULL);
// 注册SIGUSR1信号处理函数
struct sigaction sa_usr1;
sa_usr1.sa_handler = sigusr1_handler;
sigemptyset(&sa_usr1.sa_mask);
sa_usr1.sa_flags = 0;
sigaction(SIGUSR1, &sa_usr1, NULL);
// 注册SIGALRM信号处理函数
struct sigaction sa_alrm;
sa_alrm.sa_handler = sigalrm_handler;
sigemptyset(&sa_alrm.sa_mask);
sa_alrm.sa_flags = 0;
sigaction(SIGALRM, &sa_alrm, NULL);
// 主循环,等待信号
while (1) {
printf("Waiting for signals...\n");
sleep(1);
}
// 销毁互斥锁
pthread_mutex_destroy(&mutex);
return 0;
}
代码说明
- 全局变量处理:
- 声明了全局变量
global_var
,并使用pthread_mutex_t
类型的互斥锁mutex
来保护对它的访问。在sigusr1_handler
和sigalrm_handler
信号处理函数中,在对global_var
进行操作前加锁,操作完成后解锁。
- 标准I/O函数处理:
- 虽然在示例中使用了
printf
,但实际应用中应避免。这里只是为了简单演示。如果需要输出,可以将printf
替换为write
函数,如write(STDOUT_FILENO, "SIGUSR1 received, global_var: ", 27);
并使用snprintf
等函数将global_var
的值转换为字符串再输出。
- 嵌套调用:
- 在
sigusr1_handler
函数中,通过alarm(1)
发送了SIGALRM
信号,模拟了嵌套调用的情况。由于使用了互斥锁保护全局变量,即使在嵌套调用时,对全局变量的操作也是安全的。