2010-06-06 47 views
2

在并行线程清除栈溢出信号后堆,达到堆黄色区域后,信号处理程序通过使其返回如何发生

但是停止递归函数,我们只能继续在黄灯区使用额外的面积,

如何清理线程栈中黄色区域之前的垃圾?


(从 “答案” 复制):在Linux

#include <pthread.h> 

#include <stdio.h> 
#include <stdlib.h> 
#include <signal.h> 
#include <setjmp.h> 
#include <sys/mman.h> 
#include <unistd.h> 
#include <assert.h> 
#include <sys/resource.h> 


#define ALT_STACK_SIZE (64*1024) 
#define YELLOW_ZONE_PAGES (1) 

typedef struct { 
    size_t stack_size; 
    char* stack_pointer; 
    char* red_zone_boundary; 
    char* yellow_zone_boundary; 

    sigjmp_buf return_point; 
    size_t red_zone_size; 
} ThreadInfo; 

static pthread_key_t thread_info_key; 
static struct sigaction newAct, oldAct; 
bool gofromyellow = false; 
int call_times = 0; 

static void main_routine(){ 
    // make it overflow 
    if(gofromyellow == true) 
    { 
     printf("return from yellow zone, called %d times\n", call_times); 
     return; 
    } 
    else 
    { 
     call_times = call_times + 1; 
     main_routine(); 
     gofromyellow = true; 
    } 
} 
// red zone management 
static void stackoverflow_routine(){ 
    fprintf(stderr, "stack overflow error.\n"); 
    fflush(stderr); 
} 
// yellow zone management 
static void yellow_zone_hook(){ 
    fprintf(stderr, "exceed yellow zone.\n"); 
    fflush(stderr); 
} 

static int get_stack_info(void** stackaddr, size_t* stacksize){ 
    int ret = -1; 
    pthread_attr_t attr; 
    pthread_attr_init(&attr); 

    if(pthread_getattr_np(pthread_self(), &attr) == 0){ 
     ret = pthread_attr_getstack(&attr, stackaddr, stacksize); 
    } 
    pthread_attr_destroy(&attr); 
    return ret; 
} 

static int is_in_stack(const ThreadInfo* tinfo, char* pointer){ 
    return (tinfo->stack_pointer <= pointer) && (pointer < tinfo->stack_pointer + tinfo->stack_size); 
} 

static int is_in_red_zone(const ThreadInfo* tinfo, char* pointer){ 
    if(tinfo->red_zone_boundary){ 
     return (tinfo->stack_pointer <= pointer) && (pointer < tinfo->red_zone_boundary); 
    } 
} 
static int is_in_yellow_zone(const ThreadInfo* tinfo, char* pointer){ 
    if(tinfo->yellow_zone_boundary){ 
     return (tinfo->red_zone_boundary <= pointer) && (pointer < tinfo->yellow_zone_boundary); 
    } 
} 

static void set_yellow_zone(ThreadInfo* tinfo){ 
    int pagesize = sysconf(_SC_PAGE_SIZE); 
    assert(pagesize > 0); 
    tinfo->yellow_zone_boundary = tinfo->red_zone_boundary + pagesize * YELLOW_ZONE_PAGES; 
    mprotect(tinfo->red_zone_boundary, pagesize * YELLOW_ZONE_PAGES, PROT_NONE); 
} 

static void reset_yellow_zone(ThreadInfo* tinfo){ 
    size_t pagesize = tinfo->yellow_zone_boundary - tinfo->red_zone_boundary; 
    if(mmap(tinfo->red_zone_boundary, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0) == 0){ 
     perror("mmap failed"), exit(1); 
    } 
    mprotect(tinfo->red_zone_boundary, pagesize, PROT_READ | PROT_WRITE); 
    tinfo->yellow_zone_boundary = 0; 
} 

static void signal_handler(int sig, siginfo_t* sig_info, void* sig_data){ 
    if(sig == SIGSEGV){ 
     ThreadInfo* tinfo = (ThreadInfo*) pthread_getspecific(thread_info_key); 
     char* fault_address = (char*) sig_info->si_addr; 

     if(is_in_stack(tinfo, fault_address)){ 
      if(is_in_red_zone(tinfo, fault_address)){ 
       siglongjmp(tinfo->return_point, 1); 
      }else if(is_in_yellow_zone(tinfo, fault_address)){ 
       reset_yellow_zone(tinfo); 
       yellow_zone_hook(); 
       gofromyellow = true; 
       return; 
      } else { 
       //inside stack not related overflow SEGV happen 
      } 
     } 
    } 
} 

static void register_application_info(){ 
    pthread_key_create(&thread_info_key, NULL); 

    sigemptyset(&newAct.sa_mask); 
    sigaddset(&newAct.sa_mask, SIGSEGV); 
    newAct.sa_sigaction = signal_handler; 
    newAct.sa_flags = SA_SIGINFO | SA_RESTART | SA_ONSTACK; 

    sigaction(SIGSEGV, &newAct, &oldAct);  
} 

static void register_thread_info(ThreadInfo* tinfo){ 
    stack_t ss; 

    pthread_setspecific(thread_info_key, tinfo); 

    get_stack_info((void**)&tinfo->stack_pointer, &tinfo->stack_size); 

    printf("stack size %d mb\n", tinfo->stack_size/1024/1024); 

    tinfo->red_zone_boundary = tinfo->stack_pointer + tinfo->red_zone_size; 

    set_yellow_zone(tinfo); 

    ss.ss_sp = (char*)malloc(ALT_STACK_SIZE); 
    ss.ss_size = ALT_STACK_SIZE; 
    ss.ss_flags = 0; 
    sigaltstack(&ss, NULL); 
} 

static void* thread_routine(void* p){ 
    ThreadInfo* tinfo = (ThreadInfo*)p; 

    register_thread_info(tinfo); 

    if(sigsetjmp(tinfo->return_point, 1) == 0){ 
     main_routine(); 
    } else { 
     stackoverflow_routine(); 
    } 
    free(tinfo); 
    printf("after tinfo, end thread\n"); 
    return 0; 
} 

int main(int argc, char** argv){ 
    register_application_info(); 

    if(argc == 2){ 
     int stacksize = atoi(argv[1]); 

     pthread_attr_t attr; 
     pthread_attr_init(&attr); 
     pthread_attr_setstacksize(&attr, 1024 * 1024 * stacksize); 
     { 
      pthread_t pid0; 
      ThreadInfo* tinfo = (ThreadInfo*)calloc(1, sizeof(ThreadInfo)); 

      pthread_attr_getguardsize(&attr, &tinfo->red_zone_size); 
      pthread_create(&pid0, &attr, thread_routine, tinfo); 
      pthread_join(pid0, NULL); 
     } 
    } else { 
     printf("Usage: %s stacksize(mb)\n", argv[0]); 
    } 
    return 0; 
} 

C语言,Ubuntu的

+0

请指定您所说的平台/语言 – Dewfy 2010-06-06 08:40:24

回答

1

信号处理程序是在程序中的错误处理一个糟糕的方式。它们在本质上是异步的,而且你可以在其中做很少的事情。

递归函数也是一个坏主意 - 你不能保证你不会溢出堆栈。是的,你分配堆栈大小,是的,你正在处理溢出警告,但是你必须有一个更好的方式来做你想做的事......只需要一个同事在递归函数中添加一些堆栈局部变量,你改变可能递归的次数......

反正回答你原来的问题...

  • 在信号处理程序,设置指示 一个全球 (或线程本地)变量堆栈溢出情况

  • 在 递归函数,检查 变量,如果设置,使用“C的 魔术堆栈自动清理 关键字...

哦,忘了提,魔术堆栈清理关键字是什么:

return; 

你也可以用它来在你的递归函数返回一个错误状态,所以每个来电者将退出处理并返回错误状态返回到它的调用者...

+0

可以'返回'之前清除所有呼叫吗?或者只是清除以前的一个电话? – user353573 2010-06-06 10:26:20

+1

'在递归函数中,检查变量'...并返回。 这将返回到父递归函数,它将检查变量并返回到它的父项,它将检查变量...等等... – CuriousPanda 2010-06-06 10:49:33

+0

它确实有效,我可以看到它返回到原来的 \t如果(gofromyellow ==真) \t { \t \t的printf( “从黄区的回报,称为%d次\ n”,次); \t \t return; \t} \t别的 \t { \t \t call_times = call_times + 1; \t \t main_routine(call_times); \t \t call_times = call_times - 1; printf(“从黄区返回,%d次\ n”,call_times); \t \t // gofromyellow = true; \t} – user353573 2010-06-06 11:02:32

1

虽然我找到第q有点混乱,我怀疑你想要做的是使用siglongjmp返回,因为你目前正在使用红色区域。这会将堆叠倒回sigsetjmp点。