sigaltstack系统调用及示例

sigaltstack 函数详解

1. 函数介绍

sigaltstack 是Linux系统调用,用于设置和获取信号处理程序的备用栈(alternate signal stack)。当进程收到信号时,内核通常在当前栈上执行信号处理程序。使用 sigaltstack 可以为信号处理程序指定一个独立的栈空间,这对于处理栈溢出等异常情况特别有用。

2. 函数原型

#include <signal.h>
int sigaltstack(const stack_t *ss, stack_t *oss);

3. 功能

sigaltstack 允许进程为信号处理程序设置一个备用的栈空间。当信号被递送到使用备用栈的信号处理程序时,内核会切换到备用栈执行信号处理程序,执行完毕后再切换回原来的栈。

4. 参数

  • *const stack_t ss: 指向新栈设置的指针(NULL表示不改变当前设置)
  • *stack_t oss: 指向存储旧栈设置的指针(NULL表示不获取旧设置)

5. 返回值

  • 成功: 返回0
  • 失败: 返回-1,并设置errno

6. 相似函数,或关联函数

  • signal/sigaction: 设置信号处理程序
  • sigprocmask: 设置信号屏蔽字
  • setjmp/longjmp: 非局部跳转
  • getcontext/setcontext: 上下文操作

7. 示例代码

示例1:基础sigaltstack使用

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

/**
 * 信号栈结构体
 */
typedef struct {
    void *stack_ptr;
    size_t stack_size;
    int is_active;
} signal_stack_t;

/**
 * 显示当前信号栈信息
 */
void show_signal_stack_info() {
    stack_t current_stack;
    
    if (sigaltstack(NULL, &current_stack) == 0) {
        printf("=== 当前信号栈信息 ===\n");
        printf("栈指针: %p\n", current_stack.ss_sp);
        printf("栈大小: %zu 字节\n", current_stack.ss_size);
        printf("栈标志: ");
        if (current_stack.ss_flags & SS_ONSTACK) {
            printf("SS_ONSTACK (正在使用中)\n");
        } else if (current_stack.ss_flags & SS_DISABLE) {
            printf("SS_DISABLE (已禁用)\n");
        } else {
            printf("SS_ENABLED (已启用)\n");
        }
        printf("\n");
    } else {
        printf("获取信号栈信息失败: %s\n", strerror(errno));
    }
}

/**
 * 信号处理程序
 */
void signal_handler(int sig) {
    stack_t current_stack;
    
    printf("信号处理程序执行中 (信号: %d)\n", sig);
    
    // 检查当前是否在备用栈上
    if (sigaltstack(NULL, &current_stack) == 0) {
        if (current_stack.ss_flags & SS_ONSTACK) {
            printf("  ✓ 正在备用栈上执行\n");
        } else {
            printf("  ✗ 在主栈上执行\n");
        }
    }
    
    printf("  当前栈指针: %p\n", &sig);
    printf("  信号处理完成\n\n");
}

/**
 * 演示基础sigaltstack使用方法
 */
int demo_sigaltstack_basic() {
    stack_t new_stack, old_stack;
    char *stack_buffer;
    struct sigaction sa;
    
    printf("=== 基础sigaltstack使用示例 ===\n");
    
    // 显示原始信号栈信息
    printf("1. 原始信号栈信息:\n");
    show_signal_stack_info();
    
    // 分配备用栈空间
    size_t stack_size = SIGSTKSZ;  // 系统推荐的栈大小
    stack_buffer = malloc(stack_size);
    if (!stack_buffer) {
        perror("分配栈空间失败");
        return -1;
    }
    
    printf("2. 分配备用栈空间:\n");
    printf("   栈大小: %zu 字节\n", stack_size);
    printf("   栈地址: %p\n", (void*)stack_buffer);
    
    // 设置备用栈
    new_stack.ss_sp = stack_buffer;
    new_stack.ss_size = stack_size;
    new_stack.ss_flags = 0;  // 启用栈
    
    printf("3. 设置备用信号栈:\n");
    if (sigaltstack(&new_stack, &old_stack) == 0) {
        printf("   ✓ 备用栈设置成功\n");
        show_signal_stack_info();
    } else {
        printf("   ✗ 备用栈设置失败: %s\n", strerror(errno));
        free(stack_buffer);
        return -1;
    }
    
    // 设置使用备用栈的信号处理程序
    printf("4. 设置信号处理程序:\n");
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = signal_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_ONSTACK;  // 使用备用栈
    
    if (sigaction(SIGUSR1, &sa, NULL) == 0) {
        printf("   ✓ 信号处理程序设置成功 (使用备用栈)\n");
    } else {
        printf("   ✗ 信号处理程序设置失败: %s\n", strerror(errno));
        free(stack_buffer);
        return -1;
    }
    
    // 发送信号测试
    printf("5. 发送测试信号:\n");
    if (kill(getpid(), SIGUSR1) == 0) {
        printf("   ✓ 信号发送成功\n");
        sleep(1);  // 等待信号处理完成
    } else {
        printf("   ✗ 信号发送失败: %s\n", strerror(errno));
    }
    
    // 禁用备用栈
    printf("6. 禁用备用栈:\n");
    stack_t disable_stack;
    disable_stack.ss_flags = SS_DISABLE;
    
    if (sigaltstack(&disable_stack, NULL) == 0) {
        printf("   ✓ 备用栈禁用成功\n");
        show_signal_stack_info();
    } else {
        printf("   ✗ 备用栈禁用失败: %s\n", strerror(errno));
    }
    
    // 清理资源
    free(stack_buffer);
    
    return 0;
}

int main() {
    return demo_sigaltstack_basic();
}

示例2:栈溢出保护演示

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <setjmp.h>

/**
 * 栈溢出保护结构
 */
typedef struct {
    stack_t alt_stack;
    char *stack_buffer;
    sigjmp_buf jump_buffer;
    int overflow_detected;
} stack_protection_t;

/**
 * 栈溢出信号处理程序
 */
void stack_overflow_handler(int sig) {
    printf("⚠ 检测到栈溢出异常 (信号: %d)\n", sig);
    
    // 检查是否在备用栈上
    stack_t current_stack;
    if (sigaltstack(NULL, &current_stack) == 0) {
        if (current_stack.ss_flags & SS_ONSTACK) {
            printf("  ✓ 在备用栈上安全处理异常\n");
        } else {
            printf("  ✗ 不在备用栈上 (异常情况)\n");
        }
    }
    
    // 恢复到安全状态
    printf("  跳转到安全恢复点...\n");
    siglongjmp(((stack_protection_t*)NULL)->jump_buffer, 1);
}

/**
 * 递归函数(可能导致栈溢出)
 */
void recursive_function(int depth) {
    char buffer[1024];  // 消耗栈空间
    
    // 填充缓冲区以确保栈使用
    memset(buffer, depth & 0xFF, sizeof(buffer));
    
    printf("递归深度: %d, 栈地址: %p\n", depth, (void*)&buffer);
    
    // 深度递归可能导致栈溢出
    if (depth < 1000) {  // 限制递归深度
        recursive_function(depth + 1);
    }
}

/**
 * 安全的递归执行
 */
int safe_recursive_execution(stack_protection_t *protection) {
    struct sigaction sa;
    int result;
    
    printf("=== 栈溢出保护演示 ===\n");
    
    // 设置信号处理程序
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = stack_overflow_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_ONSTACK;
    
    if (sigaction(SIGSEGV, &sa, NULL) != 0) {
        printf("设置SIGSEGV处理程序失败: %s\n", strerror(errno));
        return -1;
    }
    
    if (sigaction(SIGBUS, &sa, NULL) != 0) {
        printf("设置SIGBUS处理程序失败: %s\n", strerror(errno));
        return -1;
    }
    
    // 设置跳转点
    result = sigsetjmp(protection->jump_buffer, 1);
    if (result == 0) {
        printf("开始安全递归执行...\n");
        
        // 执行可能引起栈溢出的操作
        recursive_function(0);
        
        printf("递归执行正常完成\n");
        return 0;
    } else {
        printf("✓ 从栈溢出异常中成功恢复\n");
        return 1;
    }
}

/**
 * 演示栈溢出保护
 */
int demo_stack_overflow_protection() {
    stack_protection_t protection = {0};
    size_t stack_size = SIGSTKSZ * 2;  // 更大的备用栈
    
    printf("=== 栈溢出保护演示 ===\n");
    
    // 分配备用栈
    protection.stack_buffer = malloc(stack_size);
    if (!protection.stack_buffer) {
        perror("分配备用栈失败");
        return -1;
    }
    
    printf("分配备用栈: %zu 字节\n", stack_size);
    
    // 设置备用栈
    protection.alt_stack.ss_sp = protection.stack_buffer;
    protection.alt_stack.ss_size = stack_size;
    protection.alt_stack.ss_flags = 0;
    
    if (sigaltstack(&protection.alt_stack, NULL) != 0) {
        printf("设置备用栈失败: %s\n", strerror(errno));
        free(protection.stack_buffer);
        return -1;
    }
    
    printf("备用栈设置成功\n");
    show_signal_stack_info();
    
    // 执行安全递归
    int result = safe_recursive_execution(&protection);
    
    // 清理资源
    free(protection.stack_buffer);
    
    return result;
}

// 辅助函数声明
void show_signal_stack_info();

int main() {
    return demo_stack_overflow_protection();
}

示例3:多信号处理程序管理

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>

/**
 * 多栈管理器
 */
typedef struct {
    stack_t stack1;
    stack_t stack2;
    char *buffer1;
    char *buffer2;
    int current_stack;
} multi_stack_manager_t;

/**
 * 信号处理程序1(使用栈1)
 */
void signal_handler_1(int sig) {
    printf("信号处理程序1执行 (信号: %d)\n", sig);
    
    stack_t current_stack;
    if (sigaltstack(NULL, &current_stack) == 0) {
        if (current_stack.ss_flags & SS_ONSTACK) {
            printf("  使用备用栈1执行\n");
        }
    }
    
    printf("  处理程序1完成\n");
}

/**
 * 信号处理程序2(使用栈2)
 */
void signal_handler_2(int sig) {
    printf("信号处理程序2执行 (信号: %d)\n", sig);
    
    stack_t current_stack;
    if (sigaltstack(NULL, &current_stack) == 0) {
        if (current_stack.ss_flags & SS_ONSTACK) {
            printf("  使用备用栈2执行\n");
        }
    }
    
    printf("  处理程序2完成\n");
}

/**
 * 初始化多栈管理器
 */
int init_multi_stack_manager(multi_stack_manager_t *manager) {
    size_t stack_size = SIGSTKSZ;
    
    printf("=== 多栈管理器初始化 ===\n");
    
    // 分配两个栈空间
    manager->buffer1 = malloc(stack_size);
    manager->buffer2 = malloc(stack_size);
    
    if (!manager->buffer1 || !manager->buffer2) {
        printf("分配栈空间失败\n");
        if (manager->buffer1) free(manager->buffer1);
        if (manager->buffer2) free(manager->buffer2);
        return -1;
    }
    
    printf("栈1地址: %p, 大小: %zu\n", (void*)manager->buffer1, stack_size);
    printf("栈2地址: %p, 大小: %zu\n", (void*)manager->buffer2, stack_size);
    
    // 初始化栈结构
    manager->stack1.ss_sp = manager->buffer1;
    manager->stack1.ss_size = stack_size;
    manager->stack1.ss_flags = 0;
    
    manager->stack2.ss_sp = manager->buffer2;
    manager->stack2.ss_size = stack_size;
    manager->stack2.ss_flags = 0;
    
    manager->current_stack = 0;
    
    return 0;
}

/**
 * 切换备用栈
 */
int switch_alternate_stack(multi_stack_manager_t *manager, int stack_id) {
    stack_t *target_stack = (stack_id == 1) ? &manager->stack1 : &manager->stack2;
    
    printf("切换到备用栈 %d\n", stack_id);
    
    if (sigaltstack(target_stack, NULL) == 0) {
        printf("✓ 成功切换到备用栈 %d\n", stack_id);
        manager->current_stack = stack_id;
        return 0;
    } else {
        printf("✗ 切换备用栈 %d 失败: %s\n", stack_id, strerror(errno));
        return -1;
    }
}

/**
 * 演示多信号处理程序
 */
int demo_multi_signal_handlers() {
    multi_stack_manager_t manager = {0};
    struct sigaction sa1, sa2;
    
    printf("=== 多信号处理程序演示 ===\n");
    
    // 初始化多栈管理器
    if (init_multi_stack_manager(&manager) != 0) {
        printf("初始化多栈管理器失败\n");
        return -1;
    }
    
    // 设置信号处理程序1(使用栈1)
    printf("\n设置信号处理程序1:\n");
    switch_alternate_stack(&manager, 1);
    
    memset(&sa1, 0, sizeof(sa1));
    sa1.sa_handler = signal_handler_1;
    sigemptyset(&sa1.sa_mask);
    sa1.sa_flags = SA_ONSTACK;
    
    if (sigaction(SIGUSR1, &sa1, NULL) == 0) {
        printf("✓ 信号处理程序1设置成功\n");
    } else {
        printf("✗ 信号处理程序1设置失败: %s\n", strerror(errno));
    }
    
    // 设置信号处理程序2(使用栈2)
    printf("\n设置信号处理程序2:\n");
    switch_alternate_stack(&manager, 2);
    
    memset(&sa2, 0, sizeof(sa2));
    sa2.sa_handler = signal_handler_2;
    sigemptyset(&sa2.sa_mask);
    sa2.sa_flags = SA_ONSTACK;
    
    if (sigaction(SIGUSR2, &sa2, NULL) == 0) {
        printf("✓ 信号处理程序2设置成功\n");
    } else {
        printf("✗ 信号处理程序2设置失败: %s\n", strerror(errno));
    }
    
    // 测试信号处理
    printf("\n测试信号处理:\n");
    
    // 发送SIGUSR1
    printf("发送SIGUSR1信号:\n");
    switch_alternate_stack(&manager, 1);
    if (kill(getpid(), SIGUSR1) == 0) {
        printf("✓ SIGUSR1发送成功\n");
        sleep(1);
    }
    
    // 发送SIGUSR2
    printf("发送SIGUSR2信号:\n");
    switch_alternate_stack(&manager, 2);
    if (kill(getpid(), SIGUSR2) == 0) {
        printf("✓ SIGUSR2发送成功\n");
        sleep(1);
    }
    
    // 清理资源
    free(manager.buffer1);
    free(manager.buffer2);
    
    return 0;
}

int main() {
    return demo_multi_signal_handlers();
}

示例4:线程安全的信号栈管理

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>

/**
 * 线程信号栈管理器
 */
typedef struct {
    stack_t signal_stack;
    char *stack_buffer;
    pthread_t thread_id;
    int thread_num;
} thread_stack_manager_t;

/**
 * 线程信号处理程序
 */
void thread_signal_handler(int sig) {
    pthread_t current_thread = pthread_self();
    
    printf("线程 %lu 收到信号 %d\n", (unsigned long)current_thread, sig);
    
    stack_t current_stack;
    if (sigaltstack(NULL, &current_stack) == 0) {
        if (current_stack.ss_flags & SS_ONSTACK) {
            printf("  线程 %lu 在备用栈上处理信号\n", (unsigned long)current_thread);
        } else {
            printf("  线程 %lu 在主栈上处理信号\n", (unsigned long)current_thread);
        }
    }
    
    printf("  线程 %lu 信号处理完成\n", (unsigned long)current_thread);
}

/**
 * 初始化线程栈管理器
 */
int init_thread_stack_manager(thread_stack_manager_t *manager, int thread_num) {
    size_t stack_size = SIGSTKSZ;
    
    manager->thread_num = thread_num;
    manager->thread_id = pthread_self();
    
    // 分配栈空间
    manager->stack_buffer = malloc(stack_size);
    if (!manager->stack_buffer) {
        printf("线程 %d: 分配栈空间失败\n", thread_num);
        return -1;
    }
    
    // 初始化栈结构
    manager->signal_stack.ss_sp = manager->stack_buffer;
    manager->signal_stack.ss_size = stack_size;
    manager->signal_stack.ss_flags = 0;
    
    printf("线程 %d: 分配备用栈 %p, 大小 %zu\n", 
           thread_num, (void*)manager->stack_buffer, stack_size);
    
    return 0;
}

/**
 * 线程工作函数
 */
void* thread_worker(void *arg) {
    thread_stack_manager_t *manager = (thread_stack_manager_t*)arg;
    struct sigaction sa;
    sigset_t set;
    
    printf("工作线程 %d 启动 (ID: %lu)\n", 
           manager->thread_num, (unsigned long)manager->thread_id);
    
    // 设置备用栈
    if (sigaltstack(&manager->signal_stack, NULL) != 0) {
        printf("线程 %d: 设置备用栈失败: %s\n", 
               manager->thread_num, strerror(errno));
        return NULL;
    }
    
    printf("线程 %d: 备用栈设置成功\n", manager->thread_num);
    
    // 设置信号处理程序
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = thread_signal_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_ONSTACK;
    
    if (sigaction(SIGUSR1, &sa, NULL) != 0) {
        printf("线程 %d: 设置信号处理程序失败: %s\n", 
               manager->thread_num, strerror(errno));
        return NULL;
    }
    
    printf("线程 %d: 信号处理程序设置成功\n", manager->thread_num);
    
    // 阻塞SIGUSR1以便测试
    sigemptyset(&set);
    sigaddset(&set, SIGUSR1);
    pthread_sigmask(SIG_BLOCK, &set, NULL);
    
    // 执行工作
    for (int i = 0; i < 5; i++) {
        printf("线程 %d: 工作中... (%d/5)\n", manager->thread_num, i + 1);
        sleep(2);
    }
    
    // 解除阻塞并等待信号
    printf("线程 %d: 等待信号...\n", manager->thread_num);
    pthread_sigmask(SIG_UNBLOCK, &set, NULL);
    
    // 等待一段时间让信号处理完成
    sleep(2);
    
    printf("线程 %d: 工作完成\n", manager->thread_num);
    return NULL;
}

/**
 * 演示线程安全的信号栈管理
 */
int demo_thread_safe_signal_stacks() {
    const int num_threads = 3;
    pthread_t threads[num_threads];
    thread_stack_manager_t managers[num_threads];
    sigset_t set;
    
    printf("=== 线程安全的信号栈管理演示 ===\n");
    
    // 阻塞SIGUSR1信号
    sigemptyset(&set);
    sigaddset(&set, SIGUSR1);
    pthread_sigmask(SIG_BLOCK, &set, NULL);
    
    // 创建工作线程
    printf("创建 %d 个工作线程:\n", num_threads);
    
    for (int i = 0; i < num_threads; i++) {
        if (init_thread_stack_manager(&managers[i], i + 1) != 0) {
            printf("初始化线程 %d 失败\n", i + 1);
            // 清理已分配的资源
            for (int j = 0; j < i; j++) {
                free(managers[j].stack_buffer);
            }
            return -1;
        }
        
        if (pthread_create(&threads[i], NULL, thread_worker, &managers[i]) != 0) {
            printf("创建线程 %d 失败\n", i + 1);
            free(managers[i].stack_buffer);
            // 清理已分配的资源
            for (int j = 0; j < i; j++) {
                free(managers[j].stack_buffer);
            }
            return -1;
        }
        
        managers[i].thread_id = threads[i];
        printf("创建线程 %d: ID=%lu\n", i + 1, (unsigned long)threads[i]);
    }
    
    // 等待线程启动
    sleep(1);
    
    // 向所有线程发送信号
    printf("\n向所有线程发送SIGUSR1信号:\n");
    pthread_sigmask(SIG_UNBLOCK, &set, NULL);
    
    for (int i = 0; i < num_threads; i++) {
        // 注意:实际应用中需要更精确的线程信号发送方法
        if (kill(getpid(), SIGUSR1) == 0) {
            printf("向线程 %d 发送信号成功\n", i + 1);
        } else {
            printf("向线程 %d 发送信号失败: %s\n", i + 1, strerror(errno));
        }
        sleep(1);  // 间隔发送
    }
    
    // 等待所有线程完成
    printf("\n等待所有线程完成:\n");
    for (int i = 0; i < num_threads; i++) {
        void *result;
        pthread_join(threads[i], &result);
        printf("线程 %d 已完成\n", i + 1);
        
        // 清理资源
        free(managers[i].stack_buffer);
    }
    
    return 0;
}

int main() {
    return demo_thread_safe_signal_stacks();
}

示例5:信号栈监控和调试

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/resource.h>

/**
 * 信号栈监控器
 */
typedef struct {
    stack_t original_stack;
    stack_t current_stack;
    int stack_switch_count;
    size_t total_stack_size;
    time_t last_switch_time;
} stack_monitor_t;

/**
 * 详细的栈信息显示
 */
void show_detailed_stack_info(const char *context) {
    stack_t stack_info;
    
    printf("=== %s ===\n", context);
    
    if (sigaltstack(NULL, &stack_info) == 0) {
        printf("栈指针: %p\n", stack_info.ss_sp);
        printf("栈大小: %zu 字节\n", stack_info.ss_size);
        printf("栈标志: 0x%x\n", stack_info.ss_flags);
        
        if (stack_info.ss_flags & SS_ONSTACK) {
            printf("状态: 正在使用备用栈\n");
        } else if (stack_info.ss_flags & SS_DISABLE) {
            printf("状态: 备用栈已禁用\n");
        } else {
            printf("状态: 备用栈已启用但未使用\n");
        }
        
        // 计算栈使用情况(简化版)
        void *current_sp;
        asm volatile("mov %%rsp, %0" : "=r"(current_sp));
        printf("当前栈指针: %p\n", current_sp);
        
        if (stack_info.ss_sp) {
            ptrdiff_t distance = (char*)current_sp - (char*)stack_info.ss_sp;
            printf("距离栈底: %td 字节\n", distance);
            
            if (distance > 0 && (size_t)distance < stack_info.ss_size) {
                double usage = (double)distance / stack_info.ss_size * 100;
                printf("栈使用率: %.1f%%\n", usage);
            }
        }
    } else {
        printf("获取栈信息失败: %s\n", strerror(errno));
    }
    
    printf("\n");
}

/**
 * 信号处理程序(带监控)
 */
void monitored_signal_handler(int sig) {
    static int call_count = 0;
    call_count++;
    
    printf("监控信号处理程序执行 (第 %d 次, 信号: %d)\n", call_count, sig);
    
    show_detailed_stack_info("信号处理程序中的栈状态");
    
    // 模拟一些栈使用
    char local_buffer[512];
    memset(local_buffer, call_count & 0xFF, sizeof(local_buffer));
    
    printf("  处理程序使用了 %zu 字节本地缓冲区\n", sizeof(local_buffer));
    printf("  处理程序执行完成\n\n");
}

/**
 * 栈使用压力测试
 */
void stack_pressure_test(int depth) {
    char buffer[1024];  // 每层消耗1KB栈空间
    
    // 填充缓冲区
    memset(buffer, depth & 0xFF, sizeof(buffer));
    
    if (depth < 50) {  // 限制递归深度
        printf("递归深度: %d, 使用栈空间: %d KB\n", depth, depth);
        stack_pressure_test(depth + 1);
    } else {
        printf("达到最大递归深度: %d\n", depth);
    }
}

/**
 * 演示信号栈监控和调试
 */
int demo_stack_monitoring() {
    stack_t alt_stack, old_stack;
    char *stack_buffer;
    struct sigaction sa;
    struct rlimit rl;
    
    printf("=== 信号栈监控和调试演示 ===\n");
    
    // 显示系统栈限制
    printf("1. 系统栈限制信息:\n");
    if (getrlimit(RLIMIT_STACK, &rl) == 0) {
        printf("   主栈大小限制: %ld 字节", rl.rlim_cur);
        if (rl.rlim_cur == RLIM_INFINITY) {
            printf(" (无限制)");
        }
        printf("\n");
        printf("   最大栈大小: %ld 字节", rl.rlim_max);
        if (rl.rlim_max == RLIM_INFINITY) {
            printf(" (无限制)");
        }
        printf("\n");
    }
    
    // 显示初始栈状态
    show_detailed_stack_info("初始栈状态");
    
    // 分配备用栈
    size_t stack_size = SIGSTKSZ * 4;  // 4倍标准大小
    stack_buffer = malloc(stack_size);
    if (!stack_buffer) {
        perror("分配备用栈失败");
        return -1;
    }
    
    printf("2. 分配备用栈:\n");
    printf("   请求大小: %zu 字节 (%.1f KB)\n", stack_size, stack_size / 1024.0);
    printf("   分配地址: %p\n", (void*)stack_buffer);
    
    // 设置备用栈
    alt_stack.ss_sp = stack_buffer;
    alt_stack.ss_size = stack_size;
    alt_stack.ss_flags = 0;
    
    printf("3. 设置备用栈:\n");
    if (sigaltstack(&alt_stack, &old_stack) == 0) {
        printf("   ✓ 备用栈设置成功\n");
        show_detailed_stack_info("设置备用栈后");
    } else {
        printf("   ✗ 备用栈设置失败: %s\n", strerror(errno));
        free(stack_buffer);
        return -1;
    }
    
    // 设置监控信号处理程序
    printf("4. 设置监控信号处理程序:\n");
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = monitored_signal_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_ONSTACK;
    
    if (sigaction(SIGUSR1, &sa, NULL) == 0) {
        printf("   ✓ 监控信号处理程序设置成功\n");
    } else {
        printf("   ✗ 监控信号处理程序设置失败: %s\n", strerror(errno));
        free(stack_buffer);
        return -1;
    }
    
    // 发送多个信号进行测试
    printf("5. 发送测试信号:\n");
    for (int i = 1; i <= 3; i++) {
        printf("   发送第 %d 个信号:\n", i);
        if (kill(getpid(), SIGUSR1) == 0) {
            printf("     ✓ 信号发送成功\n");
            sleep(1);  // 等待处理完成
        } else {
            printf("     ✗ 信号发送失败: %s\n", strerror(errno));
        }
    }
    
    // 栈压力测试
    printf("6. 栈压力测试:\n");
    printf("   开始递归栈使用测试...\n");
    stack_pressure_test(0);
    printf("   栈压力测试完成\n");
    
    show_detailed_stack_info("压力测试后栈状态");
    
    // 禁用备用栈
    printf("7. 禁用备用栈:\n");
    stack_t disable_stack;
    disable_stack.ss_flags = SS_DISABLE;
    
    if (sigaltstack(&disable_stack, NULL) == 0) {
        printf("   ✓ 备用栈禁用成功\n");
        show_detailed_stack_info("禁用备用栈后");
    } else {
        printf("   ✗ 备用栈禁用失败: %s\n", strerror(errno));
    }
    
    // 清理资源
    free(stack_buffer);
    
    printf("=== 监控演示完成 ===\n");
    return 0;
}

int main() {
    return demo_stack_monitoring();
}

sigaltstack 使用注意事项

系统要求:

  1. 内核版本: 支持信号备用栈的Linux内核
  2. 权限要求: 通常不需要特殊权限
  3. 架构支持: 支持所有主流架构

栈大小考虑:

  1. 最小大小: 至少MINSIGSTKSZ字节
  2. 推荐大小: 使用SIGSTKSZ或更大
  3. 动态分配: 建议在堆上分配栈空间

错误处理:

  1. ENOMEM: 内存不足
  2. EINVAL: 参数无效
  3. EPERM: 权限不足

安全考虑:

  1. 栈溢出保护: 备用栈可以防止主栈溢出
  2. 信号安全: 确保信号处理程序的安全执行
  3. 资源管理: 及时释放分配的栈空间

最佳实践:

  1. 适当大小: 根据信号处理程序的需求分配栈大小
  2. 错误检查: 始终检查sigaltstack的返回值
  3. 资源清理: 程序结束时释放栈空间
  4. 线程安全: 多线程环境中每个线程需要独立的栈
  5. 监控调试: 监控栈使用情况以便调试

信号栈标志说明

SS_ONSTACK:

  • 含义: 当前正在使用备用栈执行信号处理程序
  • 用途: 检查信号处理程序是否在备用栈上执行

SS_DISABLE:

  • 含义: 备用栈被禁用
  • 用途: 禁用备用栈功能

SS_ENABLED:

  • 含义: 备用栈已启用但未使用
  • 用途: 正常状态,可以使用备用栈

相关常量

SIGSTKSZ:

  • 含义: 系统推荐的信号栈大小
  • 典型值: 8KB或更大

MINSIGSTKSZ:

  • 含义: 信号栈的最小大小
  • 典型值: 2KB

常见使用场景

1. 栈溢出保护:

// 为可能引起栈溢出的程序设置备用栈
stack_t alt_stack;
alt_stack.ss_sp = malloc(SIGSTKSZ);
alt_stack.ss_size = SIGSTKSZ;
alt_stack.ss_flags = 0;
sigaltstack(&alt_stack, NULL);

2. 信号处理程序:

// 为信号处理程序设置独立的执行环境
struct sigaction sa;
sa.sa_handler = signal_handler;
sa.sa_flags = SA_ONSTACK;
sigaction(SIGSEGV, &sa, NULL);

3. 多线程应用:

// 每个线程设置独立的信号栈
void* thread_function(void *arg) {
    stack_t thread_stack;
    thread_stack.ss_sp = malloc(SIGSTKSZ);
    thread_stack.ss_size = SIGSTKSZ;
    thread_stack.ss_flags = 0;
    sigaltstack(&thread_stack, NULL);
    // 线程工作...
}

总结

sigaltstack 是Linux系统中重要的信号处理机制,提供了:

  1. 栈隔离: 为信号处理程序提供独立的执行环境
  2. 异常处理: 防止栈溢出等异常情况
  3. 安全执行: 确保信号处理程序的安全执行
  4. 灵活配置: 支持动态栈管理和配置

通过合理使用 sigaltstack,可以构建更加健壮和安全的信号处理系统。在实际应用中,需要注意栈大小、错误处理和资源管理等关键问题。

此条目发表在未分类分类目录。将固定链接加入收藏夹。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注