Почему я получаю ошибку сегментации, когда я сигнал

Я получил ошибку сегментации, когда имею дело с сигналом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]); готово.

1 ответ

Решение

Показанный код устанавливает обработчик сигнала:

    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. На странице справочника Linux -security (7) Linux перечислены только системные вызовы, которые вы можете выполнять из обработчика сигналов. Если системный вызов отсутствует в списке, его нельзя вызвать из обработчика сигнала. Обратите внимание, что в этом списке нет ничего, связанного с C++. Ничто в библиотеке C++ не безопасно для сигналов. Вот и все, конец истории, полная остановка.

Однако в Linux существует способ безопасной обработки сигналов в коде C++ с использованием дескрипторов файлов сигналов. Просмотрите содержание этой страницы руководства, возможно, вы сможете адаптировать свой код для использования дескрипторов файлов сигналов.

Другие вопросы по тегам