Управление процессами
Определение процесса. Основные понятия
Жизненный цикл процесса
Модельная ОС
Модель пакетной однопроцессной системы
Модель пакетной мультипроцессной системы
Модель ОС с разделением времени
Модель ОС с разделением времени (модификация)
Типы процессов
Типы процессов
Понятие «процесс»
Контекст процесса
Определение процесса Unix
Определение процесса в UNIX
Контекст процесса
Разделение сегмента кода
Контекст процесса
Контекст процесса
Второе определение процесса в UNIX
Создание нового процесса
Создание нового процесса
Создание нового процесса
Схема создания нового процесса
Пример
Семейство системных вызовов exec()
Семейство системных вызовов exec()
Семейство системных вызовов exec()
Пример
Использование схемы fork-exec
Завершение процесса
Завершение процесса
Жду «завершения» своего потомка
Получение информации о завершении своего потомка
Пример. Использование системного вызова wait()
Жизненный цикл процессов
Начальная загрузка
Инициализация Unix системы
Инициализация системы
Инициализация системы
Схема дальнейшей работы системы
Легковесные процессы (нити) Unix
Создание нити
Операции с идентификатором нити tid
Завершение нити
Завершение нити
Ожидание завершения нити
Пример №1 программирования нитей
Пример №1 программирования нитей
Пример №1 программирования нитей
Пример №2 программирования нитей
Пример №2 программирования нитей
Пример №2 программирования нитей
704.00K
Категория: ПрограммированиеПрограммирование

���������� ���������� 1v11

1. Управление процессами

2. Определение процесса. Основные понятия

Процесс — совокупность машинных команд и данных,
которая обрабатывается в рамках вычислительной
системы и обладает правами на владение некоторым
набором ресурсов.
Ресурсы могут принадлежать только одному процессу,
либо ресурсы могут разделяться между процессами —
разделяемые ресурсы.
Выделение ресурсов процессу
• предварительная декларация — до начала выполнения
• динамическое пополнение списка принадлежащих процессу
ресурсов
Количество допустимых процессов в системе — ресурс ВС.

3. Жизненный цикл процесса

Основной из задач ОС является поддержание жизненного
цикла процесса.
Типовые этапы обработки процесса в системе
Жизненный цикл процесса в системе:
• Образование (порождение) процесса
• Обработка (выполнение) процесса
• Ожидание (по тем или иным причинам) постановки на
выполнение
• Завершение процесса

4. Модельная ОС

1. Буфер ввода процессов (БВП) — пространство, в
котором размещаются и хранятся сформированные
процессы с момента их образования, до момента
начала выполнения.
2. Буфер обрабатываемых процессов (БОП) — буфер
для размещения процессов, находящихся в системе в
мультипрограммной обработке.

5. Модель пакетной однопроцессной системы

0
Ожидание
начала
обработки
1
2
Обработка ЦП
Завершение
0. Поступление процесса в очередь на начало обработки ЦП
(процесс попадает в БВП)
1. Начало обработки процесса на ЦП (из БВП в БОП)
2. Завершение выполнения процесса, освобождение
системных ресурсов.

6. Модель пакетной мультипроцессной системы

0
Ожидание
начала
обработки
1
Обработка ЦП
5
Завершение
БВП
2
Ожидания
операции в/в
4
3
БОП
Очередь
на выполнение

7. Модель ОС с разделением времени

0
Ожидание
начала
обработки
1
6
Обработка ЦП
Завершение
БВП
2
Ожидания
операции в/в
5
4
3
БОП
Очередь
на выполнение

8. Модель ОС с разделением времени (модификация)

0
Ожидание
начала
обработки
1
6
Обработка ЦП
Завершение
БВП
2
Ожидания
операции в/в
5
БОП
4
3
Очередь
на выполнение
свопинг
Диск

9. Типы процессов

• «полновесные» процессы
• «легковесные» процессы
«Полновесные
процессы»

процессы,
выполняющиеся
внутри
защищенных
участков
оперативной памяти.
«Легковесные процессы» (нити) —
работают в
мультипрограммном
режиме
одновременно
с
активировавшим их полновесным процессом и
используют его виртуальное адресное пространство.

10. Типы процессов

Однонитевая организация процесса
— «один процесс — одна нить»:
процесс
нить
Многонитевая организация процесса:
процесс
процесс
нить
нить
нить
нить
нить
нить
нить
нить

11. Понятие «процесс»

Понятие «процесс» включает в себя следующее:
• исполняемый код
• собственное адресное
пространство,
которое
представляет собой совокупность виртуальных
адресов, которые может использовать процесс
• ресурсы системы, которые назначены процессу ОС
• хотя бы одну выполняемую нить

12. Контекст процесса

Контекст
процесса — совокупность данных,
характеризующих актуальное состояние процесса.
• Пользовательская составляющая — текущее состояние
программы (совокупность машинных команд и данных,
размещенных в ОЗУ)
• Системно-аппаратная составляющая
– информация идентификационного характера (PID процесса,
PID «родителя»…)
– информация о содержимом регистров, настройках
аппаратных интерфейсов, режимах работы процессора,
настройках и использовании виртуальной памяти и т.п.
– информация, необходимая для управления процессом
(состояние процесса, приоритет).

13. Определение процесса Unix

Процесс
Объект,
зарегистрированный в
таблице процессов ОС
Объект,
порожденный системным
вызовом fork()

14. Определение процесса в UNIX

Процесс в UNIX — объект, зарегистрированный в
таблице процессов UNIX.
Идентификатор процесса (PID)
Таблица процессов
Пользовательская
составляющая
адресное пространство
процесса
Системная составляющая
Аппаратная составляющая
адресное пространство ядра,
ресурсы процессора
Контекст
процесса
PID

15. Контекст процесса

Контекст процесса
Пользовательская
составляющая (тело процесса)
Сегмент
кода
• Машинные
команды
• Неизменяемые
константы
Аппаратная
составляющая
Системная
составляющая
Сегмент
данных
Статические
данные
Разделяемая
память
Стек и
динамическая
память
•Статические переменные
•Стек:
– фактические параметры в
функциях
– автоматические переменные
•Динамическая память

16. Разделение сегмента кода

Процесс 1
Процесс 2
Сегмент
данных 1
Сегмент
данных 2
...
Процесс N
Сегмент
данных N
Сегмент кода программы

17. Контекст процесса

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

18. Контекст процесса

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

19. Второе определение процесса в UNIX

Процесс в UNIX — это объект, порожденный системным
вызовом fork().
Системный вызов — обращение процесса к ядру ОС за
выполнением тех или иных действий.

20. Создание нового процесса

#include <sys/types.h>
#include <unistd.h>
pid_t fork ( void );
• При удачном завершении возвращается:
сыновнему процессу значение 0
родительскому процессу PID порожденного
процесса
• При неудачном завершении возвращается –1, код
ошибки устанавливается в переменной errno
• Заносится новая запись в таблицу процессов
• Новый процесс получает уникальный идентификатор
• Создание контекста для нового (сыновьего) процесса

21. Создание нового процесса

Составляющие контекста, наследуемые при вызове
fork()
• Окружение
• Файлы, открытые в процессе-отце
• Способы обработки сигналов
• Разрешение переустановки эффективного
идентификатора пользователя
• Разделяемые ресурсы процесса-отца
• Текущий рабочий каталог и домашний каталоги
• …

22. Создание нового процесса

Составляющие контекста, не наследуемые при вызове
fork()
• Идентификатор процесса (PID)
• Идентификатор родительского процесса (PPID)
• Сигналы, ждущие доставки в родительский процесс
• Время посылки ожидающего сигнала,
установленное системным вызовом alarm()
• Блокировки файлов, установленные родительским
процессом

23. Схема создания нового процесса

PID = 2757
main(){
int i=0,pid;

if ( (pid=fork()) > 0 ){
i++;

} else { … }
}
PID = 2757
main(){
int i=0,pid;

if ( (pid=fork()) > 0 ){
i++;

} else { … }
}
Предок: выполняются
операторы в if-секции
fork()
PID = NNNN
main(){
int i=0,pid;

if ( (pid=fork()) > 0 ){
i++;

} else { … }
}
Потомок: выполняются
операторы в else-секции

24. Пример

/* PID 2757; PPID PPPP */
int main ( int argc, char **argv )
{
printf ( "PID = %d; PPID = %d \n", getpid(), getppid() );
fork ();
printf ( "PID = %d; PPID = %d \n", getpid(), getppid() );
return 0;
}
Возможные варианты вывода (PPPP – PID родительского
процесса; 2757; 1 или NNNN – PID сыновнего, «нового»
процесса):
PID =2757;PPID =PPPP
PID =2757;PPID =PPPP
PID =NNNN;PPID =2757
PID =2757;PPID =PPPP
PID =NNNN;PPID =2757
PID =2757;PPID =PPPP
PID =2757;PPID =PPPP
PID =2757;PPID =PPPP
PID =NNNN;PPID =1

25. Семейство системных вызовов exec()

#include <unistd.h>
int execl (const char *path, char *arg0, …, char *argn, NULL);
• path — имя файла, содержащего исполняемый код программы
• arg0 — имя файла, содержащего вызываемую на выполнение
программу
• arg1, …, argn — аргументы программы, передаваемые ей при
вызове
Последний параметр – признак завершения списка аргументов
(переменное число передаваемых параметров)
Возвращается:
в случае ошибки
–1

26. Семейство системных вызовов exec()

Пример запуска команды: ls -l
PID = 2760
PID = 2760
main()
{...
execl(“/bin/ls”, ”ls”,
”-l”, NULL);
...
}
exec()
int main(int argc,char** argv)
{
// реализация программы ls
}
//argc==2
//argv[0]=="ls“
//argv[1]==“-l"
//argv[2]==0

27. Семейство системных вызовов exec()

Сохраняются:
• Идентификатор процесса
• Идентификатор родительского процесса
• Таблица дескрипторов файлов (за исключением
специальным образом открытых)
• ……………..
Изменяются:
• Режимы обработки сигналов
• Эффективные идентификаторы

28. Пример

#include <unistd.h>
int main ( int argc, char **argv ) {

execl ( “/bin/ls”, ”ls”, ”-l”, NULL);
/* или execlp ( “ls” ,”ls”, ”-l”, NULL ); */
printf ( “это напечатается в случае неудачного обращения к
предыдущей функции, к примеру, если не был найден файл ls.\n”
);

}
Задание 1: самостоятельно изучить использование вариантов семейства
системных вызовов exec().
Задание 2: доработать пример таким образом, чтобы программа обеспечивала
запуск команды с 3-мя аргументами; информация о команде и её аргументах
передаётся через командную строку.
Задание 3: переработать пример таким образом, чтобы программа обеспечивала
запуск команды с произвольным числом аргументов, переданных через
командную строку.

29. Использование схемы fork-exec

PID = 2757
main()
{
if((pid=fork())== 0)
{
execl(“/bin/ls”, ”ls”, ” -l”,
NULL);
} else {…}
}
fork()
PID = 2757
main()
{
if((pid=fork())== 0)
{
execl(“/bin/ls”, ”ls”, ” -l”,
NULL);
} else {…}
}
PID = ХХХХ
int main(int argc,char*
argv[])
{
//реализация программы ls
}
exec()
PID = ХХХХ
main()
{
if((pid=fork())== 0)
{
execl(“/bin/ls”, ”ls”, ” -l”,
NULL);
} else {…}
}

30. Завершение процесса

• Системный вызов _exit()
• Выполнение оператора return, входящего в состав функции main() или
выход на закрывающую операторную скобку объемлющего блока функции
• Реакция на получение определённых сигналов
При завершении процесса (отдельно рассматривается случай трассировки)
он переходит в состояние «зомби»:
1. корректно освобождаются ресурсы (закрываются все открытые дескрипторы файлов,
освобождаются сегмент кода и сегмент данных процесса и пр.);
2. освобождается большая часть контекста процесса, однако сохраняется запись в
таблице процессов и та часть контекста, в которой хранится статус завершения
процесса и статистика его выполнения;
3. все сыновние процессы, чьи родители завершились, усыновляются процессом с
номером 1;
4. процессу-предку от данного завершаемого процесса передается сигнал SIGCHLD,
обычно он, по умолчанию, игнорируется.

31. Завершение процесса

#include <unistd.h>
void _exit ( int status ); //системный вызов
status :
= 0 при успешном завершении
# 0 при неудаче (возможно, номер варианта)
void exit (); //обращение к библиотечной
функции:
• разгрузка библиотечных кэш-таблиц;
• ……..
• обращение к _exit(int status )

32. Жду «завершения» своего потомка

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait ( int *status );
1. если к моменту обращения к wait какие-то сыновние процессы находятся в
состоянии зомби, то wait сразу вернёт PID одного из этих процессов и данный
процесс удаляется из системы;
2. если у процесса имеются только работающие сыновние процессы, то при
обращении к wait данный отцовский процесс будет блокирован до перехода
любого из своих сыновних процессов в состояние зомби;
3. если у процесса нет сыновних процессов (как работающих, так и зомби) то при
обращении к wait, будет получен код ответа -1;
4. если при выполнении wait в данный процесс придет сигнал, то будет получен код
ответа -1 (см. сигналы);
status по завершению содержит:
• в старшей части — код завершения процесса-потомка (пользовательский код
завершения процесса/номер сигнала)
• в младшей части — системный код завершения процесса-потомка, устанавливаемый
ядром UNIX (код завершения по _exit() или номер сигнала)

33. Получение информации о завершении своего потомка

После обращение к системному вызову wait():
• Приостановка родительского процесса до завершения
(остановки) какого-либо из потомков
• После передачи информации о статусе завершения
предку, все структуры, связанные с процессом-«зомби»
освобождаются, удаляется запись о нем из таблицы
процессов

34. Пример. Использование системного вызова wait()

#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 );
exit (1); /* ошибка */
}
}
Запуск программы:
Стандартный вывод:

35. Жизненный цикл процессов

планирование процессов
fork()
Создан
Готов к
выполнению
очередь готовых
процессов
внешнее событие
Выполняется
_exit()
Зомби
В режиме ядра
В пользовательском
режиме
Блокирован
ожидает внешнего
события
прерывание
wait()

36. Начальная загрузка

Начальная загрузка — загрузка ядра системы в
оперативную память, запуск ядра.
• Чтение нулевого блока системного устройства
аппаратным загрузчиком
• Поиск и считывание в память файла /unix
• Запуск на исполнение файла /unix

37. Инициализация Unix системы

• Начальная инициализация компонентов компьютера
(установка часов, инициализация контроллера памяти
и пр.)
• Инициализация системных структур данных
• Инициализация процесса с номером “0”:
• не имеет кодового сегмента
• существует в течении всего времени работы
системы
0
Контекст
процесса
Кодовый
сегмент

38. Инициализация системы

• Создание ядром первого процесса
• Копируется процесс “0” (запись таблицы
процессов)
• Создание области кода процесса “1”
• Копирование в область кода процесса “1”
программы, реализующей системный вызов
exec(), который необходим для выполнения
программы /etc/init
0
1
1
1
Контекст
процесса
Контекст
процесса
Контекст
процесса
Контекст
процесса
Кодовый
сегмент
Кодовый
сегмент
Кодовый
сегмент
exec()

39. Инициализация системы

• Замена команды процесса “1” кодом из файла /etс/init (запуск
exec() )
• Подключение интерпретатора команд к системной консоли
• Создание многопользовательской среды
“1” “init”
init
getty
Терминал 1
getty
Терминал 2

getty
Терминал N

40. Схема дальнейшей работы системы

fork()/
exec()
init
После окончания
работы shell
создает новый getty
getty
Печатает login: и
ожидает входа в
систему
Ввод
пароля
Окончание работы
shell
Выполняет
пользовательские
программы
login
Запрашивает
пароль и проверяет
его
Верный
пароль

41. Легковесные процессы (нити) Unix

1. Реализация аппарата управления нитями – использование библиотеки
функций в рамках полновесного процесса.
2. Нити, созданные в разных процессах друг друга «не видят» и, в общем
случае, не могут осуществлять взаимодействие.
3. Нити, созданные «внутри» процесса, разделяют глобальные переменные и
системные ресурсы, выделенные процессу. Стек и регистры (включая
счетчик команд) локализованы в рамках каждой нити.
4. Идентификация нити TID - значение предопределенного типа pthread_t. В
различных системах TID может представляться по разному:
– Linux 3.2.0 использует тип long int,
– Solaris 10 — unsigned int;
– FreeBSD 8.0 и Mac OS X 10.6.8 на структуру pthread.

42. Создание нити

#include <pthread.h>
int pthread_create(pthread_t *tid, const pthread_attr_t *attr,
void* (*start)(void *), void *arg)
tid –указатель на область памяти, в которой будет размещен идентификатор
созданной нити.
attr
– используется для задания свойств (или атрибутов) нити. Для простоты
будем передавать в этом аргументе пустой указатель (NULL).
start – имя функции (указатель на функцию) фиксированного формата,
реализующей нить - один входной параметр типа void* и возвращаемое
значение типа void*. Пример такой функции: void* my_thread(void* arg);
arg – используется для передачи значения единственного аргумента в функцию,
указатель на которую передается в параметре start.
Функция возвращает код ответа: 0 – в случае успешного создания и запуска
нити. И значение #0 – в случае возникновения ошибки (errno не используется).

43. Операции с идентификатором нити tid

Сравнение идентификаторов нитей
int pthread_equal(pthread_t tid1, pthread_t tid2)
возвращает #0, если идентификаторы равны и 0, в противном
случае.
Получение идентификатора нити
pthread_t pthread_self(void)
возвращает идентификатор вызывающей функцию нити.

44. Завершение нити

Завершение нити и всего процесса.
Завышение работы всего процесса произойдет в случае исполнения
в одной из нитей системного вызова exit. Также произойдет
завершение всего процесса, если в момент выполнения кода нити
придет сигнал, вызывающий завершение работы процесса.
Завершение отдельной нити
Завершение работы отдельной нити произойдет в следующих
случаях:
Нить может вызвать функцию pthread_exit
Принудительное завершение нити из другой нити в том же
процессе.
Нить может завершиться и вернуть управление в другую нить
Выход из функции прототипа нити – например, выполнение return
(возможно с параметром кода ответа).

45. Завершение нити

void pthread_exit(void *val_ptr) - завершение текущей нити
val_ptr – параметр «ответа», через него можно передать нити,
ожидающей завершения данной, информацию о завершении (указатель,
так как «код завершения» может быть составным типом, например
структурой).
Если завершается последняя (единственная) нить процесса – процесс
завершается.
int pthread_cancel(pthread_t tid) – завершение заданной нити
tid
- идентификатор принудительно завершаемой нити;
возвращает 0 в случае успеха, #0 - в случае неудачи.

46. Ожидание завершения нити

int pthread_join(pthread_t tid, void** val_ptr)
tid - идентификатор нити;
val_ptr – указатель на переменную, в которой будет размещен «код ответа»
завершенной нити (в частности, аргумент pthread_exit).
Возвращает 0 в случае успеха, #0 — в случае неудачи.
Обратившаяся к функции нить будет заблокирована до тех пор, пока нить с
идентификатором tid не завершится (выполнение pthread_exit, выход из
«вызывающей» функции или принудительное завершение). Только одна нить
может дожидаться завершения заданной нити.

47. Пример №1 программирования нитей

Основная программа:
1. Инициализирует общую разделяемую переменную, создает
несколько параллельно работающих нитей, порождая их в
цикле с передачей порядкового номера в качестве параметра в
функцию прототип нити.
2. Выводит на стандартное устройство вывода (на экран по
умолчанию) идентификатор каждой созданной нити в
шестнадцатеричном виде.
3. Каждая порождённая нить: выводит на стандартное
устройство вывода (на экран по умолчанию) полученный
порядковый номер, свой идентификатор, текущее значение
общей переменной, значение локальной переменной;
увеличивает значение переменных на 1.

48. Пример №1 программирования нитей

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define NUM_THREADS 3
/*общая переменная исп-я нитями */
int shared_counter = 0;
/*функция – прототип нити */
void* thr_fn(void *arg) {
/*локальная переменная*/
int local = 0;
/*"перемешивание" работы нитей*/
sleep(abs(5 - (int)arg));
/*вывод информации о нити, текущие
значения общей и локальной
переменных, инкремент переменных*/
printf("***Thread #%d with ID %#x:
shared value is %d, local value is
%d\n", (int)arg, pthread_self(),
shared_counter++, local++);
return NULL;
}
int main() {
pthread_t th_id;
int i, err;
for (i = 0; i<NUM_THREADS; i++) {
/*cоздаем нить, передаем порядковый
номер как аргумент по значению! */
err = pthread_create(&th_id, NULL,
thr_fn, (void *)i);
printf(«Сreated thread with ID %#x\n",
th_id);
}
/*пауза - возможность всем нитям доработать
и вывести информацию о себе*/
sleep(10);
return 0;
}

49. Пример №1 программирования нитей

Рекомендуемые задания:
1) Запустить программу, реализующую пример, на компьютере.
2) Провести и запустить на компьютере модификации
программы, реализующей пример, в части:
внесения контроля и обработки всех возможных ошибок;
сделать паузу для перемешивания выполнения нитей
случайной (используя функцию rand() );

50. Пример №2 программирования нитей

Основная программа:
1. создаёт нить, дожидается её окончания, печатает на экран
идентификатор нити и время в секундах, которое заняло
выполнение нити;
2. порождённая нить: засыпает на несколько секунд, после чего
завершается.

51. Пример №2 программирования нитей

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
/*функция – прототип нити */
void* thr_fn(void *arg) {
/*time(0) возвращает количество секунд с 1 января 1970 */
sleep(time(0) % 10); /*засыпаем на несколько секунд */
pthread_exit(NULL);
}
int main()
{
pthread_t th_id; void *pret;
/*создаём нить,параметр arg в прототипе нити не используется (NULL) */
pthread_create(&th_id, NULL, thr_fn, NULL);
time_t tm_start = time(0); /*запоминаем время создания нити */
pthread_join(th_id, &pret); /*ожидаем завершения созданной нити */
printf("Thread %#x execution time =
return 0;
}
%u\n", th_id, time(0)– tm_start);

52. Пример №2 программирования нитей

Рекомендуемые задания:
1) Запустить программу, реализующую пример, на
компьютере.
2) Провести и запустить на компьютере модификации
программы, реализующей пример, в части:
внесения контроля и обработки всех возможных
ошибок;
реализовать вариант с ненулевым кодом завершения
нити и распечатать информацию о коде завершения в
основной программе.
English     Русский Правила