КаталогИндекс раздела
НазадОглавлениеВперед


3. Администратор процессов

3.1. Введение

3.1.1. Функции Администратора процессов

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

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

3.1.2. Примитивы создания процесса

В системе QNX существует три примитива создания процесса:

    fork()
    exec()
    spawn()

Примитивы fork() и exec() определены стандартом POSIX, а примитив spawn() реализован только в QNX.

fork()

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

exec()

Примитив exec() заменяет образ порождающего процесса образом нового процесса. Возврата управления из нормально отработавшего exec() не существует, т.к. образ нового процесса накладывается на образ порождающего процесса. В системах стандарта POSIX новые процессы обычно создаются без возврата управления порождающему процессу - сначала вызывается fork(), а затем из порожденного процесса - exec().

spawn()

Примитив spawn() создает новый процесс по принципу "отец"-"сын". Это позволяет избежать использования примитивов fork() и exec(), что ускоряет обработку и является более эффективным средством создания новых процессов. В отличие от fork() и exec(), которые по определению создают процесс на том же узле, что и порождающий процесс, примитив spawn() может создавать процессы на любом узле сети.

3.1.3. Что наследует процесс

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

Что наследуется fork() exec() spawn()
Идентификатор процесса Нет Да Нет
Открытые файлы Да На выбор* На выбор
Блокировка файлов Нет Да Нет
Задержанные сигналы Нет Да Нет
Маска сигнала Да На выбор На выбор
Игнорируемые сигналы Да На выбор На выбор
Обработчик сигналов Да Нет Нет
Переменные среды Да На выбор На выбор
Идентификатор сеанса Да Да На выбор
Группа процесса Да Да На выбор
Реальные идентификаторы Да Да Да
Группы и пользователя ( UID, GID )
Эффективные UID, GID
Да На выбор На выбор
Текущий рабочий каталог Да На выбор На выбор
Маска создания файлов Да Да Да
Приоритет Да На выбор На выбор
Метод планирования Да На выбор На выбор
Виртуальные каналы Нет Нет Нет
Символические имена Нет Нет Нет
Таймеры реального времени Нет Нет Нет
Примечание.
* - вызывающий процесс может по необходимости выбрать - да или нет.

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

Каждый процесс проходит следующие четыре фазы:

  1. Создание;
  2. Загрузку;
  3. Выполнение;
  4. Завершение.

3.2.1. Создание

Создание процесса заключается в присвоении идентификатора процесса (ID) новому процессу и задании информации, определяющей программную среду нового процесса. Большая часть этой информации наследуется от "родителя" нового процесса (см. предыдущий параграф).

3.2.2. Загрузка

Загрузка образов процессов выполняется загрузчиком "по цепочке". Загрузчик входит в состав Администратора процессов и выполняется под идентификатором нового процесса. Это позволяет Администратору процессов выполнять другие запросы при загрузке программ.

3.2.3. Выполнение

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

3.2.4. Завершение

Процесс завершается одним из двух способов:

Завершение включает в себя две стадии:

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

Если породивший процесс не выдал wait() или waitpid(), то порожденный процесс становится так называемым "зомби"-процессом и не завершается до тех пор, пока породивший процесс не выдаст wait() или не завершится сам.

Для того, чтобы не ждать завершения порожденного процесса, следует либо установить признак _SPAWN_NOZOMBIE в функциях qnx_spawn() или qnx_spawn_option(), либо в функции signal() задать для SIGCHLD признак SIG_IGN. В этом случае порожденные процессы при завершении не становятся "зомби"-процессами.

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

Если процесс завершается по сигналу завершения и при этом выполняется утилита dumper, то формируется дамп образа памяти. Этот дамп можно просмотреть с помощью символьного отладчика. 3.3. Состояния процессов

Процесс всегда находится в одном из следующих состояний:

Более подробно состояния блокировок рассмотрены в разделе 2 "Микроядро".

На рис. 15 представлены возможные состояния процесса в системе QNX.

Возможные состояния процесса в системе QNX.
Рис.15

Определены следующие переходы из одного состояния в другое:

  1. Процесс посылает сообщение.
  2. Процесс-получатель принимает сообщение.
  3. Процесс-получатель отвечает на сообщение.
  4. Процесс ожидает сообщения.
  5. Процесс принимает сообщение.
  6. Сигнал разблокирует процесс.
  7. Сигнал пытаетcя разблокировать процесс; получатель запрашивает сообщение о захвате сигнала.
  8. Процесс-получатель принимает сигнал.
  9. Процесс ожидает завершения порожденного процесса.
  10. Порожденный процесс завершается, либо сигнал разблокирует процесс.
  11. Процессу выдан SIGSTOP.
  12. Процессу выдан SIGCONT.
  13. Процесс завершается.
  14. Порождающий процесс ожидает завершения, завершается сам или уже завершен.

3.3.1. Определение состояний процессов

Определить состояние конкретного процесса возможно:

Определить состояние операционной системы в целом возможно:

Утилита ps определена стандартом POSIX, следовательно командные файлы с ее использованием являются мобильными. Утилита sin уникальна в QNX, она предоставляет полезную информацию о системе QNX, которую нельзя получить с помощью утилиты ps.

3.4. Символические имена процессов

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

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

В QNX эта проблема решается путем предоставления возможности присваивать процессам символические имена. В случае одного узла процессы могут зарегистрировать это имя с помощью Администратора процессов на том узле, где они выполняются. Другие процессы могут затем получить у Администратора процессов идентификатор процесса, соответствующий этому имени.

В случае работы в сети проблема усложняется, т.к. сервер должен обслуживать клиентов, которые находятся на разных узлах сети. В QNX имеется возможность поддерживать работу как с глобальными, так и с локальными именами. Глобальные имена доступны во всей сети, а локальные - только на том узле, где они зарегистрированы. Глобальные имена начинаются со знака слэш (/). Например:
    qnx - локальное имя;
    company/xyz - локальное имя;
    /company/xyz - глобальное имя.

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

Для того, чтобы использовать глобальные имена хотя бы на одном из узлов сети, необходимо запустить "определитель имен процессов" (утилита nameloc). Этот процесс содержит записи всех зарегистрированных глобальных имен.

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

Для регистрации имени процесс-сервер использует функцию Си qnx_name_attach(). Для определения имени процесса процесс-клиент использует функцию Си qnx_name_locate().

3.5. Таймеры

3.5.1. Управление временем

В QNX управление временем основано на использовании системного таймера. Этот таймер содержит текущее координатное универсальное время (UTC) относительно 0 часов 0 минут 0 секунд 1 января 1970 г. Для установки местного времени функции управления временем используют переменную среды TZ (описана в Руководстве пользователя).

3.5.2. Простые средства таймирования

Программы интерпретатора Shell и процессы могут быть задержаны на заданное количество секунд с помощью простой утилиты таймирования. Программы интерпретатора используют для этого утилиту sleep; процессы - функцию Си sleep(). Можно также воспользоваться функцией delay(), в которой задается интервал времени в миллисекундах.

3.5.3. Более сложные средства таймирования

Процесс может также создавать таймеры, задавать им временной интервал и удалять таймеры. Эти более сложные средства таймирования соответствуют стандарту POSIX 1003.4/Draft 9.

Создание таймеров

Процесс может создать один или несколько таймеров. Таймеры могут быть любого поддерживаемого системой типа, а их количество ограничивается максимально допустимым количеством таймеров в системе (см. утилиту Proc в "Утилитах").

Для создания таймера используется функция Си mktimer(). Эта функция позволяет задавать следующие типы механизма ответа на события:

Установка таймеров

Вы можете задать таймеру следующие временные интервалы:

Можно также задать повторение таймера на заданном интервале. Например, вы установили таймер на 9 утра завтрашнего дня. Его можно установить так, чтобы он срабатывал затем каждые пять минут после истечения этого времени. Можно также установить новый временной интервал существующему таймеру. Результат этой операции зависит от типа заданного интервала:

Для установки Используйте
Абсолютного временного интервала Функцию abstimer()
Относительного временного интервала Функцию reltimer()

Удаление таймеров

Для удаления таймера воспользуйтесь функцией Си rmtimer(). Таймер может удалить сам себя по истечении временного интервала при условии:

Установка периода таймера

Период таймера задается утилитой ticksize или функцией Си qnx_timerperiod(). Вы можете выбрать период в интервале от 500 микросекунд до 50 миллисекунд.

Считывание таймера

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

3.6. Обработчики прерываний

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

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

Обработчик прерываний:

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

Если вы хотите Используйте
Установить аппаратное прерывание Функцию qnx_hint_attach()
Удалить аппаратное прерывание Функцию qnx_hint_detach()

3.6.1. Обработчики прерываний от таймера

Можно подключить обработчик прерываний напрямую к системному таймеру таким образом, чтобы обработчик запускался по каждому прерыванию от таймера. Для установки периода используйте утилиту ticksize.

Можно также подключиться к масштабируемому прерыванию от таймера, которое выдается каждые 100 миллисекунд в зависимости от значения ticksize. Эти таймеры являются альтернативой таймерам стандарта POSIX 1003.4 при обработке прерываний нижнего уровня.


НазадОглавлениеВперед
КаталогИндекс раздела