реклама на сайте
подробности

 
 
> scmRTOS+STM32L15x+OpenSTM(+CUBE), проблема с переключением контекста
skyspark
сообщение Oct 7 2015, 23:04
Сообщение #1


Участник
*

Группа: Участник
Сообщений: 31
Регистрация: 20-02-08
Пользователь №: 35 238



Добрый день.

Перенес проект в Eclipse на CDT с таким набором: scmRTOS+STM32L15x+OpenSTM(+CUBE).
Столкнулся с проблемой переключения контекста (после обновления Eclipse до Mars, всех плагинов и самой порты ОС):
Выполняется только задача с максимальным приоритетом, остально время висим тут:
CODE
{...}
while(CurProcPriority != SchedProcPriority); // until context switch done


В отладчике вижу что CurProcPriority = SchedProcPriority, проблема знакомая с AVR8 и оптимизатором в IAR.
Я грешил на что оставил stdlib при переносе и откатился в далекое прошлое, когда в проекте не было sdtlib был родной (порты OC) startup и все работало, поиграть с оптимизацией, собрать yagarto'ой, но это не изменило ситуацию.
Решилось так:

CODE
volatile uint_fast8_t CurProcPriority;


Хочется узнать почему она изначально не объявлена volatile как и SchedProcPriority?

Go to the top of the page
 
+Quote Post
 
Start new topic
Ответов
Сергей Борщ
сообщение Oct 8 2015, 08:55
Сообщение #2


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



для CurProcPriority volatile не нужен, потому что она локальная для каждого процесса. Эта переменная не меняется в течении всего времени выполнения программы. Поскольку при переключении процессов контекст также переключается полностью, то и сравнение всегда присходит с CurProcPriority текущего процесса вне зависимости от того, где она размещена - в ОЗУ или регистре. Согласен с Антоном, надо смотреть по какой причине не вызывается обработчик прерывания переключения контекста.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
dxp
сообщение Oct 8 2015, 09:23
Сообщение #3


Adept
******

Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343



QUOTE (Сергей Борщ @ Oct 8 2015, 14:55) *
для CurProcPriority volatile не нужен, потому что она локальная для каждого процесса. Эта переменная не меняется в течении всего времени выполнения программы. Поскольку при переключении процессов контекст также переключается полностью, то и сравнение всегда присходит с CurProcPriority текущего процесса вне зависимости от того, где она размещена - в ОЗУ или регистре. Согласен с Антоном, надо смотреть по какой причине не вызывается обработчик прерывания переключения контекста.

Погоди, CurProcPriority - это же переменная ядра. Может ты попутал с const TPriority TBaseProcess::Priority? CurProcPriority - это приоритет текущего процесса, значение этой переменной меняется при переключении контекста. Думается, Антон прав, что формально компилятор может засунуть эту переменную в регистр и там держать, не перечитывать каждый раз из памяти. Вот бы и посмотреть по листингу, что там реально. Хотя тоже склоняюсь, что причина, скорее всего, не в этом.

Смутно припоминаю, что этот момент пристально рассматривался, и даже был вариант, когда сравнение было вынесено во встраиваемую функцию, объявленную с квалификатором volatile, но потом остался нынешний вариант. Подробностей не помню.


--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
Go to the top of the page
 
+Quote Post
dxp
сообщение Oct 8 2015, 14:42
Сообщение #4


Adept
******

Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343



QUOTE (dxp @ Oct 8 2015, 15:23) *
Смутно припоминаю, что этот момент пристально рассматривался, и даже был вариант, когда сравнение было вынесено во встраиваемую функцию, объявленную с квалификатором volatile, но потом остался нынешний вариант. Подробностей не помню.

Значит так, появилась ясность в вопросе про волатильность, Сергей наставил на путь истинный. sm.gif

Суть в следующем. Главная тонкость состоит в том, что (цитата Сергея):

QUOTE
enable_context_switch() происходит в одном потоке, а disable_context_switch() - уже в другом


CODE
void TKernel::sched()
{
    uint_fast8_t NextPrty = highest_priority(ReadyProcessMap);
    if(NextPrty != CurProcPriority)
    {
        SchedProcPriority = NextPrty;
    
        raise_context_switch();
        do
        {
            enable_context_switch();
            DUMMY_INSTR();
            disable_context_switch();
        }
        while(CurProcPriority != SchedProcPriority); // until context switch done
    }
}


Переменная CurProcPriority в контексте каждого процесса фактически константа, точнее, не константа, конечно, но в условнии

CODE
while(CurProcPriority != SchedProcPriority);


происходит её чтение и с этого момента уже не важно, из памяти читать или из регистра, в данном процессе эта переменная не меняется

Переменная SchedProcPriority является по сути признаком (меткой, тегом) следующего процесса. Если переключение контекстов произошло, т.е. поток управления прошёл через функцию

CODE
OS::TKernel::context_switch_hook(stack_item_t* sp)

где перед выходом выполняется

CODE
CurProcPriority = SchedProcPriority;

то мы оказываемся в "правильном" - т.е. в том, который был запланирован для переключения, - процессе, у которого его значение CurProcPriority равно SchedProcPriority, и даже если эта переменная закеширована в регистре, то это не мешает, т.к. для этого нового процесса это значение в регистре соответствует SchedProcPriority, хотя операцией

CODE
CurProcPriority = SchedProcPriority;

это значение в регистр не заносилось. Иными словами, любой процесс, прошедший через

CODE
while(CurProcPriority != SchedProcPriority);

имеет в регистре это значение, которое у каждого процесса своё. Т.е. тут можно было бы вообще не CurProcPriority использовать, а просто Priority процесса, но CurProcPriority в функциях ядра технически использовать проще (члены одного класса).

Волатильность для SchedProcPriority нужна как раз по этой же причине: если компилятор закеширует значение этой переменной в регистре (см код функции), то после переключения контекста - т.е. в регистре из контекста другого процесса - будет лежать неверное значение: должно будет лежать значение, равное запланированному, которое в свою очередь равно приоритету процесса, на который произошло переключение, а будет лежать значение запланированного процесса, которое было загружено туда при прошлом переключении этого (на который переключились) процесса.



--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Oct 8 2015, 15:13
Сообщение #5


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



Ага, идея понятна. Каждый процесс, засыпая, читает текущее значение CurProcPriority в какой-то регистр, потом проходит через строчку
enable_context_switch();
, потом спит, потом, просыпаясь, уже имеет в нужном регистре правильное значение CurProcPriority.
Поэтому, если переключение произошло, то в нужном регистре автоматически будет нужное значение.

В этой логике есть изъян. Проблема в том, что если процесс ещё не засыпал, то у него в регистре нет нужного значения!
То есть, в начале работы оси, при переключении с наиболее приоритетного процесса на менее приоритетный, могут возникнуть проблемы. И, судя по описанию, возникли...
В этой связи ещё прошу автора темы дать информацию о ключах компиляции.


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
dxp
сообщение Oct 8 2015, 16:23
Сообщение #6


Adept
******

Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343



QUOTE (AHTOXA @ Oct 8 2015, 21:13) *
В этой логике есть изъян. Проблема в том, что если процесс ещё не засыпал, то у него в регистре нет нужного значения!
То есть, в начале работы оси, при переключении с наиболее приоритетного процесса на менее приоритетный, могут возникнуть проблемы. И, судя по описанию, возникли...

Да, действительно, в самом начале все процессы, кроме самого приоритетного получают управление, "выныривая" из переключателя контекста, т.е. не проходя весь код функции, поэтому, если компилятор закешировал CurProcPriority при первом обращении, то в условии while будет использоваться неверное значение.

QUOTE (AHTOXA @ Oct 8 2015, 21:13) *
В этой связи ещё прошу автора темы дать информацию о ключах компиляции.

Да, хотелось бы воспроизвести ситуацию, дабы убедиться, что так всё и есть. А заодно и workaround применить и проверить более эффективный, нежели простое volatile CurProcPriority.


--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Oct 8 2015, 16:44
Сообщение #7


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



Цитата(dxp @ Oct 8 2015, 19:23) *
Да, действительно, в самом начале все процессы, кроме самого приоритетного получают управление, "выныривая" из переключателя контекста, т.е. не проходя весь код функции,
Нет. В самом начале все процессы "выныривают" в начало TProcess::exec. А уже потом, в процессе работы, попадают в sched() и выныривают из него. При старте все присходит совершенно аналогично прерыванию менее приоритетного процесса - его прерывают где угодно и после возврата управления он продолжает исполнение с той точки, где управление у него отобрали. Выходить из sched() процесс будет только в том случае, если сам добровольно отдал управление в каком-нибудь из сервисов ОС. И это бует происходить только тогда, когда процесс перед этим в sched() зашел. Так что все здесь с логикой хорошо и правильно.


Цитата(AHTOXA @ Oct 8 2015, 18:13) *
Ага, идея понятна. Каждый процесс, засыпая, читает текущее значение CurProcPriority в какой-то регистр, потом проходит через строчку
enable_context_switch();
, потом спит, потом, просыпаясь, уже имеет в нужном регистре правильное значение CurProcPriority.
Я думаю, логика была бы более прозрачна, если бы каждый процесс имел собственную функцию sched(). Тогда бы было ясно видно, что программа уходит в sched() одного процесса, а выскакивает из sched() другого. А из первого вынырнет, когда первый процесс снова получит управление. У нас же код этой функции один на все процессы и неочевидно, что по ходу выполнения программы enable_context_switch() делает один процесс, а следующий за ним disable_context_switch() будет делать уже другой. Но это должно быть хорошо заметно при первых отдачах управления, когда процесс делает enable_context_switch() и после прерывания выполнение продолжается с точки входа в exec() следующего процесса. Очень легко это увидеть в программе из двух-трех процессов, состоящих только из sleep(1).

Еще раз про CurProcPriority - значение этой переменной по ходу выполнения программы, разумеется, меняется. Но каждый процесс всегда видит в ней только одно, свое значение. Иначе у нас не работал бы ни один сервис. Единственное место, где значение этой переменной меняется, вызывается из прерывания переключения контекста, а в прерывании понятие "текущий процесс" смысла не имеет.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Oct 8 2015, 17:20
Сообщение #8


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



Цитата(Сергей Борщ @ Oct 8 2015, 21:44) *
Еще раз про CurProcPriority - значение этой переменной по ходу выполнения программы, разумеется, меняется. Но каждый процесс всегда видит в ней только одно, свое значение.

Вот смотри. Запускается ось, CurProcPriority = 0. Процесс 0 отработал, хочет отдать управление. Вызывает sleep(), оттуда вызывается TKernel::sched():
Там у нас вот что (см. комментарии):
Код
{
    uint_fast8_t NextPrty = highest_priority(ReadyProcessMap);
    if(NextPrty != CurProcPriority)  /// [1] <<-Вот в этой строчке компилятор засовывает CurProcPriority в R1. Это R1 от процесса 0.
    {
        SchedProcPriority = NextPrty;
    
        raise_context_switch();
        do
        {
            enable_context_switch();  // [2]-- здесь процесс 0 засыпает.
            DUMMY_INSTR();
            disable_context_switch(); // [3]-- здесь просыпается процесс 1. Но в его R1 нет значения CurProcPriority! Он ещё не был в точке [1],
        }
        while(CurProcPriority != SchedProcPriority); // until context switch done
    }
}

Вот так и висим. А работало до этого у нас всё потому, что компилятор, по всей видимости, не догадывался кешировать CurProcPriority в регистре.


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
dxp
сообщение Oct 8 2015, 17:35
Сообщение #9


Adept
******

Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343



QUOTE (AHTOXA @ Oct 8 2015, 23:20) *
CODE
{
    uint_fast8_t NextPrty = highest_priority(ReadyProcessMap);
    if(NextPrty != CurProcPriority)  /// [1] <<-Вот в этой строчке компилятор засовывает CurProcPriority в R1. Это R1 от процесса 0.
    {
        SchedProcPriority = NextPrty;
    
        raise_context_switch();
        do
        {
            enable_context_switch();  // [2]-- здесь процесс 0 засыпает.
            DUMMY_INSTR();
            disable_context_switch(); // [3]-- здесь просыпается процесс 1. Но в его R1 нет значения CurProcPriority! Он ещё не был в точке [1],
        }
        while(CurProcPriority != SchedProcPriority); // until context switch done
    }
}

Вот так и висим. А работало до этого у нас всё потому, что компилятор, по всей видимости, не догадывался кешировать CurProcPriority в регистре.

С чего ты взял, что процесс 1 выныривает в точке [3]? Первый раз он выныривает, как заметил Сергей, в начале своей exeс.



--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Oct 8 2015, 17:49
Сообщение #10


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



Семён Семёныч! sm.gif
Это я тормознул, да. Тогда возвращаюсь к своей первоначальной версии.


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post

Сообщений в этой теме
- skyspark   scmRTOS+STM32L15x+OpenSTM(+CUBE)   Oct 7 2015, 23:04
- - AHTOXA   День добрый. volatile для CurProcPriority не нуже...   Oct 8 2015, 08:47
|- - dxp   QUOTE (AHTOXA @ Oct 8 2015, 23:49) Семён ...   Oct 8 2015, 18:04
|- - skyspark   Добрый день. Я проковырялся пару дней с разными в...   Oct 8 2015, 23:40
|- - skyspark   Глянул дизасмом: Вариант без volatile (нерабочий)...   Oct 9 2015, 02:03
|- - dxp   Спасибо за листинг! QUOTE (skyspark @ Oc...   Oct 9 2015, 02:42
|- - AHTOXA   Цитата(skyspark @ Oct 9 2015, 07:03) Вари...   Oct 9 2015, 04:41
|- - skyspark   Цитата(AHTOXA @ Oct 9 2015, 07:41) Хм. ...   Oct 16 2015, 16:24
|- - AHTOXA   Эх, мы так не разберёмся. На вопросы о версии комп...   Oct 16 2015, 16:52
|- - skyspark   Цитата(AHTOXA @ Oct 16 2015, 19:52) Если ...   Oct 17 2015, 00:18
- - Сергей Борщ   Попробую еще большую ясность внести: Перед передач...   Oct 8 2015, 14:56


Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 25th June 2025 - 14:21
Рейтинг@Mail.ru


Страница сгенерированна за 0.01512 секунд с 7
ELECTRONIX ©2004-2016