首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用User和信号的mac OSX上的用户线程调度API

使用User和信号的mac OSX上的用户线程调度API
EN

Stack Overflow用户
提问于 2015-11-13 16:03:15
回答 1查看 1.6K关注 0票数 6

我正在设计一个具有以下特性的调度算法:

  • 在一个进程中有2个用户线程(上下文)(我应该执行3个线程,但这在osx上还不起作用,所以我决定现在让2个线程工作)
  • 先发制人地使用SIGALRM信号,该信号每1秒关闭一次,并将控件从一个上下文更改为另一个上下文,并保存在切换之前运行的上下文的当前状态(寄存器和当前位置)。

我注意到以下几点:

  • 在mac上,ucontext.h库的行为很奇怪,而在Linux中应用时,它的行为完全符合预期的方式(来自这个man链接的例子:http://man7.org/linux/man-pages/man3/makecontext.3.html在linux上运行得很完美,而在mac上,它在做任何交换之前都会因为分段错误而失败)。不幸的是,我不得不让它在osx上运行,而不是linux。
  • 通过使用getcontext() &然后使用setcontext()来交换上下文,我设法解决了osx上的swapcontext错误。
  • 在我的信号处理程序函数中,我使用sa_sigaction( int,siginfo_t *s,void * cntxt ),因为第三个变量一旦重新抛出它,它作为一个ucontext_t指针是关于被中断的上下文的信息(在Linux上测试它是正确的),但是在mac上它没有指向正确的位置,因为当我使用它时,我再次得到一个分段错误。

我已经为每个上下文设计了测试函数,使其循环在while循环中,因为我想中断它们,并确保它们返回到函数中的适当位置执行。我定义了一个静态全局计数变量,它帮助我查看是否处于正确的用户线程中。

最后一个注意事项是,我发现在我的while循环中调用getcontext()和测试函数中的getcontext()会不断更新当前上下文的位置,因为它是空的when循环,因此在上下文出现时调用setcontext()会使它从正确的位置执行。这个解决方案是多余的,因为这些函数将从API外部提供。

代码语言:javascript
复制
    #include <stdio.h>
    #include <sys/ucontext.h>
    #include <string.h>
    #include <stdlib.h>
    #include <stdint.h>
    #include <stdbool.h>
    #include <errno.h>

    /*****************************************************************************/
    /*                            time-utility                                   */
    /*****************************************************************************/

    #include <sys/time.h> // struct timeval

    void timeval_add_s( struct timeval *tv, uint64_t s ) {
        tv->tv_sec += s;
    }

    void timeval_diff( struct timeval *c, struct timeval *a, struct timeval *b ) {

        // use signed variables
        long aa;
        long bb;
        long cc;

        aa = a->tv_sec;
        bb = b->tv_sec;
        cc = aa - bb;
        cc = cc < 0 ? -cc : cc;
        c->tv_sec = cc;

        aa = a->tv_usec;
        bb = b->tv_usec;
        cc = aa - bb;
        cc = cc < 0 ? -cc : cc;
        c->tv_usec = cc;

    out:
        return;
    }

    /******************************************************************************/
    /*                              Variables                                    */
    /*****************************************************************************/
    static int count;

    /* For now only the T1 & T2 are used */
    static ucontext_t T1, T2, T3, Main, Main_2;
    ucontext_t *ready_queue[ 4 ] = { &T1, &T2, &T3, &Main_2 };

    static int thread_count;
    static int current_thread;

    /* timer struct */
    static struct itimerval a;
    static struct timeval now, then;

    /* SIGALRM struct */
    static struct sigaction sa;

    #define USER_THREAD_SWICTH_TIME 1

    static int check;

    /******************************************************************************/
    /*                                 signals                                    */
    /*****************************************************************************/

    void handle_schedule( int sig, siginfo_t *s, void * cntxt ) {
        ucontext_t * temp_current = (ucontext_t *) cntxt;

        if( check == 0 ) {
            check = 1;
            printf("We were in main context user-thread\n");
        } else {
            ready_queue[ current_thread - 1 ] = temp_current;
            printf("We were in User-Thread # %d\n", count );
        }

        if( current_thread == thread_count ) {
            current_thread = 0;
        }
        printf("---------------------------X---------------------------\n");

        setcontext( ready_queue[ current_thread++ ] );

    out:
        return;
    }

    /* initializes the signal handler for SIGALARM, sets all the values for the alarm */
    static void start_init( void ) {
        int r;

        sa.sa_sigaction = handle_schedule;
        sigemptyset( &sa.sa_mask );
        sa.sa_flags = SA_SIGINFO;

        r = sigaction( SIGALRM, &sa, NULL );
        if( r == -1 ) {
            printf("Error: cannot handle SIGALARM\n");
            goto out;
        }

        gettimeofday( &now, NULL );
        timeval_diff( &( a.it_value ), &now, &then );

        timeval_add_s( &( a.it_interval ), USER_THREAD_SWICTH_TIME );
        setitimer( ITIMER_REAL, &a, NULL );

    out:
        return;
    }

    /******************************************************************************/
    /*                      Thread Init                                           */
    /*****************************************************************************/

    static void thread_create( void * task_func(void), int arg_num, int task_arg ) {
        ucontext_t* thread_temp = ready_queue[ thread_count ];

        getcontext( thread_temp );

        thread_temp->uc_link = NULL;
        thread_temp->uc_stack.ss_size = SIGSTKSZ;
        thread_temp->uc_stack.ss_sp = malloc( SIGSTKSZ );
        thread_temp->uc_stack.ss_flags = 0;

        if( arg_num == 0 ) {
            makecontext( thread_temp, task_func, arg_num );
        } else {
            makecontext( thread_temp, task_func, arg_num, task_arg );
        }

        thread_count++;

    out:
        return;
    }

    /******************************************************************************/
    /*                            Testing Functions                               */
    /*****************************************************************************/

    void thread_funct( int i ) {

        printf( "---------------------------------This is User-Thread #%d--------------------------------\n", i );
        while(1) { count = i;} //getcontext( ready_queue[ 0 ] );}

    out:
        return;
    }

    void thread_funct_2( int i ) {
        printf( "---------------------------------This is User-Thread #%d--------------------------------\n", i );
        while(1) { count = i;} //getcontext( ready_queue[ 1 ] ); }

    out:
        return;
    }

    /******************************************************************************/
    /*                               Main Functions                               */
    /*****************************************************************************/

    int main( void ) {
        int r;
        gettimeofday( &then, NULL );

        thread_create( (void *)thread_funct, 1, 1);
        thread_create( (void *)thread_funct_2, 1, 2);

        start_init();

        while(1);

        printf( "completed\n" );

    out:
        return 0;
    }
  • 我在这里做错什么了?为了在Linux上正确地运行它,我必须稍微改变一下&在OSX上运行Linux版本会导致分段错误,但是为什么它要在这个操作系统上工作而不是这个呢?
  • 这是否与我在每个上下文中分配的堆栈大小有关?
  • 我应该为我的信号分配一个堆栈空间吗?(它说,如果我不使用,那么它使用默认堆栈,如果我这样做了,这并没有真正的区别)?
  • 如果在mac上使用ucontext永远不会给出可预测的行为,那么在osx上实现用户线程的替代方案是什么呢?我试着使用tmrjump & longjmp,但是遇到了相同的问题,即当上下文在执行特定函数的过程中被中断时,我如何才能得到上下文被中断的确切位置,以便继续下一次中断的位置?
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-11-16 00:41:00

经过几天的测试和调试,我终于得到了这个结果。我不得不深入研究ucontext.h的实现,发现了2 OS之间的不同之处。结果表明,ucontext.h的OSX实现与Linux的实现不同。例如,mcontext_t结构在ucontext_t结构中,n=usually保存寄存器的值(PI、SP、BP、通用寄存器.)在OSX中,每个上下文都声明为指针,而在Linux上则不是。需要设置的其他两个差异--特别是上下文的堆栈指针(rsp)寄存器、基指针(rbp)寄存器、指令指针(rip)寄存器、目标索引(rdi)寄存器.所有这些都必须在开始/创建每个上下文时以及第一次返回之后正确设置。我还使用了top create mcontext结构来保存这些寄存器,并让我的ucontext_t结构的uc_mcontext指针指向它。在完成所有这些工作之后,我能够使用在sa_sigaction信号处理程序函数中作为参数传递的sa_sigaction指针(在我将它重铸到ucontext_t之后),以便完全恢复上次上下文中断的位置。说到底那是件很麻烦的事情。任何对更多细节感兴趣的人都可以给我发信息。JJ出去。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33696913

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档