Пользовательский API планирования в Mac OSX с использованием ucontext и сигналов

Я разрабатываю алгоритм планирования, который имеет следующие особенности:

  • Имейте 2 пользовательских потока (контексты) в одном процессе (я должен сделать 3 потока, но это еще не работает на OSX, поэтому я решил сделать 2 работы на данный момент)
  • упреждающее использование сигнала SIGALRM, который гаснет 1 раз в секунду и меняет элемент управления из одного контекста в другой, и сохраняет текущее состояние (регистры и текущую позицию) контекста, который был запущен до выполнения переключения.

я заметил следующее:

  • Библиотека ucontext.h ведет себя странно на Mac OSX, тогда как когда она применяется в Linux, она ведет себя именно так, как и должно быть (пример из этой ссылки man: http://man7.org/linux/man-pages/man3/makecontext.3.html работает отлично, как и положено в Linux, тогда как в Mac он завершается с ошибкой сегментации, прежде чем он совершает какую-либо перестановку). Я должен заставить его работать на OSX, к сожалению, а не Linux.
  • Мне удалось обойти ошибку swapcontext на osx с помощью getcontext () и затем setcontext () для переключения контекстов.
  • В моей функции обработчика сигналов я использую sa_sigaction (int sig, siginfo_t * s, void * cntxt), поскольку 3-я переменная, однажды повторно приведенная к нему в качестве указателя ucontext_t, представляет собой информацию о контексте, который был прерван (что верно для Linux как только я проверил это), но на Mac это не указывает на правильное местоположение, поскольку, когда я использую это, я снова получаю ошибку сегментации.

я разработал свои тестовые функции для каждого контекста внутри цикла 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, и запуск версии, работающей в Linux для OSX, вызывает ошибку сегментации, но почему это будет работать на этой ОС, а не на этом?
  • Связано ли это случайно с размером моего стека, который я выделяю в каждом контексте?
  • Я должен иметь пространство стека, выделенное для моего сигнала? (Это говорит о том, что если я этого не сделаю, то использует стек по умолчанию, и если я это сделаю, то это не имеет значения)?
  • Если использование ucontext никогда не даст предсказуемого поведения на Mac OSX, то какова альтернатива реализации пользовательских потоков на OSX? Я попытался использовать tmrjump & longjmp, но я столкнулся с той же проблемой, которая возникает, когда контекст прерывается в середине выполнения определенной функции, тогда как я могу получить точную позицию, где этот контекст был прерван, чтобы продолжить, где я остановился в следующем время?

1 ответ

Решение

Так что после нескольких дней тестирования и отладки я наконец-то получил это. Мне пришлось углубиться в реализацию ucontext.h и найти различия между 2 ОС. Оказывается, что реализация ucontext.h в OSX отличается от Linux. Например, структура mcontext_t в структуре ucontext_t, которая n= обычно содержит значения регистров (PI, SP, BP, общие регистры...) каждого контекста, объявляется как указатель в OSX, тогда как в Linux это не так. Пара других отличий, которые должны были быть установлены в первую очередь, специально: регистр указателя стека (rsp) контекста, регистр базового указателя (rbp), регистр указателя инструкции (rip), регистр индекса назначения (rdi)... Все это должно было быть правильно установленным в начале / создании каждого контекста, а также после того, как он возвращается в первый раз. Я также должен был создать структуру mcontext для хранения этих регистров и указать на нее указатель uc_mcontext моей структуры ucontext_t. После всего, что было сделано, я смог использовать указатель ucontext_t, который был передан в качестве аргумента в функцию-обработчик сигнала sa_sigaction (после того, как я преобразовал его в ucontext_t), чтобы возобновить работу с того места, где контекст был прерван в прошлый раз. В итоге это было грязное дело. Любой, кто интересуется более подробной информацией, может отправить мне сообщение. Джей Джей из.

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