在pthread中,在堆栈中到达黄色区域后,信号处理程序通过返回来停止递归函数
但是,我们只能继续使用黄色区域的额外区域,
如何清除线程堆栈中黄色区域之前的垃圾?
(从“答案”复制):
#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;
}
linux中的C语言,ubuntu
答案 0 :(得分:1)
信号处理程序是处理程序错误的一种不好方法。它们本质上是异步的,你可以用它们做很少的事情。
递归函数也是一个坏主意 - 您无法保证不会溢出堆栈。是的,你分配堆栈大小,是的,你正在处理溢出警告,但必须有一个更好的方法来做你想做的事情...所有需要的是一个同事在你的递归函数中添加一些堆栈局部变量,并且你改变了可能的递归次数......
无论如何要回答你原来的问题...
在信号处理程序中,设置全局 (或线程局部的)变量指示 堆栈溢出情况
在 递归函数,检查 变量,如果已设置,请使用'C' 魔术堆栈自动清理 关键字...
哦,忘了提一下魔术堆清理关键字是什么:
return;
您还可以使用它在递归函数中返回错误条件,这样每个调用者都会退出处理并将错误条件返回给调用者...
答案 1 :(得分:1)
虽然我觉得这个问题有点令人困惑,但我怀疑你想要做的是使用siglongjmp
返回,因为你正在使用红色区域。这会将堆栈重新回到sigsetjmp
点。