gpt4 book ai didi

c - 使用 ucontext 和信号的 mac OSX 上的用户线程调度 API

转载 作者:行者123 更新时间:2023-12-01 11:29:42 42 4
gpt4 key购买 nike

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

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

  • 我注意到的是以下内容:
  • ucontext.h 库在 mac osx 上的行为很奇怪,而在 Linux 中应用它时,它的行为完全符合预期(此 man 链接中的示例:http://man7.org/linux/man-pages/man3/makecontext.3.html 在 linux 上正常工作,而在 mac 上却失败了在进行任何交换之前出现段错误)。不幸的是,我必须让它在 osx 上运行,而不是在 linux 上运行。
  • 我设法通过使用 getcontext() 然后 setcontext() 进行上下文交换来解决 osx 上的 swapcontext 错误。
  • 在我的信号处理函数中,我使用 sa_sigaction( int sig, siginfo_t *s, void * cntxt ) 因为第三个变量曾经将它重新转换为 ucontext_t 指针是关于被中断的上下文的信息(这在 Linux 上是正确的)一旦我测试了它)但在 mac 上它没有指向正确的位置,因为当我使用它时我再次遇到段错误。

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

    最后一个注意事项是,我发现在我的 while 循环中调用 getcontext() 并在测试函数中不断更新我当前上下文的位置,因为它是空的 while 循环,因此在该上下文的时间到来时调用 setcontext() 使得它从适当的地方执行。此解决方案是多余的,因为这些功能将从 API 外部提供。
        #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 osx 上使用 ucontext 永远不会给出可预测的行为,那么在 osx 上实现用户线程的替代方法是什么?我尝试使用 tmrjump & longjmp 但我遇到了同样的问题,即当上下文在执行某些函数的过程中被中断时,我怎样才能获得该上下文被中断的确切位置,以便继续我接下来离开的地方时间?
  • 最佳答案

    所以经过几天的测试和调试,我终于得到了这个。我不得不深入研究 ucontext.h 的实现,并发现了 2 个操作系统之间的差异。结果证明 ucontext.h 的 OSX 实现与 Linux 的不同。例如,ucontext_t 结构中的 mcontext_t 结构,其中 n=通常保存每个上下文的寄存器(PI、SP、BP、通用寄存器...)的值,在 OSX 中被声明为指针,而在 Linux 上则不是。需要设置 top 的其他一些差异,特别是上下文的堆栈指针 (rsp) 寄存器、基指针 (rbp) 寄存器、指令指针 (rip) 寄存器、目标索引 (rdi) 寄存器……所有这些都必须在每个上下文的开始/创建以及第一次返回后正确设置。我还让 top 创建了一个 mcontext 结构来保存这些寄存器,并使我的 ucontext_t 结构的 uc_mcontext 指针指向它。在完成所有这些之后,我能够使用在 sa_sigaction 信号处理函数中作为参数传递的 ucontext_t 指针(在我将其重新转换为 ucontext_t 之后),以便准确地恢复上次上下文停止的位置。归根结底,这是一件困惑的事情。有兴趣了解更多详情的可以私信我。 JJ出来。

    关于c - 使用 ucontext 和信号的 mac OSX 上的用户线程调度 API,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33696913/

    42 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com