Я получил ошибку сегментации, когда имею дело с сигналомSIGALARM
,
Вот мой код
class UThread{
public:
UThread(){}
~UThread(){
signal(SIGALRM,SIG_IGN);
for(size_t i=0;i<thread_list.size();i++){
delete thread_list[i]->uc_stack.ss_sp;
delete thread_list[i];
thread_list[i]=NULL;
}
}
int create_thread(void (*callback)(void *),void *args){
ucontext_t *new_context= new ucontext_t;
assert(getcontext(new_context) != -1);
new_context->uc_stack.ss_sp=new char[1024];
new_context->uc_stack.ss_size=1024;
new_context->uc_flags=0;
new_context->uc_link=0;
assert(new_context->uc_stack.ss_sp!=NULL);
makecontext(new_context,(void (*)())callback,1,args);//make a context
size_t n=thread_list.size();
//find a position to save the pointer.
int i=0;
for(;i<n;i++){
if(thread_list[i]==NULL)
break;
}
if(i<n)
thread_list[i]=new_context;
else{
thread_list.push_back(new_context);
}
return i;
}
void start_thread(){
ucontext_t *main_context= new ucontext_t;
getcontext(main_context);
thread_list.push_back(main_context);
struct sigaction sa;
sa.sa_handler=schedule;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask,SIGALRM);
sigaction(SIGALRM,&sa,NULL);
struct itimerval tick;
tick.it_value.tv_sec = 0;
tick.it_value.tv_usec = 1;
tick.it_interval.tv_sec = 0;
tick.it_interval.tv_usec = 1000;//send a SIGALRM
setitimer(ITIMER_REAL,&tick,NULL);
}
private:
static void schedule(int signo){//deal with the signal
int last_id=current_id;
int n=thread_list.size();
int i=rand()%n;//get a random number.
while(thread_list[i]==NULL){
i=rand()%n;
}
current_id=i;
if(thread_list[last_id]==NULL){//if it has been cancelled,just setcontext.
setcontext(thread_list[i]);
return;
}
swapcontext(thread_list[last_id],thread_list[current_id]);//swap the context.
}
static int current_id;
static vector<ucontext_t*> thread_list;
};
vector<ucontext_t*> UThread::thread_list;
int UThread::current_id=0;
это то, что определяет класс. И он получит ошибку сегментации, если я вызову более двух функций.
void f2(void *){
const char *buf="I am f2.\n";;
while(true){
write(STDOUT_FILENO,buf,strlen(buf));
}
}
void f3(void *){
const char *buf="I am------- f3.\n";
while(true){
write(STDOUT_FILENO,buf,strlen(buf));
}
}
int main(){
UThread t;
t.start_thread();
int thread_id2=t.create_thread(f2,NULL);
int thread_id3=t.create_thread(f3,NULL);
const char *buf="I am main.\n";
while(true){
write(STDOUT_FILENO,buf,strlen(buf));
}
return 0;
}
это вызов функции
если я удалю один из t.create_thread(f3,NULL);
в основной функции, он будет работать без ошибок. Но если добавить два t.create_thread(func,NULL);
к основной функции, он получит ошибку сегментации после swapcontext(thread_list[last_id],thread_list[current_id]);
готово.
Показанный код устанавливает обработчик сигнала:
struct sigaction sa;
sa.sa_handler=schedule;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask,SIGALRM);
sigaction(SIGALRM,&sa,NULL);
это schedule()
функция пытается получить доступ к различным контейнерам библиотеки C ++ и вызвать их методы:
static void schedule(int signo){//deal with the signal
int last_id=current_id;
int n=thread_list.size();
thread_list
ссылка здесь std::vector
,
Ни один из контейнеров библиотеки C ++ не является сигнально-безопасным. Попытка получить к ним доступ в обработчике сигнала — неопределенное поведение. Ваша ошибка сегментации — неизбежный результат. Суть в том, что единственное, что может сделать обработчик сигнала безопасно, это вызвать ограниченное количество системных вызовов. Это даже не может позвонить malloc
или же new
, Это относится не только к SIGALRM
, но к любому сигналу.
Суть не в том, что вы пытаетесь сделать в обработчике сигналов.
Вы указали, что используете Linux. сигнал безопасности (7) На странице руководства Linux перечислены только системные вызовы, которые вы можете выполнять из обработчика сигналов. Если системный вызов отсутствует в списке, его нельзя вызвать из обработчика сигнала. Обратите внимание, что в этом списке нет ничего, связанного с C ++. Ничто в библиотеке C ++ не безопасно для сигналов. Вот и все, конец истории, полная остановка.
Однако в Linux есть способ безопасно обрабатывать сигналы, в коде C ++ используется дескрипторы файлов сигналов. Просмотрите содержание этой страницы руководства, возможно, вы сможете адаптировать свой код для использования дескрипторов файлов сигналов.
Других решений пока нет …