Приветствую.
Сразу предупрежу, что пост полон личных соображений автора, которые могут быть ошибочными, да и тема эта обширна и интересна, так что обсуждение по теме в комментариях приветствуется.
Только сейчас осознал, что во-первых, не рассказал я многого, что полезно было бы знать перед тем, как писать код. К тому же есть намного более хорошие пути для реализации многозадачности, чем я упоминал в прошлом выпуске.
Также постараюсь последовать многочисленным советам и систематизировать содержание статей.
Несмотря на то, что этот выпуск – беллетристика чистейшей воды, составим ка план.
1) Разные мысли о реализации многозадачности.
2) За и против за каждый способ.
3) Решим как делать свой велосипед.
Приступим.
1) Когда я только начал размышлять как же сабж лучше реализовать, закралась ко мне сакральная, прямолинейная мысль. Вот подумаем про область, где процессы пользователя исполняются (с ring0 всё проще). Давайте выделим им место, начиная с 16 мегабайт. Остальное – их область, до самого конца оперативки. Теперь возникает вопрос: как же нам контролировать и обеспечивать процесс всем необходимым. Сакральная мысль заключалась в следующем, вы её уже слышали: делим RAM на N кусков (не обязательно равных), потом организуем очереди из задач и к ним, будем делать замеры по 'весу' программ и.т.д. Вы, наверное, хотите уже сказать: “Автор, может, издашь боевой клич и понесешься грабить деревни? По-варварски всё это.”. Согласен. Такая модель нежизнеспособна, нерасширяема и ужасно неэффективна. Об этом писал всем известный товарищ Таненбаум в своём замечательном труде. При такой реализации многозадачности возникают следующие проблемы: маленьким программам будет уделяться меньше времени, возможно голодание. Из-за того, что размеры у всех программ разные, а рамки статичны, будет возникать неравноправная конкуренция. В принципе, это можно решить строгим контролем, но уж больно много этот метод со статичными разделами приносит головной боли.
2) Потом, немного подумав, пришла на ум и другая мысль: виртуальная память.
Давайте лучше вспомним про страничную организацию памяти (благослови её Ктулху).
Если мы не используем статичные области для задач, то возникает ряд других проблем:
1) Как же следить за растущими и процессами?
2) Не позатирают ли процессы друг друга?
3) Нужен ли в физической памяти кусок непрерывного пространства для процесса?
Давайте с ними разберёмся.
1) Для того, чтобы не упустить процессы достаточно реализовать диспетчер (менеджер) памяти, который и будет ‘раздавать пайки’ в виде свободных страниц. К тому же каждому процессу мы даруем свои собственные дескрипторы, которые и поотрубят длинные ручонки процесса.
2) Вот это ещё одна обязанность диспетчера – тщательно следить за тем, чтобы память не использовалась совместно N процессами.
3) Честно, сначала я сам заблуждался, ГДЕ же исполняется код в физическом или виртуальном пространствах (знаю, странно звучит). Ответ – в виртуальном пространстве. Просто представьте миры как в Матрице: оба связаны, и оба реальны. Так что не надо воспринимать виртуальное пространство как ничего не значащее и просто расширяющее максимальный объём адресуемой оперативки.
Для того, чтобы лучше понять, рассмотрим пример. Вот, допустим, есть некий процесс, частицы которого разбросаны по всей физической памяти. Но мы же можем назначить страницам с определёнными виртуальными адресами физические адреса, правде же? Т.е виртуальный адрес опять отправится в путешествие по каталогам и страницам, а в конце будет вполне себе нужный и валидный адрес в физической памяти. Получается, что для процесса виртуальные адреса будут последовательными, но на самом деле его код может где угодно валяться в физической памяти.
Способ, описанный выше, используется сейчас во многих операционках, так он довольно стабильный, гибкий. Сложность будет только в грамотном написании качественного кода.
Вот теперь незаметно подошли к теме менеджера памяти. Вообще то считается, что это самая сложная часть в проекте. Даже слышал такую шутку:
“Почему osdev проекты глохнут?
1) Сильно занят на работе;
2) Женился;
3) Попробовал написать менеджер памяти; ”
Но не будем пугаться. Постараемся справиться. Он должен будет ‘справедливо править’, чтобы в оперативке царила идиллия и ‘золотой век’:
1) Следить за страницами: вовремя их освобождать, ремапить.
2) Подыскивать новые свободные страницы по запросу (#PF, чаще всего).
Для этого предусмотрено несколько алгоритмов, которые также описаны у товарища Таненбаума. Рассмотрим их позже.
3) Если стало недостаточно физической памяти, то использовать своп. При таком раскладе нужно будет найти менее используемые страницы, загонять их в своп. Потом обратно. Короче говоря, придётся менеджеру изрядно попотеть в таком случае.
Теперь давайте опишем на словах, как же будет происходить создание новой задачи (учитываем, что это пока ещё не боеспособная версия):
1) Проверить, есть ли вообще место ещё для одной задачи.
2) Загрузить образ программы в память по определённому адресу.
3) Описать для задачи дескрипторы кода, стека, данных.
4) Было бы красиво, если бы создали свой каталог страниц для задачи со всеми вытекающими.
5) Передать управление коду программы.
6) Если возникает #PF, то смотрим, какая задача выполнялась, при обращении к какому адресу она покрашилась, взываем с мольбами к менеджеру памяти и, если посчастливится, получаем ещё одну страничку памяти, и возвращаемся обратно к коду.
В следующем выпуске рассмотрим, как же писать пресловутый менеджер памяти, ведь без него никуда не денешься.
Что почитать:
1) Эндрю Таненбаум: «Современные операционные системы». (Очень полезная книга).
2) Intel System Programming Manuals (без них никуда).
Сразу предупрежу, что пост полон личных соображений автора, которые могут быть ошибочными, да и тема эта обширна и интересна, так что обсуждение по теме в комментариях приветствуется.
Только сейчас осознал, что во-первых, не рассказал я многого, что полезно было бы знать перед тем, как писать код. К тому же есть намного более хорошие пути для реализации многозадачности, чем я упоминал в прошлом выпуске.
Также постараюсь последовать многочисленным советам и систематизировать содержание статей.
Несмотря на то, что этот выпуск – беллетристика чистейшей воды, составим ка план.
1) Разные мысли о реализации многозадачности.
2) За и против за каждый способ.
3) Решим как делать свой велосипед.
Приступим.
1) Когда я только начал размышлять как же сабж лучше реализовать, закралась ко мне сакральная, прямолинейная мысль. Вот подумаем про область, где процессы пользователя исполняются (с ring0 всё проще). Давайте выделим им место, начиная с 16 мегабайт. Остальное – их область, до самого конца оперативки. Теперь возникает вопрос: как же нам контролировать и обеспечивать процесс всем необходимым. Сакральная мысль заключалась в следующем, вы её уже слышали: делим RAM на N кусков (не обязательно равных), потом организуем очереди из задач и к ним, будем делать замеры по 'весу' программ и.т.д. Вы, наверное, хотите уже сказать: “Автор, может, издашь боевой клич и понесешься грабить деревни? По-варварски всё это.”. Согласен. Такая модель нежизнеспособна, нерасширяема и ужасно неэффективна. Об этом писал всем известный товарищ Таненбаум в своём замечательном труде. При такой реализации многозадачности возникают следующие проблемы: маленьким программам будет уделяться меньше времени, возможно голодание. Из-за того, что размеры у всех программ разные, а рамки статичны, будет возникать неравноправная конкуренция. В принципе, это можно решить строгим контролем, но уж больно много этот метод со статичными разделами приносит головной боли.
2) Потом, немного подумав, пришла на ум и другая мысль: виртуальная память.
Давайте лучше вспомним про страничную организацию памяти (благослови её Ктулху).
Если мы не используем статичные области для задач, то возникает ряд других проблем:
1) Как же следить за растущими и процессами?
2) Не позатирают ли процессы друг друга?
3) Нужен ли в физической памяти кусок непрерывного пространства для процесса?
Давайте с ними разберёмся.
1) Для того, чтобы не упустить процессы достаточно реализовать диспетчер (менеджер) памяти, который и будет ‘раздавать пайки’ в виде свободных страниц. К тому же каждому процессу мы даруем свои собственные дескрипторы, которые и поотрубят длинные ручонки процесса.
2) Вот это ещё одна обязанность диспетчера – тщательно следить за тем, чтобы память не использовалась совместно N процессами.
3) Честно, сначала я сам заблуждался, ГДЕ же исполняется код в физическом или виртуальном пространствах (знаю, странно звучит). Ответ – в виртуальном пространстве. Просто представьте миры как в Матрице: оба связаны, и оба реальны. Так что не надо воспринимать виртуальное пространство как ничего не значащее и просто расширяющее максимальный объём адресуемой оперативки.
Для того, чтобы лучше понять, рассмотрим пример. Вот, допустим, есть некий процесс, частицы которого разбросаны по всей физической памяти. Но мы же можем назначить страницам с определёнными виртуальными адресами физические адреса, правде же? Т.е виртуальный адрес опять отправится в путешествие по каталогам и страницам, а в конце будет вполне себе нужный и валидный адрес в физической памяти. Получается, что для процесса виртуальные адреса будут последовательными, но на самом деле его код может где угодно валяться в физической памяти.
Способ, описанный выше, используется сейчас во многих операционках, так он довольно стабильный, гибкий. Сложность будет только в грамотном написании качественного кода.
Вот теперь незаметно подошли к теме менеджера памяти. Вообще то считается, что это самая сложная часть в проекте. Даже слышал такую шутку:
“Почему osdev проекты глохнут?
1) Сильно занят на работе;
2) Женился;
3) Попробовал написать менеджер памяти; ”
Но не будем пугаться. Постараемся справиться. Он должен будет ‘справедливо править’, чтобы в оперативке царила идиллия и ‘золотой век’:
1) Следить за страницами: вовремя их освобождать, ремапить.
2) Подыскивать новые свободные страницы по запросу (#PF, чаще всего).
Для этого предусмотрено несколько алгоритмов, которые также описаны у товарища Таненбаума. Рассмотрим их позже.
3) Если стало недостаточно физической памяти, то использовать своп. При таком раскладе нужно будет найти менее используемые страницы, загонять их в своп. Потом обратно. Короче говоря, придётся менеджеру изрядно попотеть в таком случае.
Теперь давайте опишем на словах, как же будет происходить создание новой задачи (учитываем, что это пока ещё не боеспособная версия):
1) Проверить, есть ли вообще место ещё для одной задачи.
2) Загрузить образ программы в память по определённому адресу.
3) Описать для задачи дескрипторы кода, стека, данных.
4) Было бы красиво, если бы создали свой каталог страниц для задачи со всеми вытекающими.
5) Передать управление коду программы.
6) Если возникает #PF, то смотрим, какая задача выполнялась, при обращении к какому адресу она покрашилась, взываем с мольбами к менеджеру памяти и, если посчастливится, получаем ещё одну страничку памяти, и возвращаемся обратно к коду.
В следующем выпуске рассмотрим, как же писать пресловутый менеджер памяти, ведь без него никуда не денешься.
Что почитать:
1) Эндрю Таненбаум: «Современные операционные системы». (Очень полезная книга).
2) Intel System Programming Manuals (без них никуда).