Основным недостатком управления потоками в ядре является существенная цена системных запросов

, поэтому постоянные ошибки с потоками (создание, завершение и т.п.) приведут к увеличению накладных расходов.

5) Регистры. Назначение регистров. Типы регистров.

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

Для начала надо разобраться в том как работает компьютер на аппаратном уровне, а именно как происходят вычисления и адресация к памяти. Рассматривать мы будем только архитектуру x86, к сожалению, но изучение архитектур микропроцессоров не входит в рамки данного курса.

Итак, x86 совместимый процессор имеет:

· 8 регистров общего назначения EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP(это те регистры, которые использует программа для хранения данных),

· 6 регистров сегментовCS (указатель сегмента кода), SS (указатель сегмента стека), DS, ES, FS, GS (последние 4 - это указатели сегмента данных)

· регистр флаговEFLAGS,

· регистр указателя командEIP,

· регистры сопроцессора x87 и расширений MMX и XMM.

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

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

Регистры сегментов - это регистры, в которых содержатся указатели (адреса) различных сегментов. Архитектура x86 имеет сегментную организацию памяти. Это значит, что ваша программа разделена на сегменты. Поддерживаются 3 типа сегментов: сегмент кода (в котором содержатся инструкции программы), сегмент стека (в котором, как это не странно, содержится стек) и сегмент данных (есть еще дополнительные сегменты данных, про них можно почитать в интернетах).

Регистры состояния - регистры, которые постоянно содержат информацию о состоянии как процессора, так и самой программы.

Регистр указателя команд содержит в себе указатель на следущую команду, которая будет выполнена.

6) Стэк. Передача параметров через стэк.

Стек вообще (англ. stack — стопка) — структура данных, в которой доступ к элементам организован по принципу LIFO (англ. last in — first out, «последним пришёл — первым вышел»). Чаще всего принцип работы стека сравнивают со стопкой тарелок: чтобы взять вторую сверху, нужно снять верхнюю.....

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

Стек вызовов (от англ. call stack; применительно к процессорам — просто «стек») — в теории вычислительных систем, LIFO-стек, хранящий информацию для возврата управления из подпрограмм (процедур) в программу (или подпрограмму, при вложенных или рекурсивных вызовах) и/или для возврата в программу изобработчика

прерывания (в том числе при переключении задач в многозадачной среде).

При вызове подпрограммы или возникновении прерывания, в стек заносится адрес возврата — адрес в памяти следующей инструкции приостановленной программы и управление передается подпрограмме или подпрограмме-обработчику. При последующем вложенном или рекурсивном вызове, прерывании подпрограммы или обработчика прерывания, в стек заносится очередной адрес возврата и т. д.

При возврате из подпрограммы или обработчика прерывания, адрес возврата снимается со стека и управление передается на следующую инструкцию приостановленной (под-)программы.

7) Соглашение вызова.

Китаевское:

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

· cdecl (сокращение от c-declaration),

· pascal,

· stdcall/winapi,

· fastcall,

· safecall (характерно для COM),

· thiscall (C++)

Мы разберем только cdecl, т.к. все примеры у нас будут на языке С.

Соглашение cdecl делится на две чати: во-первых, это правила для вызывающей функции (caller), во-вторых, для вызываемой (subroutine).

Caller:

1. Сохранись! Необходимо сохранить свои EAX, EDX и ECX.

2. Если надо что-то передать в вызываемую ф-цию, то надо положить это что-то в стэк задом наперед.

3. Используйте инструкцию call для вызова. Данная инструкция положит адрес возврата в стэк (надо же знать куда возвращаться после выполнения) и вызовет то что нам надо.

После того, как вызываемая функция завершила свои дела, результат ее выполнения можно найти в регистре EAX (что мы и наблюдали). Затем надо удалить из стека параметры и восстановить сохраненные EAX, EDX и ECX.

Subroutine:

1. Выделить себе стэк-фрейм, чтобы не мешаться вызывающему.

2. Выделить себе памяти в стеке и там хранить локальные переменные.

Далее решаем какую-то задачу и...

1. результат сохраняем в EAX

2. убрать за собой (очистить стэк)

3. удалить свой фрейм (leave)

4. и выполнить ret

Из ВИКИ:

Соглашение вызова или модель вызова (англ. Calling convention) — часть двоичного интерфейса приложений, которая регламентирует технические особенности вызова подпрограммы, передачи параметров, возврата из подпрограммы и передачи результата вычислений в точку вызова.

Соглашение вызова определяет следующие особенности процесса использования подпрограмм:

· Расположение входных параметров подпрограммы и возвращаемых ею значений. Наиболее распространённые варианты:

· В регистрах.

· В стеке.

· В регистрах и стеке.

· Порядок передачи параметров. При использовании для параметров стека определяет, в каком порядке параметры должны быть помещены в стек, при использовании регистров — порядок сопоставления параметров и регистров. Варианты:

· прямой порядок — параметры размещаются в том же порядке, в котором они перечислены в описании подпрограммы. Преимущество — единообразие кода и записи на языке высокого уровня;

· обратный порядок — параметры передаются в порядке от конца к началу. Преимущество — при любом количестве параметров на вершине стека после адреса возврата оказывается сначала первый параметр, за ним второй и так далее. Это упрощает реализацию подпрограмм с неопределённым числом параметров произвольных типов.

· Кто возвращает указатель стека на исходную позицию:

· вызываемая подпрограмма — это сокращает объём команд, необходимых для вызова подпрограммы, поскольку команды восстановления указателя стека записываются только один раз, в конце подпрограммы;

· вызывающая программа — в этом случае вызов становится сложнее, но облегчается использование подпрограмм с переменным количеством и типом параметров.

· Какой командой вызывать подпрограмму и какой — возвращаться в основную программу. Например, в стандартном режиме x86 подпрограмму можно вызвать через call near, call far и pushf/call far (для возврата применяются соответственно retn, retf, iret).

· Содержимое каких регистров процессора подпрограмма обязана восстановить перед возвратом.

Соглашения вызова зависят от архитектуры целевой машины и компилятора.

8) Переполнение стэка

В программном обеспечении переполнение стека (англ. stack overflow) возникает, когда в стеке вызовов хранится больше информации, чем он может держать. Обычно ёмкость стека задаётся при старте программы/потока. Когда указатель стека выходит за границы, программа аварийно завершает работу.[1]

Основная причина переполнения стека — излишне глубокая или бесконечная рекурсия.

Вторая большая причина переполнения стека — одноразовое выделение огромного количества памяти крупными локальными переменными. Многие авторы рекомендуют выделять память, превышающую несколько килобайт, в «куче», а не на стеке.[5]

9) Сегментная организация памяти.

Перейдем теперь к рассмотрению способов организации памяти, которые базируются на использовании виртуальных адресов, аппаратно преобразуемых в физические при выполнении команд. Два основных вида организации виртуальной памяти –сегментная и страничная организация.

При сегментной организации вся виртуальная память, используемая программой, разбивается на части, называемые сегментами. Это разбиение выполняется либо самим программистом (если он программирует на языке ассемблера), либо компилятором используемого языка программирования. Размеры сегментов могут быть различными, но в пределах максимального размера, используемого в данной архитектуре. Разбиение обычно производится на логически осмысленные части, такие, как сегмент данных, сегмент кода, сегмент стека и т.п. Большая программа может содержать несколько сегментов одного типа, например, несколько сегментов кода или данных.

Таким образом, при сегментной организации у программы нет единого линейного адресного пространства. Виртуальный адрес состоит из двух частей: селектора сегмента и смещения от начала сегмента.

Селектор сегмента представляет некоторое число, которое обычно является индексом в таблице сегментов данного процесса. Такая таблица содержит для каждого сегмента его размер, режим доступа (только чтение или возможна запись), флаг присутствия сегмента в памяти. Если сегмент находится в памяти, то в таблице хранится его базовый адрес (адрес физической памяти, соответствующий началу сегмента). Отсутствие сегмента означает, что его данные временно вытеснены на диск и хранятся в файле подкачки (swap file).

В кодах программы селектор сегмента может либо явно присутствовать как часть адреса, либо подразумеваться в зависимости от смысла конкретного адреса. Например, для адресов выполняемых команд используется селектор текущего сегмента команд, а для адресов операндов – селектор текущего сегмента данных.

При каждом обращении к виртуальному адресу аппаратными средствами выполняется преобразование пары «сегмент : смещение» в физический адрес. Упрощенная схема такого преобразования показана на рис. 52.

Основным недостатком управления потоками в ядре является существенная цена системных запросов - student2.ru

Рис. 52

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

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

Но откуда возьмется свободное место в памяти? По всей вероятности, системе придется для этого убрать из памяти какой-то другой сегмент, принадлежащий либо к этому же, либо к иному процессу. Копия вытесняемого сегмента должна остаться в файле подкачки. Чтобы избежать лишней работы, в каждой записи таблицы хранится флаг, отмечающий, является ли сегмент в памяти «чистым» или «грязным», т.е. совпадает ли его содержимое с дисковой копией или же оно было изменено в памяти после последней загрузки с диска. «Грязный» сегмент должен быть сохранен на диске, для «чистого» сохранение не требуется. Если сегмент определен как доступный только для чтения, то он заведомо «чистый».

10) Контекст выполнения процесса. Переключение контекста.

Каждому процессу соответствует контекст, в котором он выполняется.

Этот контекст включает:

1. содержимое пользовательского адресного пространства - пользовательский контекст(т.е. содержимое сегментов программного кода, данных, стека, разделяемых сегментов и сегментов файлов, отображаемых в виртуальную память),

2. содержимое аппаратных регистров - регистровый контекст (таких, как регистр счетчика команд, регистр состояния процессора, регистр указателя стека и регистров общего назначения),

3. а также структуры данных ядра (контекст системного уровня), связанные с этим процессом.

Контекст процесса системного уровня в ОС UNIX состоит из "статической" и "динамических" частей. У каждого процесса имеется одна статическая часть контекста системного уровня и переменное число динамических частей.

Статическая часть контекста процесса системного уровня включает следующее:
A. Описатель процесса, т.е. элемент таблицы описателей существующих в системе процессов. Описатель процесса включает, в частности, следующую информацию:

1. ● состояние процесса;

2. ● физический адрес в основной или внешней памяти u-области процесса;

3. ● идентификаторы пользователя, от имени которого запущен процесс;

4. ● идентификатор процесса;

5. ● прочую информацию, связанную с управлением процессом.

B. U-область (u-area), индивидуальная для каждого процесса область пространства ядра, обладающая тем свойством, что хотя u-область каждого процесса располагается в отдельном месте физической памяти, u-области всех процессов имеют один и тот же виртуальный адрес в адресном пространстве ядра. Именно это означает, что какая бы программа ядра не выполнялась, она всегда выполняется как ядерная часть некоторого пользовательского процесса, и именно того процесса, u-область которого является "видимой" для ядра в данный момент времени. U-область процесса содержит:

1. ● указатель на описатель процесса;

2. ● идентификаторы пользователя;

3. ● счетчик времени, которое процесс реально выполнялся (т.е. занимал процессор) в режиме пользователя и режиме ядра;

4. ● параметры системного вызова;

5. ● результаты системного вызова;

6. ● таблица дескрипторов открытых файлов;

7. ● предельные размеры адресного пространства процесса;

8. ● предельные размеры файла, в который процесс может писать;

9. ● и т.д.


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

11)Таблица процессов.

Для реализации модели процесса операционная система содержит таблицу (массив структур),называемую таблицей процессов, с одним элементом для каждого процесса.(Эти элементы иногда называют блоками управления процессом.)

Элемент таблицы содержит информацию о =

1. состоянии процесса,

2. счетчике команд,

3. указателе стека,

4. распределении памяти,

5. состоянии открытых файлов,

6. об распределении и использовании ресурсов,

7. а также всю остальную информацию, которую необходимо сохранять при переключении в состояние готовности или блокировки для последующего запуска - как если бы процесс не останавливался.

Наиболее важные поля элемента(записи для конкретного процесса) таблицы процессов (разделённые на три логические группы):
1# Управление процессом=

· Регистры

· Счётчик команд

· Слово состояния программы

· Указатель стека

· Состояние процесса

· Приоритет

· Параметры планирования

· Идентификатор процесса

· Родительский процесс

· Группа процесса

· Сигналы

· Время начала процесса

· Использованное процессорное время

· Процессорное время дочернего процесса

· Время следующего аварийного сигнала


2# Управление памятью=

· Указатель на текстовый сегмент

· Указатель на сегмент данных

· Указатель на сегмент стека


3# Управление файлами=

· Корневой каталог

· Рабочий каталог

· Дескриптор файла

· Идентификатор пользователя

· Идентификатор группы

Выше представлены некоторые наиболее важные поля типичной системы. Поля в первой колонке относятся к управлению процессом. Остальные колонки описывают управление памятью и файлами.

Необходимо отметить, что от конкретной системы очень сильно зависит, какие именно поля будут в таблице процессов. Но таблица, представленная выше, дает общее представление о необходимой информации.

12) Виртуальное адресное пространство.

Наши рекомендации