Изменение образа памяти процесса

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

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

1. Гибкость: два процесса могут исполнять одну и ту же программу без ее дополнительной загрузки в память; в один процесс можно загружать разные программы.

2. Производительность: уменьшается время на создание нового процесса, т.к. требуется скопировать только таблицу страниц.

Синхронизация между родительским и дочерним процессами

Для синхронизации между дожительским и дочерним процессами предназначен системный вызов wait.

#include <sys/types.h>

#include <sys/wait.h>

pid_t wait(int *status)

Системный вызов возвращает wait PID первого найденного завершенного потомка; в переменную, адресуемую указателем status, записывает код завершения этого потомка; освобождает структуру task_struct этого потомка. Если есть завершенные дочерние процессы, то функция wait возвращает PID первого найденного завершенного потомка; в переменную, адресуемую указателем status, записывает код завершения этого потомка; освобождает структуру task_struct этого потомка. Если нет завершенных дочерних процессов, то wait блокирует вызывающий (родительский) процесс до завершения хотя бы одного потомка.

Существует и неблокирующий вариант – функция waitpid.

Ниже приведен фрагмент программы, в котором родительский процесс ожидает завершения всех его потомков:

int i, pid, status, w;

for (i=0; i<3; ++i) {; /* создание трех потомков */

pid=fork();

if(pid==0)

{ /* работа дочернего процесса */ exit(0);}

}

while ((w=wait(&status))&& w!=-1)

printf("потомок%d возвратил код возврата %d\n", w, status);

4.5. Состояние "зомби"

Если дочерний процесс завершается раньше, чем родитель вызовет wait, то дочерний процесс переходит в состояние "зомби". В этом состоянии он завершен, но остается в списке процессов; он виден по команде ps, но он не может быть снят командой kill, т.к. уже завершен. Его управляющий блок процесса остается неосвобожденным, пока родитель не вызовет wait, чтобы прочитать код завершения потомка.

Вопросы для самоконтроля

1. Каковы состояния процесса в ОС UNIX?

2. При помощи какого системного вызова создается процесс в ОС UNIX?

3. Какой системный вызов предназначен для завершения процесса в ОС UNIX?

4. Как можно изменить образ процесса в ОС UNIX?

5. Почему в ОС UNIX существуют раздельные системные вызовы для создания процесса и для изменения образа памяти процесса?

6. При помощи каких системных вызовов можно синхронизировать процессы в ОС UNIX?

7. Что такое состояния "зомби" для процесса в ОС UNIX?

Тема 5. Сигналы в ОС UNIX

Назначение сигналов

Функции сигналов:

- оповестить процесс о возникновении системных событий;

- механизм для коммуникации и синхронизации между прикладными процессами.

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

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

- при выборке процесса диспетчером на выполнение из очереди готовых;

- перед блокировкой и после блокировки.

- во время некоторых блокировок (например, ожидания ввода с клавиатуры).

Независимо от источника сигнала все сигналы доставляет процессу ядро ОС.

Обработка сигналов

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

- аварийное завершение с созданием дампа памяти (Dump);

- аварийное завершение без создания дампа памяти (Abort);

- игнорирование сигнала (Ignore);

- приостановка процесса (Stop);

- возобновление работы приостановленного процесса (Continue).

Процесс может переопределить действие по умолчанию для любого сигнала, кроме сигналов SIGKILL и SIGSTOP, на игнорирование сигнала либо определить свой обработчик сигнала. Процесс может в любое время указать новое действие либо сбросить установки на действие по умолчанию. Процесс может временно блокировать сигнал, и сигнал не будет обработан до тех пор, пока не будет разблокирован (функция sigprogmask).

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

1) Написать функцию обработчика сигнала, включив ее в ту программу, для которой она предназначена.

2) Зарегистрировать эту функцию, вызвав в начале программы функцию sigaction. Вызовы sigaction можно использовать многократно, задавая различные реакции на сигнал.

Ниже приведен пример обработки сигналов Ctrl+C. Действием по умолчанию для этого сигнала обычно является аварийное завершение программы.

include <signal.h>

. . . . . . . . . .

void handler(int sig);

{printf("Получен сигнал прерывания\n");

}

main()

{ struct sigaction act;

act.sa_handler=handler;

if(sigaction(SIGINT,&act,0)!=0){perror(…);exit(1);}

/* Продолжение работы */

}

В табл. 1.1 показаны 20 сигналов, определеные в стандарте POSIX.

Таблица 1.1

Сигналы, определенные в стандарте POSIX

Обозначение сигнала Действие по умолчанию Описание
SIGHUP Abort Зависание терминала / гибель управляющего процесса
SIGINT Dump Прерывание от клавиатуры (Ctrl+C)
SIGQUIT Dump Сигнал выхода из терминала (Ctrl+\)
SIGILL Dump Неверная команда
SIGTRAP Dump Ловушка трассировки/точки останова
SIGABRT Dump Сигнал Abort от клавиатуры
SIGBUS Dump Ошибка шины
SIGFPE Dump Исключение с плавающей точкой
SIGKILL Abort Сигнал Kill
SIGUSR1 Abort Сигнал №1, определенный пользователем
SIGSEGV Dump Неверный адрес памяти
SIGUSR2 Abort Сигнал №2, определенный пользователем
SIGPIPE Abort Запись в канал без его чтения
SIGALRM Abort Сигнал таймера от функции alarm
SIGTERM Abort Сигнал завершения
SIGSTKFLT Abort Ошибка стека сопроцессора
SIGCHLD Ignore Потомок остановлен или завершен
SIGCONT Continue Продолжен остановленный процесс
SIGSTOP Stop Процесс остановлен
SIGTSTP Stop Сигнал остановки терминала (Ctrl+Z)

Во время обработки сигнала другие сигналы того же типа блокируются. Как результат, обработчики сигналов могут не быть реентерабельными, т.к. несколько копий обработчика не могут выполняться одновременно. Однако обработчик сигнала одного типа может прервать работу обработчика сигнала другого типа.

Обычные сигналы ядро не запоминает в очереди. Если сигнал обрабатывается, то второй сигнал того же типа, поступивший в этот момент, теряется. В системах реального времени потеря сигналов может привести к катастрофе. Поэтому многие версии UNIX, включая Linux, поддерживают сигналы реального времени. Эти сигналы ядро помещает в очередь, и они не теряются. Гарантируется их доставка в порядке FIFO.

Посылка сигнала процессу

Послать сигнал процессу можно при помощи системного вызова kill.

#include <sys/types.h>

#include <signal.h>

int kill(pid_t pid,intsig);

Здесь pid – получатель сигнала, sig – номер сигнала. Если pid>0, то сигнал sig посылается процессу с PID=pid. Если pid=0, то сигнал посылается всем процессам в группе, к которой принадлежит текущий процесс. Если pid=-1, то сигнал посылается всем процессам в системе, кроме первого (init), начиная от наибольших PID в таблице процессов. Если pid<-1, то сигнал посылается всем процессам в группе процессов -pid.

Ниже приведе пример посылки сигнала SIGUSR1 родительскому процессу.

kill(getppid(),SIGUSR1);

Вопросы для самоконтроля

1. Каковы функции сигналов в ОС UNIX?

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

3. Как можно послать сигнал процессу в ОС UNIX?

Тема 6. Потоки

Основные понятия

Определение процесса, данное в разделе 2.1, базируется на двух независимых концепциях: группирование ресурсов и выполнение программ. Если их разделить, то появится понятие потока. Проще управлять ресурсами, объединив их в форме процесса. С другой стороны, процесс можно рассматривать как поток исполняемых команд, или просто поток.

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

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

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

Таблица 3.1

Сравнение процессов и потоков

Возможность Процесс Поток
Время для создания Большое Малое
Время для переключения Большое Малое
Механизм взаимодействия Сложный Простой
Разделяемые данные Нет Есть
Защита Есть Нет
Положение Тот же или разные компьютеры Тот же компьютер

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

Многопоточность – исполнение нескольких потоков в одном процессе.

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

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

Если программа выполняет много разных действий параллельно, и некоторые из них могут время от времени блокироваться, то использование нескольких потоков дает следующие преимущества:

- упрощается приложение;

- легкость совместного использования данных;

- легкость создания и уничтожения потоков;

- повышается производительность, если есть операции ввода-вывода;

- снижается нагрузка на подсистему управления памятью;

- удобство для многопроцессорных систем.

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