Spawnl(modeflag,pathname,arg0,arg1...,argn,NULL).



Лабораторная работа 4

Изучение механизмов управления процессами QNX Neutrino.

Задание

1) Написание программы со следующими возможностями.

a) Получение информации о процессе;

b) Создание процесса;

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

 

2) Изучение и демонстрация возможностей утилит получения информации о текущем состоянии процессов.

Введение

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

· идентификатор процесса (Process ID, PID) уникальный номер, присваиваемый процессу при его порождении операционной системой;

·  идентификатор родительского процесса (Parent PID, PPID) PID процесса, породившею данный процесс, т. е. выполнившею запрос к операционной системе для создания данного процесса;

· реальные идентификаторы владельца и группы (User ID (UID) и Group ID(GID)). Это номера, позволяющие механизмам защити информации от несанкционированного доступа (НСД) определять, какому пользователю принадлежит процесс и к какой группе пользователей принадлежит этот пользователь. Эти идентификаторы присваиваются при регистрации пользователя в системе или командному интерпретатору (login shell), если выполнялась командно-стоковая регистрация через утилиту login, или графической оболочке Photon, если регистрация выполнялась в графическом режиме через утилиту phlogin. Процессы, запускаемые пользователем, наследуют LID и GID той программы, из которой они запускаются (т. с. родительского процесса):

· эффективные идентификаторы владельца и труппы (Effective UID,E UID и Effective GID, EGID)— предназначены для повышения гибкости механизмов защиты информации от НСД. Пользователь при наличии соответствующих полномочий может в ходе работы менять эффективные идентификаторы. При этом реальные идентификаторы не меняются. Механизмы, реализующие дискреционную защиту информации от НСД, для проверки прав доступа используют эффективные идентификаторы;

· текущий рабочий каталог путь (разделенный слэшами список каталогов), который будет автоматически добавляться к относительным именам файлов. Выводится на экран командой pwd;

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

· маска создания файлов (umask) - - атрибуты доступа, которые будут заданы язя файла, созданного процессом.

· значение приоритета:

· дисциплина диспетчеризации.

· использование ресурсов процессора (статистика по времени выполнения программы) — включает, время выполнения программы в прикладном контексте (user lime - время выполнения инструкций, написанных программистом), время выполнения в контексте ядра (system time— время выполнения инструкций ядра по запросу программы, т. е. системных вызовов), суммарное время выполнения всех дочерних процессов в прикладном контексте, суммарное время выполнения всех дочерних процессов в контексте ядра.

 

Выполнение

Получение информации о процессе

Для получения информации о процессе используем следующий код

 

#include<stdlib.h> /*подключение стандартной библиотеки */

#include <sys/resource.h> /*подключение файла с идентификаторами ресурсов*/

int main(int arge, char **argv, char **cnv)

{

printf("\n Process Informaition: \ n");

printf("Process name = \t \t %s \n", argv[0]);

printf("User ID = \t \t < %d > \n", getuid(0));

printf("Effective User ID = \t< %d >\n", geteuid(0));

printf("Group ID = \t \t < %d > \n", getgid(0));

printf("Effective Group ID = \t < %d > \n", getegid(0));

printf("Process Group ID = \t < %d > \n", getpgid(0));

printf("Process ID ( PID )= \t < %d > \n", getpid(0));

printf("Parent PI D ( PPI D ) = \t< %d > \n", getppid(0));

printf("Process priori t y = \t < %d > \n", getprio(0));

return EXIT_SUCCESS; /* возвращаем значение успешного завершения программы */

}

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

Функция printf() это функция форматированного вывода. Это означает, что в параметрах функции необходимо указать формат данных, которые будут выводиться. Формат данных указывается спецификаторами формата. Спецификатор формата начинается с символа % за которым следует код формата.

Спецификаторы формата:

символ
%d целое десятичное число
%i целое десятичное число
%e десятичное число в виде x.xx e+xx
%E десятичное число в виде x.xx E+xx
%f десятичное число с плавающей запятой xx.xxxx
%F десятичное число с плавающей запятой xx.xxxx
%g %f или %e, что короче
%G %F или %E, что короче
%o восьмеричное число
%s строка символов
%u беззнаковое десятичное число
%x шестнадцатеричное число
%X шестнадцатеричное число
%% символ %
%p указатель
%n указатель


Например, если команда :

printf("%d",20); выводит результат: 20

Кроме спецификаторов формата данных в управляющей строке могут находиться управляющие символы:

\f Новая страница, перевод страницы
\n Новая строка, перевод строки
\r Возврат каретки
\t Горизонтальная табуляция
\v Вертикальная табуляция
\" Двойная кавычка
\' Апостроф
\\ Обратная косая черта
\0 Нулевой символ, нулевой байт
\a Сигнал
\N Восьмеричная константа
\xN Шестнадцатеричная константа
\? Знак вопроса

Чаще всего используется символ \n. С помощью этого управляющего символа вы сможете переходить на новую строку.

Пример программы.

#include <stdio.h>

 

void main(void)

{

printf("Здравствуйте!\n"); // После печати будет переход на новую строку - \n

printf("Меня зовут Павел."); // Это будет напечатано на новой строке

}

Результат работы программы:
Здравствуйте!
Меня зовут Павел.

Для вывода данных о процессе используются функции: argv() getuid() geteuid() getgid() getegid() getpgid() getpid() getppid() getprio(),а в качестве аргумента передается 0 – идентификатор процесса самой программы.

Жизненный цикл процесса можно разделить на четыре этапа:

  • создание процесса
  • загрузка образа процесса;
  • выполнение процесса.
  • завершение процесса

Создание процесса

“Предком” всех процессов является процесс Администратор процессов (процесс procnto). идентификатор PID которого равен 1. Остальные процессы порождаются в результате вызова соответствующей функции другим процессом, именуемыми родительскими. Таких функций несколько:

  • семейство функций exec() отличаются набором аргументов функций, заменяют образ вызвавшего процесса указанным исполняемым файлом:
  • fork() создает дочерний процесс путем "клонирования" родительского процесса;
  • vfork() (“виртуальный fork()") - используется как "облегченная" альтернатива паре вызовов fork() exec(). В отличие oт стандартной функции fork(), она не выполняет реального копирования данных, а просто блокирует родительский процесс, пока дочерний не вызовет ехес()
  • семейство функций spawn*() - сразу порождает дочерний процесс, загрузив указанный исполняемый файл. Наиболее эффективный способ порождения процессов в QNX Neutrino;
  • system() - порождает shell для выполнения командной строки, указанной в качестве аргумента.

Рассмотрим пример создания процесса функцией fork(). В этом примере видно, что функция fork()  возвращает целое число, которое в родительском процессе равно нулю, а в дочернем идентификатору процесса:

#include<stdlib.h> /*подключение стандартной библиотеки */

#include <sys/resource.h> /*подключение файла с идентификаторами ресурсов*/

int main(int arge, char **argv, char **cnv)

{

pid_t pid; /* создаем переменную pid типа pid_t (process id) */

char *Prefix; /* создаем переменную Prefix типа char (символ) */

Prefix = (char *) malloc (sizeof(char)); /* Функция malloc захватывает блок памяти по крайней мере не

меньшей, чем из size байтов. Смысл это операции выделение памяти под переменную Prefix */

pid = fork(); /* функция fork() создает процесс и возвращает его ID */

 

if (pid==0) sprintf(Prefix, "CHILD:"); /* если мы в родительском процессе то возращаем ID потомка */

else sprintf ( Prefix, "PARENT:"); /* если мы в дочернем процессе то возращаем ID родителя */

 

printf("%s Process name = %s \n", Prefix, argv[0]);

printf("%s PID = %d \n", Prefix, getpid(0));

printf("%s PPID = %d \n", Prefix, getppid(0));

return EXIT_SUCCESS; /* возвращаем значение успешного завершения программы */

}

#include<stdlib.h> /*подключение стандартной библиотеки */

Теперь рассмотрим пример порождение нового процесса с помощью комбинации вызовов vfork() и exec()

#include <sys/resource.h> /*подключение файла с идентификаторами ресурсов*/

int main(int arge, char **argv, char **cnv)

{

pid_t pid; /* создаем переменную pid типа pid_t (process id) */

pid = vfork();

if (pid==0)

{

execlp("process","process",NULL);

perror("Child");

exit(EXIT_FAILURE);

}

waitpid(0,NULL,0);

printf("Parents's PID = %d \n", getpid(0));

printf("Parents's PPID = %d \n", getppid(0));

return EXIT_SUCCESS;

}

Функция waitpid() может приостановить процесс в ожидании изменения состояния определенного дочернего процесса, заданного значением PID. У функции waitpid() три параметра. Первый параметр – значение PID процесса, завершения которого ждет функция. Если передать в этом параметре значение -1, функция будет ждать изменения состояния любого процесса, аналогично wait(). Если первый параметр равен нулю, waitpid() ждет завершения любого процесса из той же группы, что и текущий. Второй параметр waitpid() аналогичен параметру wait(). Третий параметр позволяет указать дополнительные флаги функции. Например, если установить флаг NOHANG, функция вернет управление немедленно, даже если ни один дочерний процесс не завершился (подробнее об этом будет сказано ниже). Результатом функции waitpid(), также как и в случае wait(),является идентификатор завершившегося процесса.

Функция perror печатает сообщение об ошибке в stderr (стандартный поток вывода ошибок). В этом сообщении аргумент string печатается первым, затем печатается двоеточие, системное сообщение об ошибке для последнего библиотечного вызова, выработавшего ошибку, и новая строка. Номер действительной ошибки хранится в переменной errno, которая объявлена на внешнем уровне.

Самый простой и быстрый способ порождения процесса использование функцию spawn()

#include<stdlib.h> /*подключение стандартной библиотеки */

#include <sys/resource.h> /*подключение файла с идентификаторами ресурсов*/

int main(int arge, char **argv, char **cnv)

{

spawnl(P_WAIT,"process","process",NULL);

printf("Parants's PID= %d \n",getpid(0));

printf("Parants's PPID= %d \n",getppid(0));

return EXIT_SUCCESS;

}

spawnl(modeflag,pathname,arg0,arg1...,argn,NULL).

Функции spawn создают и выполняют новый child-процесс. Для загрузки и выполнения child-процесса должно быть доступно достаточно памяти. Аргумент modeflag определяет действия, выбираемые parent-процессом перед и на протяжении spawn. Следующие значения modeflag объявлены в <process.h>

 

Значение Его смысл
 P_WAIT Приостанавливает parent-процесс, пока не завершится child-процесс.
 P_NOWAIT     Продолжает выполнение parent-процесса, параллельного с child-процессом
 P_OVERLAY    Parent-процесс перекрывается с child- процессом; parent-процесс уничтожается (то же действие, что и при вызовах exec)

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

Завершить процесс можно, послав ему сигнал с помощью утилиты slay или kill в командной строке, или из программы с помощью таких функций, как kill(), sigqueue() и др.

Завершение процесса выполняется в две стадии

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

Процесс, если во время его работы не произошло никаких фатальных сбоев, обычно завершается путем вызова функции exit() или выполнением в функции main() оператора return. Число, передаваемое при этом в качестве аргумента функции exit() или с оператором return, называют кодам возврата программы. Если программа запускалась из командной строки в интерактивном режиме, то со код возврата можно посмотреть в служебной переменной окружения ?:

 echo $?

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

При порождении дочернею процесса с помощью функции spawn*() можно указать флаг Р_NOWAITO. В лом случае порожденный процесс никогда не сможет стать процессом-*зомби". т. к. Администратор процессов не будет хранить код возврата. Соответственно, нельзя будет использовать функции wait() для получения кода возврата. Существует функция aiexilf()позволяющая зарегистрировать для процесса функции-деструкторы, т.е. функции, вызываемые при нормальном завершении процесса непосредственно перед завершением. Таких деструкторов можно зарегистрировать 32 штуки, причем вызываться они будут в порядке LIFO ("Last In First Out", т. e. последняя зарегистрированная функция-деструктор будет вызвана первой).

 


Дата добавления: 2018-08-06; просмотров: 234; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!