Самодел 1 (1114716), страница 23
Текст из файла (страница 23)
Что происходит с процессом-потомком, если его предок вообще не обращался к wait() и/или завершился раньше потомка? Как уже говорилось, при завершении процесса отцом для всех его потомков становится процесс с идентификатором 1. Он и осуществляет системный вызов wait(), тем самым освобождая все структуры, связанные с потомками-зомби.
Часто используется сочетание функций fork()-wait(), если процесс-сын предназначен для выполнения некоторой программы, вызываемой посредством функции exec(). Фактически этим предоставляется процессу- родителю возможность контролировать окончание выполнения процессов-потомков.
Пример программы, последовательно запускающей программы, имена которых указаны при вызове.
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int i;
for (i=1; i<argc; i++)
{
int status;
if(fork()>0)
{
/*процесс-предок ожидает сообщения от процесса-потомка о завершении */
wait(&status);
printf(“process-father\n”);
continue;
}
execlp(argv[i], argv[i], 0);
return -1;
/*попадем сюда при неуспехе exec()*/
}
return 0;
}
Пусть существуют три исполняемых файла print1, print2, print3, каждый из которых только печатает текст first, second, third соответственно, а код вышеприведенного примера находится в исполняемом файле с именем file. Тогда результатом работы команды file print1 print2 print3 будет
first
process-father
second
process-father
third
process-father
Пример. Использование системного вызова wait().
В данном примере процесс-предок порождает два процесса, каждый из которых запускает команду echo. Далее процесс-предок ждет завершения своих потомков, после чего продолжает выполнение.
int main(int argc, char **argv)
{
if ((fork()) == 0) /*первый процесс-потомок*/
{ execl(“/bin/echo”,”echo”,”this is”,”string 1”,0);
exit(); }
if ((fork()) == 0) /*второй процесс-потомок*/
{ execl(“/bin/echo”,”echo”,”this is”,”string 2”,0);
exit(); }
/*процесс-предок*/
printf(“process-father is waiting for children\n”);
while(wait() != -1);
printf(“all children terminated\n”);
exit();
}
В данном случае wait() вызывается в цикле три раза –первые два ожидают завершения процессов-потомков, последний вызов вернет неуспех, ибо ждать более некого.
Жизненный цикл процессов
1. Процесс только что создан посредством вызова fork().
2. Процесс находится в очереди готовых на выполнение процессов.
3. Процесс выполняется в режиме задачи, т.е. когда реализуется алгоритм, заложенный в программу. Выход из этого состояния может произойти через системный вызов, прерывание или завершение процесса.
4. Процесс может выполняться в режиме ядра ОС, т.е. когда по требованию процесса через системный вызов выполняются определенные инструкции ядра ОС или произошло другое прерывание.
5. Процесс в ходе выполнения не имеет возможность получить требуемый ресурс и переходит в состояние блокирования.
6. Процесс осуществил вызов exit() или получил сигнал на завершение. Ядро освобождает ресурсы, связанные с процессом, кроме кода возврата и статистики выполнения. Далее процесс переходит в состоянии зомби, т.е. Не работает, а завершает работу,а затем уничтожается.
Формирование процессов 0 и 1
Выше упоминалось о нестандартном формировании некоторых процессов в Unix. Речь шла о процессе начальной загрузки системы и нестандартном формировании двух специфических процессов с PID 0 и 1.
Начальная загрузка
Рассмотрим подробнее, что происходит в момент начальной загрузки OC UNIX.
Начальная загрузка – это загрузка ядра системы в основную память и ее запуск.
Нулевой блок каждой файловой системы предназначен для записи короткой программы, выполняющей начальную загрузку.
Начальная загрузка выполняется в несколько этапов.
1.Аппаратный загрузчик читает нулевой блок системного устройства и передает точку входа.
2.После чтения этой программы она выполняется, т.е. ищется и считывается в память файл /unix, расположенный в корневом каталоге и который содержит код ядра системы. Осуществляется запуск яжра операционной системы.
3.Запускается на исполнение этот файл.
Инициализация системы
В самом начале ядром выполняются определенные действия по инициализации системы, а именно:
1)устанавливаются системные часы (для генерации прерываний),
2)формируется диспетчер памяти,
3)формируются значения некоторых структур данных (наборы буферов блоков, буфера индексных дескрипторов) и ряд других.
4)По окончании этих действий происходит инициализация процесса с номером "0".
По понятным причинам для этого невозможно использовать методы порождения процессов, изложенные выше, т.е. с использованием функций fork() и exec().
При инициализации этого процесса резервируется память под его контекст и формируется нулевая запись в таблице процессов.
Основными отличиями нулевого процесса являются следующие моменты
1.Данный процесс не имеет кодового сегмента – это просто структура данных, используемая ядром и процессом его называют потому, что он каталогизирован в таблице процессов.
2. Он существует в течении всего времени работы системы (чисто системный процесс) и считается, что он активен, когда работает ядро ОС.
Далее ядро копирует "0" процесс и создает "1" процесс.
Сначала процесс "1" представляет собой полную копию процесса "0" , т.е. у него нет области кода. Полее происходит увеличение его размера. Во вновь созданную кодовую область копируется программа, реализующая системный вызов exec() , необходимый для выполнения программы /etc/init.
На этом завершается подготовка первых двух процессов.
Первый из них представляет собой структуру данных, при помощи которой ядро организует мультипрограммный режим и управление процессами.
Второй – это уже подобие реального процесса.
Далее ОС переходит к выполнению программ диспетчера.
Диспетчер наделен обычными функциями и на первом этапе он запускает exec() , который заменит команды процесса "1" кодом, содержащимся в файле /etc/init. Получившийся процесс, называемый init, призван настраивать структуры процессов системы.
Далее он подключает интерпретатор команд к системной консоли. Так возникает однопользовательский режим, так как консоль регистрируется с корневыми привилегиями и доступ по каким-либо другим линиям связи невозможен.
На этом завершается подготовка первых двух процессов.
При выходе из однопользовательского режима init создает многопользовательскую среду.
С этой целью init организует процесс getty для каждого активного канала связи, т.е. каждого терминала. Это программа ожидает входа кого-либо по каналу связи.
init организует процесс getty для каждого активного канала связи, т.е. каждого терминала. Это программа ожидает входа кого-либо по каналу связи.
Далее, используя системный вызов exec(), getty передает управление программе login, проверяющей пароль.
Во время работы ОС процесс init ожидает завершения одного из порожденных им процессов, после чего он активизируется и создает новую программу getty для соответствующего терминала.
Таким образом процесс init поддерживает многопользовательскую структуру во время функционирования системы.
. Планирование
Основные задачи планирования
Важной проблемой, на решение которой ориентированы многие компоненты современных ОС является проблема планирования предоставления тех или иных услуг или функций операционной системой. Традиционно, в состав задач планирования ОС могут входить следующие:
-Планирование очереди процессов на начало обработки
-Планирование распределения времени ЦП между процессами
-Планирование свопинга («откачка»части процессов из ОП на диск)
-Планирование обработки прерываний (на какое прерывание можно сейчас не реагировать, на какое надо реагировать всегда)
-Планирование очереди запросов на обмен
В целом, комплексное решение задач планирования в ОС определяет основные эксплуатационные качества каждой конкретной системы.
Неправильное планирование приводит к деградации системы.
Планирование очереди процессов на начало обработки
На данном этапе определяется уровень многопроцессности системы.
Дисциплина обслуживания очереди :
- простейшая – FIFO (было на старых машинах. Сложилось исторически)
- по приоритету ()
- с учетом предполагаемого времени выполнения процесса, объема операций ввода/вывода и так далее.
(если задача большая и в\в не понадобится 3 часа, то можно запустить что-нибудь другое, чему в\в нужен)
В общем случае очередь процессов в БВП может предоставляться как объединение подочередей, где каждая подочередь включает в себя определенные классы процессов (например, такая классификация может строится на объеме запрашиваемых ресурсов и/или типе процесса). При этом возможно определение приоритета каждой из очередей (сначала рассматриваются непустые очереди с наименьшим приоритетом).
Планирование распределения времени ЦП между процессами
Квант времени – непрерывный период процессорного времени.
Приоритет процесса – числовое значение, показывающее степень привилегированности процесса при использовании ресурсов ВС (в частности, времени ЦП).
Для грамотного планирования надо решить две задачи:
– определить величину кванта
– определить стратегию обслуживания очереди готовых к выполнению процессов
Может существовать несколько очередей на обработку на ЦП. Первыми берутся процессы из первой очереди. Вторая очередь подпитывает первую, третья – вторую, и т.д.. Если первая очередь пуста берется из второй, вторая пуста – из третей и т. д. Планировщик определяет в какую очередь скинуть процесс.
Рассмотрим, как решается проблема с определения кванта времени.
Если величина кванта не ограничена – невытесняющая стратегия планирования времени ЦП (применяется в пакетных системах). Никто принудительно не скидывает процесс с ЦП. Разработчики берут на себя функции диспетчера. Например, программа что-то долго считает => сама периодически снимает себя с ЦП, что могли выполнится задачи требующие меньшее количество времени для выполнения и не могущие долго ждать.
Вытесняющая стратегия - величина кванта ограничена.
Может существовать несколько очередей на обработку на ЦП. Первыми берутся процессы из первой очереди. Вторая очередь подпитывает первую, третья – вторую, и т.д.. Если первая очередь пуста берется из второй, вторая пуста – из третей и т. д. Планировщик определяет в какую очередь скинуть процесс.
Рассмотрим, как решается проблема с определения кванта времени.
Кванты постоянной длины.
• Время ожидания кванта процессом ~ q(n-1)
•Параметры: длина очереди и величина кванта.
•Дисциплина обслуживания очереди, например, FIFO.
•Переключение процессов – операция, требующая времени.
Проблема: как определить длину кванта. Слишком маленький – не хватит времени на переключение, большой - некоторые успеют выполниться полностью.
Кванты переменной длины
Величина кванта может меняться со временем
• Вначале «большой» квант q=A,на следующем шаге q=A-t, q=A-2t,…, до q=B (B<A). Преимущество для коротких задач.
• Вначале q=B, далее q=B+t,…, до q=A. Уменьшение накладных расходов на переключение задач, когда несколько задач выполняют длительные вычисления.