
- •Введение
- •Глава 1. Фундаментальные концепции unix Систем
- •Программы, процессы и потоки
- •Сигналы
- •Идентификаторы процессов, группы процессов и сеансы
- •Система прав
- •Другие атрибуты процесса
- •Межпроцессное взаимодействие
- •Использование системных вызовов
- •Краткие описания функций и обработка ошибок
- •Контрольные вопросы
- •Литература
- •Глава 2. Базовые операции ввода-вывода
- •Файловые операции ввода - вывода
- •Стандартные дескрипторы
- •Системные вызовы open и creat
- •Системный вызов umask
- •Системный вызов unlink
- •Текущая позиция в файле
- •Системный вызов write
- •2.8. Системный вызов read
- •2.9. Системный вызов close
- •2.10. Системный вызов lseek
- •2.11. Системные вызовы pread и pwrite
- •2.12. Системные вызовы truncate и ftruncate
- •Контрольные вопросы
- •Литература
- •Глава 3. Дополнительные операции файлового ввода_вывода
- •Низкоуровневый доступ к файловой системе
- •Жесткие и символические ссылки
- •Системный вызов getcwd
- •Отображение метаданных файла
- •Системные вызовы getpwuid, getgrgid и getlogin
- •Каталоги
- •Системные вызовы chdir и fchdir
- •Системные вызовы mkdir и rmdir
- •Контрольные вопросы
- •Литература
- •Глава 4. Процессы и потоки
- •4.1. Среда окружения
- •Системный вызов exec
- •Системный вызов fork
- •Завершение процесса и системные вызовы exit
- •Системные вызовы wait, waitpid и waitid
- •Получение и изменение идентификаторов пользователя и группы
- •Получение и изменение приоритета
- •Контрольные вопросы
- •Литература
- •Глава 5. Механизмы межпроцессного взаимодействия
- •5.1. Каналы
- •5.2. Системные вызовы dup и dup2
- •5.3. Двунаправленное взаимодействие с использованием однонаправленных каналов
- •Контрольные вопросы
- •Литература
- •Глава 6.Механизмы взаимодействия процессов
- •Именованные каналы (fifo)
- •Системные вызовы для работы с очередями сообщений posix
- •Семафоры
- •Системные вызовы для работы с общей памятью posix
- •Контрольные вопросы
- •Литература
- •Глава 7.Сетевое взаимодействие и сокеты
- •Основные системные вызовы для работы с сокетами, образующими логические соединения
- •Обслуживание нескольких клиентов
- •Адресация сокетов
- •In_port_t sin_port; /* номер порта (uint16_t) */
- •In_addr_t s_addr; /* адрес iPv4 (uint32_t) */
- •Домен адресов af_inet6
- •In_port_t sin6_port; /* номер порта (uint16_t) */
- •Доменная система именования
- •Параметры сокетов
- •Контрольные вопросы
- •Литература
- •Глава 8.Сигналы и таймеры
- •Введение в сигналы
- •Жизненный цикл сигналов
- •Типы сигналов
- •Системный вызов sigaction
- •Контрольные вопросы
- •Литература
- •Заключение
- •Список литературы
- •Глава 2. Базовые операции ввода-вывода 14
- •Глава 3. Дополнительные операции файлового ввода_вывода 25
- •Глава 6. Механизмы взаимодействия процессов 58
Системный вызов exec
Понять системные вызовы exec и fork без четкого понимания различий между процессом и программой невозможно. Напомним суть различий: процесс – это среда исполнения, которая включает в себя сегмент исполняемого кода, сегменты пользовательских и системных данных, а также набор дополнительных ресурсов, полученных во время исполнения. Программа - это файл, который содержит исполняемый код, сегменты с данными для инициализации и с данными пользователя.
Системный вызов exec повторно инициализирует процесс, подменяя его указанной программой - программа меняется, а процесс остается. Системный вызов fork наоборот запускает новый процесс, который является точной копией существующего, простым копированием сегментов с исполняемым кодом и данными. Вновь созданный процесс продолжает работу с того же места, что и изначальный, таким образом, оба процесса продолжают исполнять один и тот же код.
Системный вызов exec - единственный способ запуска программ в Unix, а системный вызов fork - единственный способ запустить новый процесс. На самом деле, системного вызова с именем exec не существует. Под этим именем подразумевается целое семейство из шести системных вызовов, имена которых можно записать как execAB. A – это один из символов l или v, они определяют, как входные аргументы передаются вызову - в виде списка или в виде массива. B (может отсутствовать) – это либо p, указывающий, что поиск файла программы должен выполняться с помощью переменной PATH, либо e - такому вызову передается специфичная среда окружения. Получается шесть различных системных вызовов: execl, execv, execlp, execvp, execle и execve. Рассмотрим execl:
execl - запускает программу, входные аргументы передаются в виде списка
#include <unistd.h>
int execl (
const char *path, /* полный путь к программе */
const char *arg0, /* первый аргумент – имя файла */
const char *arg1, /* второй аргумент*/
…………………… /* остальные аргументы */
NULL /* пустой указатель, завершающий список */
);
/* В случае ошибки возвращает -1 (код ошибки - в переменной errno) */
Аргумент path должен содержать полное имя исполняемого файла с программой действующего идентификатора пользователя (например, с правами 755). Исполняемый код процесса будет затерт исполняемым кодом новой программы, сегмент данных также будет затерт сегментом данных новой программы, а стек будет переинициализирован. Исполнение новой программы начнется с самого начала. В случае успеха возврат из execl не предусмотрен, потому что точка возврата будет утеряна. В случае ошибки execl вернет -1. Наиболее частые причины, приводящие к невозможности запустить новую программу, это либо отсутствие файла с программой, либо отсутствие права на ее запуск. Все остальные аргументы вызова собираются в массив указателей на строки, а последним всегда должен стоять пустой указатель, который определяет конец списка входных аргументов. Первый аргумент – это имя файла программы (только само имя, без пути к нему). Новая программа получает доступ к этому списку через уже знакомые нам аргументы функции main() - argc и argv. Среда окружения также передается новой программе и доступна ей через указатель environ или посредством функции getenv.
Поскольку процесс продолжает существовать и сегмент с системными данными практически не изменяется, почти все атрибуты также остаются неизменными, включая идентификатор процесса, идентификатор процесса-предка, идентификатор группы процесса, идентификатор сессии, управляющий терминал, реальные идентификаторы пользователя и группы, текущий и корневой каталоги, приоритет, статистические характеристики времени исполнения в пространстве пользователя и в пространстве ядра и, как правило, дескрипторы открытых файлов. Гораздо проще перечислить основные атрибуты, которые изменяются:
если процесс назначал свои обработчики сигналов, то все они сбрасываются в исходное состояние, поскольку функции-обработчики после запуска новой программы станут недоступны;
если в новой программе установлены биты set-user-ID или set-group-ID, то действующие идентификаторы пользователя и группы переустанавливаются в соответствии с идентификаторами владельца и группы файла программы. Нет никакого способа вернуть прежние действующие идентификаторы, если они отличаются от реальных;
регистрация всех функций, которая была выполнена с помощью atexit(), отменяется, поскольку код этих функций будет затерт;
сегменты общей памяти отсоединяются, поскольку точки соединения будут утеряны;
именованные семафоры POSIX закрываются. Семафоры System V остаются без изменений.
Если сохранение атрибута или ресурса не имеет смысла, то он или сбрасывается в состояние по умолчанию, или закрывается.
Другие пять вызовов семейства exec предоставляют три дополнительные возможности, недоступные в execl:
передать аргументы в виде массива, а не в виде списка. Это совершенно необходимо, когда число аргументов в момент написания программы заранее неизвестно;
поиск файла программы с использованием значения переменной окружения PATH, как это делает командная оболочка;
передавать указатель на сформированную вручную среду окружения вместо неявной передачи указателя environ.
Ниже приводится краткое описание остальных системных вызовов семейства exec:
execv - запускает программу, аргументы передаются в виде массива
#include <unistd.h>
int execv (
const char *path, /* полный путь к файлу с программой */
char * const argv[ ] /* массив аргументов */
);
/* В случае ошибки возвращает -1 (код ошибки - в переменной errno) */
execlp - запускает программу, аргументы передаются в виде списка, поиск файла ведется с использованием переменной PATH
#include <unistd.h>
int execlp (
const char *file, /* имя файла с программой */
const char *arg0, /* первый аргумент – имя файла */
const char *arg1, /* второй аргумент*/
…………………… /* остальные аргументы */
NULL /* пустой указатель, завершающий список аргументов*/
);
/* В случае ошибки возвращает -1 (код ошибки - в переменной errno) */
execvp - запускает программу, аргументы передаются в виде массива, поиск файла ведется с использованием переменной PATH
#include <unistd.h>
int execvp (
const char *file, /* имя файла с программой */
char * const argv[ ] /* массив аргументов */
);
/* В случае ошибки возвращает -1 (код ошибки - в переменной errno) */
execle - запускает программу, аргументы передаются в виде списка, так же передается среда окружения
#include <unistd.h>
int execle (
const char *path, /* полный путь к файлу с программой */
const char *arg0, /* первый аргумент – имя файла */
const char *arg1, /* второй аргумент*/
…………………… /* остальные аргументы */
NULL /* пустой указатель, завершающий список аргументов*/
char *const envv[ ] /* массив сформированной среды окружения */
);
/* В случае ошибки возвращает -1 (код ошибки - в переменной errno) */
execve - запускает программу, аргументы передаются в виде списка, так же передается среда окружения
#include <unistd.h>
int ve (
const char *path, /* полный путь к файлу с программой */
char * const argv[ ], /* массив аргументов */
char *const envv[ ] /* массив сформированной среды окружения */
);
/* В случае ошибки возвращает -1 (код ошибки - в переменной errno) */
Используемый аргумент argv абсолютно идентичен аргументу argv, передаваемому функции main. Последним в этом массиве всегда должен стоять NULL. Если аргумент file в вызовах execlp или execvp не содержит символ «/», то выполняется последовательная подстановка имен каталогов из переменной PATH и производится поиск файла с правом на исполнение. Если такой файл найден, и он является исполняемой программой, то она запускается на исполнение. Если нет, то делается предположение о том, что файл является сценарием и вызывается командный интерпретатор, которому передается путь к файлу в первом аргументе.
Например, если file - это команда echo, и в результате поиска был обнаружен файл /bin/echo, то этот файл будет запущен как программа, потому что он содержит команды микропроцессора.