|
Программирование >> Структура ядра и системные вызовы
временем родительский процесс приостанавливает свое выполнение посредством вызова функции waitpid. После завершения порожденного процесса выполнение родительского возобновляется, и его переменным status и child pid присваивается код завершения порожденного процесса, а также его идентификатор. Родительский процесс использует макросы, определенные в <sys/wait.h>, для определения статуса выполнения порожаенного процесса следуюшим способом: Если WIFEXITED возврашает ненулевое значение, это означает, что порожденный процесс был завершен с использованием вызова exit. Родительский процесс извлекает его код завершения (который в этом примере равен 15) с помошью макрокоманды WEXITSTATUS. Затем он направляет полученное значение в стандартный поток ошибок. Если WIFEXITED возврашает нулевое значение, а WIFSTOPPED - ненулевое, то порожденный процесс был остановлен сигналом. Родительский процесс извлекает номер сигнала посредством макрокоманды WSTOPSIG и направляет это значение в стандартный поток ошибок. Если и WIFEXITED, и WIFSTOPPED возвращают нулевое, а WIFSIG-NALED - ненулевое значение, то порожденный процесс был завершен неперехваченным сигналом. Родительский процесс извлекает номер сигнала с помошью макрокоманды WTERMSIG и направляет это значение в стандартный поток ошибок. Если WIFEXITED, WIFSTOPPED и WIFSIGNALED возвращают нулевые значения, это означает, что либо родительский процесс не имеет порожденных, либо вызов waitpid был прерван сигналом. Поэтому родительский процесс вызывает функцию perror для вывода подробной диагностической информации о причинах неудачи. Результаты выполнения этой программы могут быть такими: % СС -о test waitpid test waitpid.C % test waitpid Parent process after fork Child process created 1354 exits: 15 8.2.4. Функция exec Системный вызов exec заставляет вызывающий процесс изменить свой контекст и выполнить другую программу. Есть шесть версий системного вызова exec. Все они выполняют одну и ту же функцию, но отличаются друг От друга аргументами. Вот Прототипы функций exec; #include <unistd.h> int exec/ (const char* path, const char* arg, ...); int exec/{o (const char* file, const char* arg, ...); int exec/e (const char* path, const char* arg, const char** env); int execv (const char* path, const char** argv, ...); int exectp (const char* file, const char** argv, ...); int execve (const char* path, const char** argv, const char** env); Первый аргумент функции является либо полным путевым именем, либо именем файла программы, подлежащей исполнению. Если вызов проходит успешно, данные и команды вызывающего процесса, хранящиеся в памяти, перекрываются данными и командами новой программы. Процесс начинает выполнение новой программы с ее первых строк. После завершения выполнения новой программы код завершения процесса передается обратно родительскому процессу. Следует обратить внимание, что в отличие от fork, создающей порожденный процесс, который выполняется независимо от родительского, exec лишь изменяет содержание вызывающего процесса для выполнения другой программы. Вызов exec может быть неудачным, если к программе, подлежащей выполнению, нет доступа или если она не имеет разрешения на вьшолнение. Далее, программа, указанная в первом аргументе вызова exec, должна быть исполняемым файлом (имеющим, например, формат a.out). В UNIX, однако, можно указать имя сценария shell для вызовов execlp и execvp, чтобы я ipo UNIX для интерпретации сценария shell активизировало Bourne-shell i/bin/sh). Так как в POSIX. 1 shell не определен, применение execlp или execvp для выполнения сценариев shell не допустимо. В действительности это не является проблемой, так как пользователи всегда могут активизировать --hell вызовом exec, указав при этом имя того сценария, который требуется выполнить. Суффикс р в вызовах execlp и execvp указывает, что если фактическое значение аргумента fde не начинается с / , то функции при поиске подлежащего выполнению файла будут использовать переменную среды PATH. Для всех остальных функций exec фактическим значением их первого аргумента должно быть путевое имя файла, подлежащего выполнению. Аргументы arg и argv являются аргументами также для программы, вызванной функцией exec. Они отображаются в переменную argv функции main новой программы. Для функций execl, execlp и execle аргумент arg Отображается в argvfOJ, значение, указанное после arg,- в argvflj и т.д. Список аргументов, имеющийся в вызове exec, должен завершиться значением NULL, чтобы указать функции, где необходимо прекратить поиск значений аргумента. Для функций execv, execvp и execve аргумент argv является массивом (вектором) указателей на символьные строки, где каждая строка представляет собой одно значение аргумента. Аргумент argv отображается непосредственно в переменную argv функции main новой программы. Так, символ / в имени функции exec указывает на то, что значения аргумента передаются в виде списка, а символ v в этом имени означает, что аргументы передаются в векторном формате. Следует заметить, что в каждом вызове exec необходимо указывать по крайней мере два значения аргументов. Первое значение (arg или argv[0]) - это имя программы, подлежащей выполнению; оно отображается в argvfOJ функции main новой программы. Второй обязательный аргумент - значение NULL, заверщающее список аргументов (для execl, execlp и execle) либо вектор аргументов (для execv, execvp и execve). Суффикс е функции exec {execle или execve) указывает, что последний аргумент {env) вызова функции является массивом (вектором) указателей на символьные строки. Здесь каждая строка определяет одну переменную среды и ее значение в формате Bourne-shell: <имя переменной среды>=<значение> Последний элемент env должен быть NULL, что сигнализирует об окончании списка. В среде, отличной от ANSI С, значение env будет присвоено третьему параметру функции main выполняемой программы. В среде ANSI С функция main может иметь только два аргумента {argc и argv), а env отображается в глобальную переменную environ выполняемой программы. При вызове в процессе функций execl, execlp и execvp глобальная переменная environ не изменяется (следует отметить, что значение переменной environ можно обновить, используя функцию putenv). Если вызов exec успещен, сегменты стека, данных и текста первоначального процесса заменяются новыми сегментами, относящимися к выполняемой программе. Однако таблица дескрипторов файлов процесса остается неизменной. Те из файловых дескрипторов, чьи флаги close-on-exec были установлены (системным вызовом fcntl), будут закрыты перед выполнением новой программы. Во время выполнения программы с помощью API exec возможны следующие изменения атрибутов процесса: Эффективный идентификатор пользователя: изменяется, если для выполняемой программы установлен бит смены идентификатора пользователя set-UID. Эффективный идентификатор группы: изменяется, если для выполняемой программы установлен бит смены идентификатора группы set-GID. Установленный идентификатор пользователя: изменяется, если для выполняемой программы установлен set-UID. Параметры обработки сигналов: для сигналов, определенных в процессе как перехватываемые, устанавливается режим обработки по умолчанию, когда процесс начинает выполнение новой программы. Определяемые пользователем функции-обработчики сигналов в выполняемой программе отсутствуют. Большинство программ вызывают exec в порожденном процессе, так как выполнение родительского процесса после вызова exec желательно продолжать. Однако вызовы fork и exec реализуются в качестве двух раздельных функций. Это происходит по следующим причинам: Реализовать/ог/: и exec раздельно проще. Программа может вызвать exec без fork, а fork без exec. Это обеспечивает гибкость в использовании этих функций. Многие программы будут производить определенные операции в порожденных процессах, например такие, как переназначение стандартного ввода-вывода в файлы перед вызовом exec. Это становится возможным благодаря разделению API fork и exec. Использование API exec демонстрируется на примере ТТрограммы test exec.C: finclude <iostream.hWib, о tinclude <stdio.h> #include <stdlib.h> ♦include <errno.h> tinclude <sys/types.h> tinclude <sys/wait.h> tincludS<unistd.h> эмуляция С-функции system int System( const char *cmd) { pid t pid; int status; switch (pid=fork()) i =; , .case -1: return -1; ;.caseO: execl ( /bin/sh , sh , -c , cmd, 0); perrot( execl ) ; exit(errno); Ьу.л; if (waitpid(pid,&status,0)==pid && WIFEXITED(status; return WEXITSTATUS(status); return -1; int main() { int char do - rc = 0; buf[256]; printf( sh> ); if (.gets (buf)) rc = System(buf! } while (!rc); return (rc); fflush,(stdout) break; приведенная выше программа является простой программой, эмулирующей shell UNIX. Она приглашает пользователя подавать команды shell на стандартный ввод и выполняет каждую команду с помощью функции System. Программа завершается, когда пользователь вводит либо признак конца файла ([Ctrl+D]) в командной строке shell , либо когда статус возврата вызова System не равен нулю. Эта программа отличается от shell UNIX тем, что не поддерживает команду cd и операции с переменными shell. Функция System эмулирует библиотечную С-функцию system. Вот прототип этой функции: int system (const char* cmd); Обе функции вызывают Bourne-shell {/bin/sh) для интерпретации и выполнения команды, указанной в аргументе cmd. Команда может представлять собой простую команду shell или состоять из серии команд, разделенных точкой с запятой или символами канала (). Кроме того, этими командами можно задавать переназначение ввода и (или) вывода. Функция System вызывает fork для создания порожденного процесса. Порожденный процесс, в свою очередь, вызывает execlp для выполнения программы Bourne-sheU с аргументами -с и cmd. Опция -с дает Bourne-shell указание интерпретировать и выполнять аргументы cmd так, как будто их ввели в командной строке shell. После выполнения cmd порожденный процесс завершается, а статус завершения Bourne-shell передается родительскому процессу, который вызывает функцию System. Следует отметить, что функция System вызывает waitpid специально с целью выявить момент завершения порождаемого процесса. Это важно, так как функция System может быть вызвана процессом, породившим другой процесс перед вызовом System; таким образом, функция System будет ожидать завершения только порожденного ею процесса, а не процессов, которые были созданы вызывающим процессом. Когда waitpid возвращает результат, функция System следит за тем, чтобы: возвращенный идентификатор процесса совпадал с идентификатором порожденного ею процесса; порожденный процесс завершался с использованием exit. Если оба условия соблюдаются, функция System возвращает статус завершения порожденного процесса. В противном случае она возвращает -1 (неудачное завершение). Библиотечная функция system сходна с функцией System, за исключением того, что первая из них производит обработку сигналов и присваивает переменной егто код ошибки, когда вызов waitpid завершается неудачно. Пробное выполнение этой программы может дать такие результаты: % СС -о test exec test exec.C % test exec , > sh> date; pwd Sat May 17 18:09:53 PST 1991.. /home/terry/sample . sh> echo Hello world 1 2 12 sh> D wc > foo; cat foo 8.2.5. Функция pipe Системный вызов pipe создает коммуникационный канал между двумя взаимосвязанными процессами (например, между родительским и порожденным процессами или между двумя процессами-братьями, порожденными одним родительским процессом). В частности, эта функция создает файл канала, который служит в качестве временного буфера и используется для того, чтобы вызывающий процесс мог записывать и считывать данные другого процесса. Файлу канала имя не присваивается, поэтому он называется неименованным каналом. Канал освобождается сразу после того, как все процессы закроют файловые дескрипторы, ссылающиеся на этот канал. #include <unistd.h> int pipe (int fifo[2]); Аргумент fifo является массивом, состоящим из двух целых чисел, присваиваемых ему интерфейсом pipe. В большинстве систем UNIX канал является однонаправленным, т.е. для чтения данных из канала процесс использует дескриптор файла fifofOJ, а для записи данных в канал - другой дескриптор файла, fifoflj. Однако в UNIX System V.4 канал является двунаправленным, поэтому для записи и чтения данных через канал могут использоваться оба дескриптора -fifo[0]Kfifo[l]. Стандарт POSIX. 1 поддерживает как традиционные модели каналов ОС UNIX, так и каналы System V.4, не указывая точное назначение дескрипторов канапов. Приложения, которые желательно переносить на все системы UNIX и POSIX, должны использовать канады так, как будто они однонаправленные. К хранящимся в канале данным доступ производится последовательно, по алгоритму первым вошел - первым вышел . Процесс не может использовать функцию Iseek для произвольного доступа к данным в канале. Данные удаляются из канала сразу же после их считывания. Общепринятый метод создания коммуникационного канала между процессами с помощью функции pipe: Родительский и порожденный процессы: родительский процесс вызывает pipe для создания канала, а затем с помощью fork порождает процесс-потомок. Так как порожденный процесс имеет копию дескрипторов файлов родительского, два процесса могут общаться между собой по каналу через свои дескрипторы fifo[0] и fifo[l].
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |