Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Конспект 58 страниц 2002.doc
Скачиваний:
91
Добавлен:
15.06.2014
Размер:
4.07 Mб
Скачать

Системный вызов завершения процесса exit

#include <unistd.h>

void _exit(int exit_code);

int atexit(void(*func)(void));

- - - - -

void exit(int status)

Системный вызов _exit завершает процесс, он вызывает освобождение сегмента данных процесса, сегмента стека, распределения областей и закрытия всех открытых дескрипторов файловю

Но запись в таблице процессов, в которой был зарегистрирован этот процесс, не удаляется, чтобы там был зарегистрирован статус завершения процесса и информация о его выполнении. Такой остаток процесса называется процессом - “зомби”. Его выполнение уже не возможно, но родительский процесс может выбрать данные, хранящиеся в таблице процессов, путём использования системного вызова wait(). Эти системные вызовы wait() освобождают запись в таблице процессов, соотнесённую с порождённым процессом.

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

Целочисленный аргумент _exit  код завершения процесса (exit_code). Родительскому процессу передаются только 8 младших бит этого кода. Нулевое значение свидетельствует об успешном завершении, ненулевое  о завершении процесса с ошибкой или. Сама функция ничего не возвращает, так как она выполниться неудачно не может.

Библиотечная функция exit() является оболочкой для системного вызова _exit(). Она сначала очищает все буферы и закрывает все открытые потоки. Затем она вызывает все функции, которые были зарегестрированы посредством функции atexit(), причём в порядке, обратном порядку их регистрации. После этого вызывается системный вызов _exit().

Системные функции типа exec

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

#include<unistd.h>

int execl (const char *path, char *arg1, …, (char*)NULL);

int execve (const char *path, const char **argv,….., const char **envp);

#include<unistd.h>

main ()

{

pid_t pid;

switch (pid = fork())

{

case -1 : printf (“Ошибка fork () \n”);

break ;

case 0 : execl (“/bin/ls” , “ ls ”, “-l ”, (char) NULL );

printf (“Ошибка exec”);

break;

default : wait ((int *) NULL);

printf (“Программа ls завершена”);

exit (0);

}

}

Системный вызов exec() заставляет вызывающий процесс изменить свой контекст и выполнить другую программу.

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

Перегруженный процесс наследует:

  • Идентификатор процесса и идентификатор родительского процесса.

  • Идентификатор пользователя и идентификатор пользователя группы.

  • Ограничения, накладываемые на процесс.

  • Текущий и корневой каталоги.

  • Маска создания файлов.

  • Управляющий терминал.

  • Файловые дескрипторы, для которых не установлен флаг FD_CLOEXEC. Файлы, для которых этот флаг установлен, закрываются перед загрузкой новой программы.

Возможны следующие изменения атрибутов процесса:

  1. Идентификатор пользователя (если на файле установлен флаг S_UID).

  2. Эффективный идентификатор группы (если установлен флаг S_GID).

  3. Установленный (реальный) идентификатор пользователя (если установлен флаг S_UID).

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

Определённая пользователем функция-обработчик сигналов, в новой программе отсутствует.

В названной функции встречаются буквы:

pесли значение первого аргумента начинается не с / (не с корневого каталога), то при поиске подлежащего выполнению файла будет использоваться переменная среды окружения PATH.

lзначения аргументов передаются в виде списка.

vаргументы передаются в векторном виде (в виде массива указателей на символьные строки).

Если exec() завершается успешно, то она не может возвращать никакого значения. При ошибке возвращается -1.

Могут быть обнаружены следующие ошибки:

E2BIG

EACCES

ENAMETOOLONG

ENOENT

ENOTDIR

ENOEXEC

ENOMEM

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

ENOTDIR – часть имени файла, указанного в качестве параметра, не является каталогом.

ENOEXEC – файл процесса имеет разрешение на выполнение, но не является выполняемым файлом.

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

СИСТЕМНЫЕ ВЫЗОВЫ ДЛЯ ОЖИДАНИЯ ЗАВЕРШЕНИЯ ДОЧЕРНИХ ПРОЦЕССОВ.

#include <sys/wait.h>

pid_t wait(int *status_p);

pid_t waitpid(pid_t child_pid, unt *status_p, int option);

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

Функция wait приостанавливает выполнение родительского процесса до тех пор, пока ему не будет послан сигнал, либо пока один из его порождённых процессов не завершится или не будет остановлен.

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

#inckude <iostream.h>

#inckude <stdio.h>

#inckude <stdlib.h>

#inckude <errno.h>

#inckude <sys/types.h>

#inckude <sys/wait.h>

#inckude <unistd.h>

int sustem (const char *cmd)

{

pid_t pid;

int status;

switch (pid = fork ())

{

case -1: return (-1);

case 0: exec (“bin/sh” , ”-c” , cmd , 0 );

printf (“Ошибка exec \n”);

exit (errno);

}

if (waitpid (pid, &status, 0) == pid&&WIFEXITED (status)) return (WEXITSTATUS(status));

return (-1);

}

int main ()

{

int rc = 0;

char buf[256];

do

{

printf (“sh”);

fflush (stdout);

if (!gets (buf)) break;

rc = sustem (buf);

}

while (!rc);

return (rc);

}

Функция wait() возвращает идентификатор процесса, завершения которого она дождалась.

Если родительский процесс не имеет порождённых процессов, завершения которых он ожидает, или был прерван сигналом при выполнении wait(), то функция возвратит значение равное -1, а внешняя переменная errno будет содержать код ошибки. Если было порождено более одного процесса, то wait будет ожидать завершения любого из них.

Функция waitpid() более сложная и универсальная. Она также возвращает статус завершения или идентификатор порождённого процесса, но для неё можно указать, завершения какого из порождённых процессов следует ожидать. Для этого используется переменная child_pid. Она может содержать следующие значения:

  • ID  ожидать завершения процесса с этим идентификатором

  • -1  ожидать завершения любого порождённого процесса

  • 0  ожидать завершения любого порождённого процесса, принадлежащего к той же группе, что и родительский процесс

  • <0, ≠ -1  ожидать завершения любого порождённого процесса, идентификатор группы которого равен абсолютному значению аргумента

С помощью третьего аргумента (option) можно определить, будет функция waitpid() блокирующей или нет.

Можно задать:

  • WNOHANG  не блокирующий вызов, т.е. функция немедленно возвращает управление, если среди порождённых процессов нет такого, который отвечает критериям ожидания, и уже завершился. Если третий аргумент имеет любое другое значение, то системный вызов – блокирующий.

  • WUNTRACED  функция будет ожидать завершения порождённого процесса, остановленного в результате действия механизма управления заданиями, но о статусе которого не было ранее сообщено.

  • Если в качестве аргумента status_p указан нулевой указатель, то статус завершения порождённого процесса не получается и нет необходимости запрашивать статус. Если же явно указан адрес переменной в качестве параметра status_p, то родительский процесс может проверить статус завершения с помощью макрокоманд:

  1. WIFEXITED (*status_p)  возвращает ненулевое значение, если порождённый процесс был завершён с помощью системного вызова _exit, иначе возвращается 0.

  2. WEXTTSTATUS (*status_p)  возвращает код завершения порождённого процесса, который передан системным вызовом _exit.

  3. WIFSIGNALED (*status_p)  возвращает ненулевое значение, если порождённый процесс был завершён по причине прерывания сигнала.

  4. WTERMSIG (*status_p)  возвращает номер сигнала, завершившего порождённый процесс, если WIFSIGNALE вернуло ненулевое значение.

  5. WIFSTOPPED (*status_p)  возвращает ненулевое значение, если порождённый процесс был остановлен механизмом управления заданиями

  6. WSTOPSIG (*status_p)  возвращает номер сигнала, завершившего порождённый процесс, если WIFSTOPPED вернуло ненулевое значение.