2010-06-06 20 views
2

Dans pthread, Après avoir atteint la zone jaune dans la pile, gestionnaire de signal d'arrêt de la fonction récursive en rendant le retourcomment effacer la pile après le signal de débordement de pile se

cependant, nous ne pouvons continuer à utiliser la zone supplémentaire dans la zone jaune, Comment effacer les déchets avant la zone jaune dans la pile de filetage?


(Copié de "réponses"):

#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; 
} 

langage C dans linux, ubuntu

+0

veuillez préciser la plate-forme/la langue dont vous parlez – Dewfy

Répondre

1

Les gestionnaires de signaux sont une mauvaise façon de gérer les erreurs dans un programme. ils sont par nature asynchrones et il y a très peu de choses que vous pouvez faire en eux.

Les fonctions récursives sont également une mauvaise idée - vous n'avez aucune garantie que vous ne déborderez pas de la pile. oui, vous allouez la taille de la pile, oui vous gérez les avertissements de débordement, mais il doit y avoir une meilleure façon de faire ce que vous voulez faire ... Tout ce qu'il faut, c'est un collègue pour ajouter des variables stack-locales dans votre fonction récursive, et que vous modifiez le nombre de récurrences possibles ...

Quoi qu'il en soit pour répondre à votre question initiale ...

  • Dans le gestionnaire de signal, une variable globale (ou thread local) indiquant une état de dépassement de pile

  • Dans le fonction récursive, vérifiez la variable , et si elle est définie, utilisez « mot-clé de la pile magique C nettoyage automatique ...

Oh, a oublié de mentionner ce que mot-clé de nettoyage de la pile magique est:

return; 

Vous pouvez également l'utiliser pour retourner une condition d'erreur dans vos fonctions récursives, de sorte que chaque appelant quittez le traitement et retourner la condition d'erreur à son appelant ...

+0

Est-ce que 'retourner' peut effacer tous les appels avant? Ou juste effacer un appel précédent? – user353573

+1

'Dans la fonction récursive, vérifiez la variable' ... et retour. Cela retournera à la fonction récursive parent, qui va vérifier la variable et retourner à son parent, qui va vérifier la variable ... etc ... – CuriousPanda

+0

cela fonctionne vraiment, je peux le voir revenir au \t d'origine si (gofromyellow == true) { \t \t \t printf ("retour de la zone jaune, appelé% fois d \ n", les temps); \t \t retour; \t} \t autre \t { \t \t call_times = call_times + 1; \t \t main_routine (call_times); \t \t call_times = call_times - 1; Printf ("retour de la zone jaune,% d fois \ n", call_times); \t \t \t \t // gofromyellow = true; \t} – user353573

1

Bien que je trouve q Je suppose que ce que vous voulez faire est d'utiliser siglongjmp pour revenir, comme vous le faites actuellement avec la zone rouge. Cela ramènera la pile au point sigsetjmp.