Назад |
Как упоминалось выше, UNIX является многозадачной ОС, т.е. в ней одновременно могут работать несколько программ-процессов. Ядро системы ответственно за их запуск и последующее сопровождение. Поскольку процессор в ЭВМ, как правило, один, то ядро же обеспечивает выделение каждой программе интервалов машинного времени. Ядро осуществляет корректное переключение между задачами.
Выполнение программы начинается с вызова системой функции main( ). Полный формат ее следующий:
int main (int argc, char *argv[], char *evnir[]);
Здесь argv - массив указателей на строки, содержащие параметры, переданные задаче при запуске, argc - число элементов массива argv, envir - массив строк, содержащих переменные среды (environment) и их значения.
Завершение процесса происходит при возврате из функции main( ) или вызове программой функции exit( ).
Во время выполнения задача может запустить другой процесс. Для этого в системе UNIX предусмотрена системная функция fork( ):
#include <sys/types.h> #include <unistd.h> pid_t fork (void);
После выполнения процедуры, ядром UNIX создается точная копия процесса, выполнившего системный вызов. При этом, вызывающий процесс называется родительским, а новый - процессом-"потомком". Родительскому процессу функция fork( ) возвращает идентификатор порожденного процесса, а "потомку" возвращается 0. Важным свойством вызова fork( ) является то, что оба процесса продолжают иметь доступ ко всем открытым файлам "родителя".
После того, как новая задача запущена, она может выполнять код "родителя", но, как правило, подгружается и начинает работать программа, находящаяся в другом выполнимом файле. Чтобы осуществить это, можно использовать системный вызов exec( ), exec1( ) или другой аналогичный. Перечисленные функции отличаются друг от друга способом передачи параметров вызываемой программе. Функция exec( ) имеет прототип:
#include <unistd.h> int exec ( const char *path, int argc, char *argv[ ] );
Здесь path - имя выполняемого файла, argv - массив указателей на строки, передаваемые загружаемой программе в качестве параметров, argc - количество строк массива argv. Прототипы и объяснение других процедур, подобных exec( ), можно найти в соответствующих разделах подсказки UNIX (см. подробнее 1.9.1.).
Процесс-"родитель", когда заканчивает работу, может подождать завершения запущенных им ранее программ. Для этого следует обратиться к системной процедуре wait( ):
#include <sys/types.h> #include <sys/wait.h> pid_t wait (int *status);
Функция возвращает идентификатор процесса, окончившего работу. Код его завершения записывается по адресу, заданному аргументом status.
Заметим, что в различных диалектах UNIX имеются более развитые варианты функции wait( ). Это wait3( ), waitpid( ), waitid( ) (более подробную информацию можно найти в документации по той системе, на которой работает пользователь).
В качестве примера рассмотрим подпрограмму, реализующую функцию system( ). Она выполняет переданную ей командную строку при помощи интерпретатора командного языка (командного процессора) sh.
#include <sys/types.h> #include <sys/wait.h> #include <unistd.h> int system (const char *cmd_string) { pid_t pid; int status; if (cmd_string = = NULL) return (1); if ( (pid = fork ( ) ) < 0) return (-1); if (pid == 0) { /* процесс-"потомок" */ execl ("/bin/sh", "sh", "-c", cmd_string, (char*) 0); exit (-1); /* выполняется при ошибке в execl */ } else { /* процесс-"родитель" ожидает завершения */ /* выполнения процесса-"потомка" */ wait (&status); } }