使用sigaction时不调用信号处理程序

时间:2014-02-03 05:40:04

标签: c multithreading scheduling context-switch setitimer

我正在尝试实现用户级线程库,需要以循环方式调度线程。我目前正在尝试使用makecontext,getcontext和swapcontext为我创建的2个线程进行切换工作。使用带有ITIMER_PROF值的setitimer,并为sigaction分配一个处理程序,以便在生成SIGPROF信号时调度新线程。 但是,不调用信号处理程序,因此线程永远不会被调度。可能是什么原因?以下是代码的一些片段:

    void userthread_init(long period){
    /*long time_period = period;
//Includes all the code like initializing the timer and attaching the signal
// handler function "schedule()" to the signal SIGPROF. 
// create a linked list of threads - each thread's context gets added to the list/updated in the list
// in userthread_create*/

    struct itimerval it;
    struct sigaction act;
    act.sa_flags = SA_SIGINFO;
    act.sa_sigaction = &schedule;
    sigemptyset(&act.sa_mask);
    sigaction(SIGPROF,&act,NULL);
    time_period = period;
    it.it_interval.tv_sec = 4;
    it.it_interval.tv_usec = period;
    it.it_value.tv_sec = 1;
    it.it_value.tv_usec = 100000;
    setitimer(ITIMER_PROF, &it,NULL);
    //for(;;);
}

上面的代码是初始化一个计时器并将处理程序计划附加到信号处理程序。我假设信号SIGPROF将被赋予上述函数,该函数将调用scheduler()函数。调度程序功能如下:

void schedule(int sig, siginfo_t *siginf, ucontext_t* context1){
    printf("\nIn schedule");
    ucontext_t *ucp = NULL;
    ucp = malloc(sizeof(ucontext_t));
    getcontext(ucp);
    //ucp = &sched->context;
    sched->context = *context1;
    if(sched->next != NULL){

        sched = sched->next;
    }
    else{
        sched = first;
    }
    setcontext(&sched->context);
}   

我有一个准备好的线程队列,其中存储了各自的上下文。每当执行setcontext指令时,每个线程都应该被调度。但是,不调用scheduler()!任何人都可以指出我的错误?

1 个答案:

答案 0 :(得分:2)

查看代码后完全修改此答案。有几个问题:

  • 有几个编译器警告
  • 您永远不会初始化您的线程ID,而不是在线程创建方法的外部或内部,所以我很惊讶代码甚至可以工作!
  • 您正在读取gtthread_create()函数中未初始化的内存,我在OSX& Linux,在OSX上它崩溃了,在Linux上被一些奇迹初始化了。
  • 在某些地方你调用malloc(),并用指向其他东西的指针覆盖它 - 泄漏内存
  • 你的线程在完成后不会从链表中删除,所以在例行程序完成后会发生奇怪的事情。

当我添加while(1)循环时,我确实看到schedule()被调用并从线程2输出,但是线程1消失为胖空气(可能是因为未初始化的线程ID)。我认为你需要进行大量的代码清理。

以下是我的建议:

  • 修复所有编译器警告 - 即使您认为它们无关紧要,噪音也可能导致您丢失内容(例如指针类型不兼容等)。你正在编译--Wall& -pedantic;这是一件好事 - 所以现在采取下一步措施&解决它们。
  • 将\ n放在printf语句的END处,而不是开头 - 两个线程正在输出到stdout,但它没有被刷新,所以你看不到它。将您的printf("\nMessage");来电更改为printf("Message\n");
  • 使用Valgrind检测内存问题 - valgrind是您用于C / C ++开发的最令人惊奇的工具。它可以通过apt-get& amp;百胜。运行./test1而不是运行valgrind ./test1,它将突出显示内存损坏,内存泄漏,未初始化的读取等。我不能强调这一点; Valgrind很棒。
  • 如果系统调用返回值,请检查 - 在代码中,检查所有getcontext,swapcontext,sigaction,setitimer的返回值
  • 仅从调度程序(或任何信号处理程序)调用异步信号安全方法 - 到目前为止,您已从调度程序中修复了malloc()和printf()。查看the signal(7) man page - 请参阅“异步信号安全功能”
  • 模块化您的代码 - 您的链接列表实现可能更整洁,如果它被分离出来,那么1)您的调度程序将具有更少的代码和更简单,2)您可以隔离链表中的问题,而无需调试调度程序代码。

几乎那里,所以坚持下去 - 但请记住这三个简单的规则:

  1. 随时清洁
  2. 保持编译器警告已修复
  3. 当发生奇怪的事情时,请使用valgrind
  4. 祝你好运!


    旧答案:

    您应该检查任何系统调用的返回值。无论它是否有助于您找到答案,无论如何都应该这样做:)

    检查sigaction()的返回值,如果为-1,则检查errno。 sigaction()可能由于某些原因而失败。如果您的信号处理程序没有被触发,则可能尚未设置它。

    编辑:并确保检查setitimer()的返回值!

    编辑2:只是想一想,你能尝试摆脱malloc()吗? malloc不是信号安全的。例如:像这样:

    void schedule(int sig, siginfo_t *siginf, ucontext_t* context1){
        printf("In schedule\n");
        getcontext(&sched->context);
        if(sched->next != NULL){
            sched = sched->next;
        }
        else{
            sched = first;
        }
        setcontext(&sched->context);
    }   
    

    编辑3:根据this discussion,您不能在信号处理程序中使用printf()。您可以尝试使用write()调用替换它, async-signal safe:

    // printf("In schedule\n");
    const char message[] = "In schedule\n";
    write( 1, message, sizeof( message ) );