OSDev

для всех
Текущее время: 01 май 2024, 11:55

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 16 ]  На страницу Пред.  1, 2
Автор Сообщение
СообщениеДобавлено: 08 сен 2010, 15:54 

Зарегистрирован: 21 сен 2007, 17:24
Сообщения: 1088
Откуда: Балаково
phantom-84 писал(а):
часто стеки ядра имеют фиксированный размер.

Это понятно. Моё предложение касалось обработки вложенных прерываний. Для этого я предлагал динамически расширять стек.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 08 сен 2010, 20:16 

Зарегистрирован: 28 окт 2007, 18:33
Сообщения: 1418
У меня подход базируется на следующих принципах.

1. Любые программные прерывания (INT, деление на 0 и т.п.) могут происходить исключительно в коде приложений; в коде ядра или драйверов режима ядра они недопустимы, а если случаются, это приводит к краху системы (БСОДу, так сказать). Посему программные прерывания у меня вложенными быть не могут в принципе.

2. Внешние аппаратные прерывания могут быть вложенными, однако от одного устройства в обработке может быть только одно прерывание. Это обеспечивается тем, что соответствующий драйвер, получив управление по прерыванию, запрещает дальнейшие прерывания от этого устройства до тех пор, пока обработка текущего не будет полностью запрещена. Следовательно, максимальное число одновременно активных обработчиков прерываний равно числу устройств, прерывания от которых маскируются независимо. Зная потребности в стеке для каждого из обработчиков, можно подсчитать общий необходимый размер стека.

3. Сложная обработка внешних прерываний (например, связанная с обработкой завершения операции ввода-вывода) и вся обработка программных прерываний ведётся в режиме отложенной обработки. Суть в том, что обработчик прерывания, получающий управление с запрещёнными прерываниями, выполняет минимально необходимые действия (в частности, запрещает дальнейшие прерывания от устройства, которое его сейчас вызвало), после чего вызывает подпрограмму ядра, которая переводит его в режим отложенной обработки. Все отложенные обработчики выполняются при разрешённых прерываниях (поэтому новое прерывание может быть обслужено немедленно, что обеспечивает быструю реакцию на срочные внешние события), но строго последовательно: пока один отложенный обработчик не закончит работу, следующий управление не получит. В случае однопроцессорной системы (а я сейчас ковыряюсь по долгу службы с АРМом, где именно так и есть) никаких мер "внутриядерной" синхронизации поэтому не возникает вообще: доступ к общесистемным данным получается строго упорядоченным. Конечно, в случае многопроцессорной системы потребовались бы дополнительные механизмы, но достаточно простые, если соблюдать ряд мер.

В итоге получается, что размер стека может быть точно определён: он складывается из размеров, необходимых всем первичным обработчикам внешних прерываний, вместе взятым, плюс самому прожорливому обработчику программных прерываний, плюс самому прожорливому из отложенных обработчиков. По опыту RSX-11 (там используется именно этот механизм; в несколько расширенном виде он перекочевал в VAX/VMS, а оттуда, опять с расширениями -- в Win NT), потребный объём составит от нескольких десятков до нескольких сотен байт. Правда, архитектура АРМ навязывает использование нескольких стеков, но лишь один из них является основным; я ему пока отвёл 1 Кбайт -- шоб с запасом. Остальные стеки имеют по 64 байта -- каждый из них используется лишь при выполнении первичных обработчиков прерываний, а они простые и со скромными аппетитами.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 08 сен 2010, 21:56 

Зарегистрирован: 18 апр 2010, 15:59
Сообщения: 155
SII писал(а):
ИМХО, множество стеков лишь усложняет жизнь, ну а предсказуемость достигается иными методами.

Разделение стеков, помимо прочего, облегчает реализацию горизонтальной и вертикальной реентерабельности. Вы не учитываете, того факта, что РТ-системы должны передавать задаче управление сразу же по возникновению определенного события (прерывание, освободился ресурс и т.д.). В том числе это должно происходить и при выполнении кода ядра ОС. Поэтому например Линукс не является РТ-системой, именно потому что его ядро не является реентерабельным и любая РТ-задача будет ожидать завершения выполнения системого вызова неопределенной продолжительности теоретически стремящейся к бесконечности, что в свою очередь ставит крест на предсказуемости. Поэтому, при достижении определенного события мы должны иметь возможность переключиться на контекст другой задачи в не зависимости от того чей код выполняется ядра или самой задачи. Это приводит к необходимости разделения стека ядра на две части: одну общую для прерываний (т.к. они не зависят от задач и их стек должен сохраняться при переключении контекстов) и одну составную, по экземпляру у каждой задачи, для сохранения стека системного вызова.

phantom-84 писал(а):
chizh, переключаться на стек прерываний можно из основного стека ядра в обобщенном обработчике прерываний.

Вы меня поняли. Именно такой подход я и рассматривалю

phantom-84 писал(а):
Вообще основная проблема - это переполнение стека ядра и возникающие в контексте ядра прерывания, способные привести к его переполнению. Если обработчик прерывания будет выполнять работу, используя свой стек, то это уменьшит нагрузку на основной стек и тем самым снизит вероятность его переполнения. Лично для себя я не виду в этом ничего хорошего кроме возможности немного сэкономить память, но вот если говорить о возможности прерывания одного обработчика прерывания другим, то здесь есть о чем поразмыслить.
.
Это тоже является существенным достоинством такого подхода. Доказательство достаточности стека ядра становиться проще.

SII писал(а):
На самом деле при грамотном проектировании и реализации (и исправном оборудовании, есно) стек не переполнится, причём его объём может быть очень небольшим. Прерывания ведь не случаются абсолютно произвольно и ни с того ни с сего, а значит, можно посчитать, какой объём стека в каком случае нужен, и определить максимально потребный объём.

Давайте прикинем: 1) системный вызов -> прерывание IPI -> прерывание первого уровня -> прерывание второго уровня -> прерывание таймера
2) исключение -> double fault -> прерывание IPI -> прерывание первого уровня -> прерывание второго уровня -> прерывание таймера
При этом обработчики прерывания достаточно просты в расчете, а вот вариантов обработки системных вызовов и исключений значительно больше, поэтому максимально необходимый им размер стека просчитать сложнее.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 09 сен 2010, 00:30 

Зарегистрирован: 18 апр 2010, 15:59
Сообщения: 155
chizh писал(а):
Можно с приходом каждого прерывания проверять оставшееся место в стеке, и по необходимости увеличивать его. Отдельно или не отдельно от ядра - не важно, кому как удобней.

Этот подход довольно хорош, но он требует довольно сложной схемы управления памятью.

chizh писал(а):
Да, можно умножать размер стека на количество пришедших прерываний. Но думаю, что будет излишнее раздувание объёма, и много неиспользованной памяти.

Эту проблему тоже решает разделение стеков, но правда не так элегантно как динамическое изменение размера стека упоминаемое вами чуть выше.

To SII:
Спасибо за подробный ответ: скорее всего придется идти именно по вашему пути, с учетом проблем обозначенных мной в заголовке ветки форума. С одним исключением, у меня обработчик, в классическом понимании, является стандартным и полностью лежит в ядре. Его задача - конвертировать прерывание в сообщение для задачи-драйвера и при необходимости перепланирование задач.

Однако, я считаю большим достоинством архитектуры AMD64 то, что она позволяет вести раздельные стеки прерываний и системных вызовов. Это автоматически и наиболее элегантно решает поднятую проблему.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 09 сен 2010, 00:33 

Зарегистрирован: 18 апр 2010, 15:59
Сообщения: 155
Хотя меня все равно смущает нарушение реентерабельности. Надо будет еще обдумать эту проблему. Может все-таки запрещение прерываний для обеспечения атомарности смены стека будет и более удачным решением.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 09 сен 2010, 01:32 

Зарегистрирован: 28 окт 2007, 18:33
Сообщения: 1418
ZarathustrA писал(а):
Вы не учитываете, того факта, что РТ-системы должны передавать задаче управление сразу же по возникновению определенного события (прерывание, освободился ресурс и т.д.). В том числе это должно происходить и при выполнении кода ядра ОС. Поэтому например Линукс не является РТ-системой, именно потому что его ядро не является реентерабельным и любая РТ-задача будет ожидать завершения выполнения системого вызова неопределенной продолжительности теоретически стремящейся к бесконечности, что в свою очередь ставит крест на предсказуемости...


Вы ошибаетесь. Во-первых, система реального времени не должна "передавать задаче управление сразу же по возникновении определённого события" -- она должна это делать за _предсказуемое время_, а не мгновенно. Что это так, понятно из простого примера. Предположим, пришёл сигнал о том, что в управляемом Вашей системой ядерном реакторе возникли серьёзные проблемы с самим реактором, и его нужно срочно глушить. Естественно, управление получает задача, отвечающая за аварийный останов реактора. Вскоре после этого (останов ещё не выполнен) приходит другой сигнал: обнаружено нарушение работы вентилятора, охлаждающего лысину главного инженера станции. Проишествие, конечно, страшное, но реакция на него вполне может быть отложена до тех пор, пока до конца не отработает задача глушения реактора. Следовательно, управление передаётся не сразу, важно лишь, чтобы это время можно было предсказать и оно ни в какой ситуации не вышло за допустимые рамки.

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

Ну а что касается линуха, то проблема как раз в непредсказуемости, а не в реентерабельности ядра. Ядро может быть нереентерабельным, но с чётко предсказуемым временем реакции (конечно, на конкретном круге задач и с конкретным оборудованием -- но настоящие системы реального времени именно так и строятся, да и сертифицируется не сама ось, а весь программно-аппаратный комплекс). Та же упомянутая мной RSX-11 является системой жёсткого реального времени, однако её ядро нереентерабельно в том смысле, что нельзя начинать обработку следующего отложенного прерывания до окончания обработки предыдущего. Просто все действительно суперсрочные действия делались в драйверах, а остальное -- в задачах, время получения управления которыми было легко прогнозируемым. Правда, на практике как управляющую обычно использовали RT-11, не имеющую многозадачности вовсе или же поддерживающую 2 или 3 задачи в зависимости от конфигурации: она, понятное дело, была компактнее и работала быстрее, что для железа начала 1970-х было очень критичным, однако бывали и случаи, когда работала именно RSX-11 (у неё даже специальный управляющий вариант был -- RSX-11S).

Кстати говоря, у линуха как раз по одному стеку ядра на каждый поток пользовательского режима -- что её, однако, не делает ОСРВ, да к тому же приводит к дикому лишнему расходу памяти.

SII писал(а):
Давайте прикинем: 1) системный вызов -> прерывание IPI -> прерывание первого уровня -> прерывание второго уровня -> прерывание таймера
2) исключение -> double fault -> прерывание IPI -> прерывание первого уровня -> прерывание второго уровня -> прерывание таймера
При этом обработчики прерывания достаточно просты в расчете, а вот вариантов обработки системных вызовов и исключений значительно больше, поэтому максимально необходимый им размер стека просчитать сложнее.


Что расчёт не является _абсолютно элементарным_, я спорить не буду: так и есть. Но, если в ядро не напихано всё подряд, а само оно достаточно нормально спроектировано, то его сложность остаётся в разумных пределах, и просчитать максимально потребный объём с точностью до байта вполне реально.

ZarathustrA писал(а):
Однако, я считаю большим достоинством архитектуры AMD64 то, что она позволяет вести раздельные стеки прерываний и системных вызовов. Это автоматически и наиболее элегантно решает поднятую проблему.


Знаете, в архитектуре АРМ тоже имеет место разделение. И лично для меня оно оказалось крайне неудобным. Другое дело, что я делаю монолитное ядро, а в Вас вроде микроядро (не люблю я их: теоретические преимущества, конечно, есть, но я не считаю их столь важными, чтобы искупить недостатки в виде усложнения системы в целом и сильной потери производительности).


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 16 ]  На страницу Пред.  1, 2

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 2


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
Создано на основе phpBB® Forum Software © phpBB Group
Русская поддержка phpBB