|
scmRTOS+STM32L15x+OpenSTM(+CUBE), проблема с переключением контекста |
|
|
|
Oct 7 2015, 23:04
|
Участник

Группа: Участник
Сообщений: 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?
|
|
|
|
|
 |
Ответов
|
Oct 8 2015, 09:23
|

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, но потом остался нынешний вариант. Подробностей не помню.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Oct 8 2015, 14:42
|

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

|
QUOTE (dxp @ Oct 8 2015, 15:23)  Смутно припоминаю, что этот момент пристально рассматривался, и даже был вариант, когда сравнение было вынесено во встраиваемую функцию, объявленную с квалификатором volatile, но потом остался нынешний вариант. Подробностей не помню. Значит так, появилась ясность в вопросе про волатильность, Сергей наставил на путь истинный.  Суть в следующем. Главная тонкость состоит в том, что (цитата Сергея): 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 нужна как раз по этой же причине: если компилятор закеширует значение этой переменной в регистре (см код функции), то после переключения контекста - т.е. в регистре из контекста другого процесса - будет лежать неверное значение: должно будет лежать значение, равное запланированному, которое в свою очередь равно приоритету процесса, на который произошло переключение, а будет лежать значение запланированного процесса, которое было загружено туда при прошлом переключении этого (на который переключились) процесса.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Oct 8 2015, 15:13
|

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

|
Ага, идея понятна. Каждый процесс, засыпая, читает текущее значение CurProcPriority в какой-то регистр, потом проходит через строчку enable_context_switch(); , потом спит, потом, просыпаясь, уже имеет в нужном регистре правильное значение CurProcPriority. Поэтому, если переключение произошло, то в нужном регистре автоматически будет нужное значение.
В этой логике есть изъян. Проблема в том, что если процесс ещё не засыпал, то у него в регистре нет нужного значения! То есть, в начале работы оси, при переключении с наиболее приоритетного процесса на менее приоритетный, могут возникнуть проблемы. И, судя по описанию, возникли... В этой связи ещё прошу автора темы дать информацию о ключах компиляции.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Oct 8 2015, 16:23
|

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.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Oct 8 2015, 16:44
|

Гуру
     
Группа: Модераторы
Сообщений: 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)
|
|
|
|
|
Oct 8 2015, 17:20
|

фанат дивана
     
Группа: Свой
Сообщений: 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 в регистре.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Oct 8 2015, 17:35
|

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с.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
Сообщений в этой теме
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
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|