|
Стек для прерываний, и недостаток таймеров |
|
|
|
Jul 26 2009, 20:51
|
Местный
  
Группа: Участник
Сообщений: 421
Регистрация: 2-01-08
Пользователь №: 33 778

|
Кто нибудь делал отдельный стек для прерываний? есть ли смысл? код думаю писать как то так, Код handler: sts stack-0, r16 sts stack-1, r17 in r16, __SP_L_ in r17, __SP_H_ sts stack-2, r16 sts stack-3, r17 ldi r16, lo8(stack-4) ldi r17, hi8(stack-4) out __SP_L__, r16 out __SP_H__, r17 //здесь код обработчика прерывания // востановление писать лень ) тут все понятно reti_ Не слишком ли будет тяжеловесно? хотя это не главный вопрос, основная сложность с компилятором (GCC) как его заставить генерировать обработчтки в таком виде? можно __attribute__ ((__naked__)) но тогда надо как то следить за тем какие регистры были использованы, не сохранять же все. Похоже надо начинать смотреть исходники GCC. Правка: push/pop и st/ld длятся все по 2 такта, можно просто заменить все push на sts с фиксированными адресами ... потерь в скорости не будет.
Сообщение отредактировал amaora - Jul 26 2009, 21:17
|
|
|
|
|
Jul 26 2009, 20:58
|
Местный
  
Группа: Участник
Сообщений: 421
Регистрация: 2-01-08
Пользователь №: 33 778

|
И вопрос второй, в atmega16 три таймера, а что если надо больше? нулевой занят планировщиком для прееключения задач, первый планирую для ШИМ, второй для часов с отдельным 32.768 кГц резонатором. А надо вот ещё опрашивать небольшую клавиатуру с частотой от 60..120 Гц. Что можно сделать? сменить MCU не вариант. Цитата(aaarrr @ Jul 27 2009, 00:55)  Вот на этот вопрос Вам и надо сначала найти ответ. Я лично не вижу ни малейшего смысла. Не хочется держать на стеке каждой задачи пустое место размером ~20 байт. Пока сложно сказать хватит ли 1кб на все.
Сообщение отредактировал amaora - Jul 26 2009, 20:59
|
|
|
|
|
Jul 26 2009, 21:19
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(amaora @ Jul 27 2009, 00:58)  А надо вот ещё опрашивать небольшую клавиатуру с частотой от 60..120 Гц. Что можно сделать? сменить MCU не вариант. Можно сделать программный таймер, не обязательно же для каждого чиха иметь отдельный аппаратный. Цитата(amaora @ Jul 27 2009, 00:58)  Не хочется держать на стеке каждой задачи пустое место размером ~20 байт. Пока сложно сказать хватит ли 1кб на все. Понятно. Тогда нужно оценивать все в целом - от тяжеловесности прерываний, до целесообразности вообще применения ОС в столь стесненных условиях. Я бы делать еще один стек не стал, но хозяин - барин.
|
|
|
|
|
Jul 27 2009, 05:47
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(amaora @ Jul 26 2009, 23:51)  Кто нибудь делал отдельный стек для прерываний? есть ли смысл? Когда писАл на асме, оформлял задачи "для каждого чиха", в виде подпрограмм с переопределяемой точкой входа, простенький TCB содержал 8 байт (pc:2, sp:2, X:2,Z:2), за ним следовали локальные переменные (на них всегда указывает Y) и стек. Все задачи, ессно, могли быть реентерабельны. Принимая во внимание, что на мега16 нельзя создать андроида  , рискну предположить, что глубина стека в этом случае будет байт 16. Но вот Сишные концепты - не влезают в эту схему. Грубое юзание стека не позволит.
|
|
|
|
|
Jul 27 2009, 09:35
|
Местный
  
Группа: Свой
Сообщений: 327
Регистрация: 12-04-05
Из: Новосибирск
Пользователь №: 4 057

|
Цитата(defunct @ Jul 27 2009, 05:01)  Возьмите за основу кооперативную ОС, - один общий стек на все задачи и прерывания. А разве в кооперативной ОС один стек на все задачи? Насколько я понимаю, кооперативная ОС отличается от некооперативной тем, что в первом случае, задача сама решает, когда передать управление другой задаче, а во втором - ОС насильно прерывает одну задачу и передает управление другой. Или я ошибаюсь в терминологии?
|
|
|
|
|
Jul 27 2009, 11:01
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(Zlumd @ Jul 27 2009, 12:35)  А разве в кооперативной ОС один стек на все задачи? Да. Иначе какой в ней смысл и преимущество над вытесняющей ОС? Цитата Насколько я понимаю, кооперативная ОС отличается от некооперативной тем, что в первом случае, задача сама решает, когда передать управление другой задаче, а во втором - ОС насильно прерывает одну задачу и передает управление другой. Или я ошибаюсь в терминологии? В общем верно, если "передать управление другой задаче" заменить на "передать управление системе". Теперь, возьмем во внимание, - если задача сама решает когда передать управление системе, то что мешает ее построить в виде конечной функции, а планировщику периодически эту функцию вызывать? Цитата(SasaVitebsk @ Jul 27 2009, 13:04)  Я бы тоже. Да ещё бы ввёл флаг-байт с временными метками... ну типа светодиодиком помигать с частотой 2Гц либо 1Гц и т.д. Не проще на один таймер посадить только планировщик. Который бы вызывал остальные задачи ровно тогда когда их нужно вызывать?
|
|
|
|
|
Jul 27 2009, 16:54
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(amaora @ Jul 27 2009, 18:56)  А вот генерацию пролога/эпилога прерываний придется похоже выполнять с помощью скриптов Имхо, это шаг в сторону. Жизнеспособны в этом случае только варианты написания прерываний либо на чистом Си, либо на чистом асме. Остальное - полумеры, рано или поздно кривизна такого подхода себя обнаружит. Цитата По поводу кооперативной многозадачности Например, так Код void system (int priority) { switch(priority) { case 0: task0(); case 1: task1(); task2(); case 2: task3(); } } /*........................*/ void task0(void) { if(!data_ready()) system(1); /*............*/ } int main(void) { while(1) system(0); return 0; } ЗЫ: при невозможности разрулить приоритетами мютексы проще встраивать в саму задачу.
|
|
|
|
|
Jul 28 2009, 01:05
|
Местный
  
Группа: Свой
Сообщений: 327
Регистрация: 12-04-05
Из: Новосибирск
Пользователь №: 4 057

|
Цитата(defunct @ Jul 27 2009, 18:01)  Да. Иначе какой в ней смысл и преимущество над вытесняющей ОС? Преимущество в том, что нужно тратить меньше усилий по разделению доступа к общим ресурсам. Например, если какая-то многобайтовая переменная используется в обоих задачах, то в вытесняющей ОС при изменении этой переменной приходится запрещать прерывания, а в кооперативной этого не надо делать. Цитата(defunct @ Jul 27 2009, 18:01)  В общем верно, если "передать управление другой задаче" заменить на "передать управление системе". Теперь, возьмем во внимание, - если задача сама решает когда передать управление системе, то что мешает ее построить в виде конечной функции, а планировщику периодически эту функцию вызывать? Так это не несколько паралельно выполняющихся задач. Это одна задача. Каждая задача подразумевает сохранение всех локальных переменных, которые лежат в стеке, при переключении с задачи на задачу.
|
|
|
|
|
Jul 28 2009, 01:33
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(Zlumd @ Jul 28 2009, 04:05)  Так это не несколько паралельно выполняющихся задач. Это одна задача. Как не крути а процессор один, и для него любая программа будет одной задачей. Вопрос в абстрагировании. Ниже приведено две реализации одного и того же: 1) Код task() { while( true) { wait_sema( sema_x); do_stuff(); } } 2) Код task() { if ( sema_x) do_stuff(); } Пусть памяти всего 1KB, и есть 3 таска. Во втором варианте каждый таск может запросто взять и пользовать например 512 байт под временный буфер, в первом же 3x512B > 1KB и приехали. Цитата Каждая задача подразумевает сохранение всех локальных переменных, которые лежат в стеке, при переключении с задачи на задачу Дайте каждой задаче static контекст с которым она работает, оформите как обычную функцию, массивные временные переменные/буферы выделяйте в стеке, и вызывайте их все поочереди(и/или по условию) и получите много задач, "параллельно" выполняющихся (находящихся в разной стадии исполнения), плюс оптимальное распределение памяти.
|
|
|
|
|
Jul 28 2009, 06:01
|
Местный
  
Группа: Свой
Сообщений: 327
Регистрация: 12-04-05
Из: Новосибирск
Пользователь №: 4 057

|
Цитата(defunct @ Jul 28 2009, 08:33)  Как не крути а процессор один, и для него любая программа будет одной задачей. Вопрос в абстрагировании. Ниже приведено две реализации одного и того же: 1) Код task() { while( true) { wait_sema( sema_x); do_stuff(); } } 2) Код task() { if ( sema_x) do_stuff(); } Пусть памяти всего 1KB, и есть 3 таска. Во втором варианте каждый таск может запросто взять и пользовать например 512 байт под временный буфер, в первом же 3x512B > 1KB и приехали. Так очень неудобно код писать. Что делать если хочется посидеть в do_stuff() примерно минуту ? Цитата(defunct @ Jul 28 2009, 08:33)  Дайте каждой задаче static контекст с которым она работает, оформите как обычную функцию, массивные временные переменные/буферы выделяйте в стеке, и вызывайте их все поочереди(и/или по условию) и получите много задач, "параллельно" выполняющихся (находящихся в разной стадии исполнения), плюс оптимальное распределение памяти. Static - это необоснованный расход памяти. Вот пример: Код Thread1() { func1(); func2(); func3(); func4(); func5(); func6(); func7(); func8(); } Пусть в каждой фунции func1...func8 есть локальные переменные по 100 байт в каждой. Если все staticами объявить, то Thread1 800 байт памяти отъест, если auto - то всего 100 байт. Может быть у нас терминология не совпадает? Я так понимаю, что задачи в вытесняющей ОС - это эквивалент Thread в программировании под Windows. А задачи в кооперативной ОС - это эквивалент Fiber. Я правильно понимаю?
|
|
|
|
|
Jul 28 2009, 07:43
|
Местный
  
Группа: Участник
Сообщений: 421
Регистрация: 2-01-08
Пользователь №: 33 778

|
Цитата(_Pasha @ Jul 27 2009, 20:54)  Имхо, это шаг в сторону. Жизнеспособны в этом случае только варианты написания прерываний либо на чистом Си, либо на чистом асме. Остальное - полумеры, рано или поздно кривизна такого подхода себя обнаружит. С этим согласен, а пример не очень понятный. Цитата(defunct @ Jul 28 2009, 05:33)  Вопрос в абстрагировании. Ниже приведено две реализации одного и того же: ... Во первых если задаче надо так много памяти то очень вероятно, что она долго не будет возвращать управление системе. Во вторых если в системе много ввода/вывода, то что регулярно делать if (data_ready) do_stuff() ? не лучше ли wait_for_event() который блокирует задачу (выводит из планирования), а потом в прерывании она снова ставится в очередь. Так и код проще выглядит.
|
|
|
|
|
Jul 28 2009, 23:02
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(Zlumd @ Jul 28 2009, 09:01)  Так очень неудобно код писать. Что делать если хочется посидеть в do_stuff() примерно минуту ? Например сделать свою реализацию ks_wait() / ks_yeild() - которая будет запускать оставшиеся задачи прямо из текущей. Есть конечно и другой вариант, - разбить do_stuff() на две части, и запускать вторую часть через минуту после первой. Цитата Static - это необоснованный расход памяти. Именно. А Вы же собрались стек статически распределить. Плюс вероятно еще и контекст регистров каждого таска хранить, статически. В Вашем случае потерь памяти будет куда больше, чем если просто выделить ровно столько памяти сколько нужно для полезных данных. Цитата Пусть в каждой фунции func1...func8 есть локальные переменные по 100 байт в каждой. Если все staticами объявить, то Thread1 800 байт памяти отъест, если auto - то всего 100 байт. Не нужно бездумно объявлять все статиками. Делайте статиком только, то что должно быть статиком. Например задача обработки принятого пакета данных - вырождается в 1. вынуть пакет из очереди, 2. обработать 3. засунуть в другую очередь. 4. Гото 1. Сколько же нужно данных хранить статически для этой задачи? Помоему очевидно, если обеспечить непрерывное выполнение с п.1. до п.3. то статически хранить потребуется всего лишь два указателя на входную и выходную очереди. Цитата Может быть у нас терминология не совпадает? Я так понимаю, что задачи в вытесняющей ОС - это эквивалент Thread в программировании под Windows. А задачи в кооперативной ОС - это эквивалент Fiber. Я правильно понимаю? И да и нет. Насчет первого утверждения - сравнение Thread с таском вытесняющей ОС подходящее. Что же касается сравнения с Fiber в кооперативной ОС, ну не совсем оно то, хотя близко: 1. Никто не заставляет App управлять процессом, процессом запуска (schedule'ом) может заниматься ядро ОС, задача лишь обязана вернуть управление этому ядру - либо нативно - выход из функции, либо непосредственным вызовом соответствующей системной функции. 2. Никто не заставляет наделять задачу в кооперативной ОС своим стеком. В связи с тем, что задача сама отдает управление ОС когда ей удобно, - все задачи могут ужиться с одним общим стеком. Цитата(amaora @ Jul 28 2009, 10:43)  Во первых если задаче надо так много памяти то очень вероятно, что она долго не будет возвращать управление системе. Почему же, вот примеры где может потребоваться большой буфер, но не нужно много времени: - банальный ping - чтение/запись внешней флешки - обработка модбас пакета и т.д. все где имеет место запрос-ответ. Цитата Во вторых если в системе много ввода/вывода, то что регулярно делать if (data_ready) do_stuff() ? не лучше ли wait_for_event() который блокирует задачу (выводит из планирования), а потом в прерывании она снова ставится в очередь. Так и код проще выглядит. Бесспорно так лучше, абсолютно согласен. Но это же концепции не меняет, соответвующий if просто уйдет "в недра" планировщика, а задача будет состоять из одного только do_stuff(). ks_run( do_stuff, &do_stuff_context, <event_id>, <timeout> );
|
|
|
|
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|