9.Для чего используется системный вызов fork?
10.Как запустить программу на выполнение во вновь порождённом процессе?
11.В чём отличия функций семейства exec?
12.Что делает системный вызов system?
13.Расскажите о функциях по работе с нитями.
14.Как происходит завершение процесса? Какие системные вызовы можно для этого использовать?
15.Как происходит завершение нити? Какие системные вызовы можно для этого использовать?
16.Расскажите о функциях, используемых для обработки ошибок.
76
ГЛАВА 6. ВЗАИМОДЕЙСТВИЕ ПРОЦЕССОВ И НИТЕЙ
Как было сказано ранее, идея многопроцессных (многопрограммных) операционных систем заключается в том, что на одной ЭВМ (псевдо)одновременно исполняется несколько программ (процессов)53. При этом считается, что процессы: 1) независимы и 2) ничего не знают о существовании друг друга54. Каждому процессу выделяется свой набор ресурсов, которыми он монопольно владеет, и никто другой (кроме операционной системы) не имеет прямого доступа к ним.
Каждый процесс, в свою очередь, может выполнять несколько нитей (потоков инструкций). При этом все нити одного процесса также выполняются независимо друг от друга и обладают некоторым набором «собственных» ресурсов, но без труда могут получить доступ к общей памяти процесса.
Очевидно, что нити изначально предназначены для взаимодействия друг с другом. А могут ли возникнуть ситуации, когда одному процессу необходимо «сообщить» другому (или нескольким другим) процессу(-ам) какую-либо информацию? Ответ очевиден – да. С такими взаимодействиями мы уже сталкивались, когда изучали конвейерный способ запуска программ. Кроме этого, процессу, например, может потребоваться:
сообщить о своей готовности обработать данные;
«поделиться» с другим процессом объемом выполняемой работы или взять часть работы другого процесса на себя;
обеспечить строгую последовательность работы нескольких процессов (например, чтобы начисление процентов на счете в банке производилось только после его пополнения);
и т.п.
Ксредствам межпроцессного взаимодействия (англ. Interprocess Communications, IPC), реализованных в операционной системе Linux, относятся:
ожидание завершения родственного процесса;
сигналы;
каналы (именованные и неименованные);
очереди сообщений;
семафоры;
разделяемая память;
сокеты.
Последний способ IPC позволяет реализовать взаимодействие между процессами, выполняющимися на разных ЭВМ, соединённых каналами передачи данных.
53Очевидно, что в случае многопроцессорной (многоядерной) ЭВМ процессы на самом деле выполняются параллельно.
54Вспомним, что каждый процесс «знает» лишь номер своего родительского процесса (поле PPID деск-
риптора).
77
Все средства IPC условно можно разделить по типу процессов, участвующих во взаимодействии:
«любой процесс». Ресурсы IPC доступные всем процессам (согласно правам доступа). Такие ресурсы создаются и управляются самими процессами. К этому типу IPC относятся: именованные каналы, очереди сообщений, семафоры, разделяемая память;
«родственные процессы». Такие ресурсы создаются и управляются операционной системой. К этому типу ресурсов относятся: сигналы, неименованные каналы, сокеты.
Кроме того, процессы могут получать информацию о том, каким образом они были запущены и в какой среде. С этого типа взаимодействия и начнём.
6.1Получение информации о запуске
6.1.1.Взаимодействие с командной оболочкой (строкой запуска)
Впрограмме, написанной на языке Си, первой выполняемой функцией
является функция main. Традиционно функция main определяется следующим образом55:
int main(int argc, char *argv[]);
Из заголовка функции видно, что она имеет два параметра и возвращает целое значение. Первым параметром (argc) в функцию передаётся число аргументов, указанных в командной строке при запуске программы, включая её имя. Указатели на каждый из аргументов командной строки передаются в массиве argv[]. Таким образом, argv[0] указывает на строку, содержащую имя программы, argv[1] – на первый аргумент и т.д. до argv [argc – 1]. Элемент argv [argc] всегда содержит NULL.
Возвращаемое значение функции main является статусом завершения программы (процесса).
Приведём пример программы, которая выводит значения всех аргументов, переданных функции main (листинг 9).
Листинг 9. Вывод информации о командной строке
1.#include <stdio.h>
2.int main(int argc, char *argv[]){
3.int i;
4.printf (“Имя программы - %s\n”, argv[0]);
5.printf (“Число параметров - %d\n”, argc-1);
6.for (i = 1; i < argc; i++)
7.printf(“argv[%d] = %s\n”, i, argv[i]);
8.return (0);
55 Некоторые компиляторы с языка Си допускают и другие определения функции main. Например, Borland TurboC допускает определение типа возвращаемого значения как void. В gcc можно разрешить игнорирование аргументов функции main.
78
Листинг 9. Вывод информации о командной строке
9. }
Чтобы определить, были ли указаны в командной строке опции, аргументы командной строки могут быть обработаны при помощи следующих функций:
#include <unistd.h>
extern char *optarg;
extern int optind, opterr, optopt;
int getopt(int argc, char *argv[], char *optstring);
#include <getopt.h>
int getopt_long(int argc, char *argv[], char *optstring,
struct option *longopts, int *longindex);
int getopt_long_only(int argc, char *argv[], char *optstring,
struct option *longopts, int *longindex);
Напомним, что опцией называется аргумент командной строки, начинающийся с символов «-»(тире) или «--» (двойное тире). Причём, если используется один знак тире, то опция считается односимвольной или короткой, если же два знака тире, то многосимвольной или длинной.
Функция getopt просматривает аргументы командной строки по очереди и определяет, есть ли в них заданные короткие опции. Поиск только длинных опций осуществляется функцией getopt_long_only. Функция getopt_long осуществляет поиск как коротких, так и длинных опций.
Вкачестве аргументов функциям передаются:
argc – количество аргументов командной строки (размер массива argv);
argv – массив указателей на строки-аргументы командной строки;
optstring – указатель на строку, содержащую символы коротких опций;
longopts – массив структур option, описывающих длинные опции. Последний элемент массива должен быть заполнен нулями;
longindex – указатель на целую переменную, в которую, в случае обнаружения длинной опции, будет помещён номер соответствующего элемента массива longopts.
79
Обратите внимание, что функции getopt, getopt_long и getopt_log_only на самом деле ничего не знают о командной строке, а всего лишь просматривают массив строк на предмет нахождения в нём заданных элементов.
Структура option определена в заголовочном файле getopt.h следующим образом:
struct option { char *name; int has_arg; int *flag; int val;
};
Значения полей структуры option приведены в таблице 11.
Таблица 11. Значения полей структуры option.
Поле |
Значение |
name |
Является именем длинной опции. |
has_arg |
Может принимать значения: no_argument (или 0), если опция не име- |
|
ет аргумента; required_argument (или 1), если опция требует обяза- |
|
тельного указания аргумента; optional_argument (или 2), если опция |
|
может иметь необязательный аргумент. |
val |
Значение, которое должно быть помещено по адресу, указанному |
|
в аргументе flag, в случае если будет обнаружена данная длинная |
|
опция. |
flag |
Указывает адрес, куда требуется поместить значение val. Если flag |
|
равен NULL, то val возвращается как значение функции getopt_long |
|
(или getopt_long_only). |
Глобальные переменные optind, optarg, opterr и optopt содержат соответственно номер аргумента командной строки, который должен быть проверен на наличие опции, указатель на аргумент найденной опции, наличие ошибки, возникшей в ходе выполнения функции поиска опций, и дополнительные параметры, конкретизирующие работу функций. Подробнее об этих переменных будет сказано далее.
Поиск коротких опций функцией getopt производится так же, как командой getopts (см. выше). Отличия следующие.
1.Если просмотрены все параметры командной строки, то getopt возвращает –1.
2.Если в optstring после некоторого символа следует два двоеточия, то это означает, что опция имеет необязательный аргумент. Это является дополнением GNU.
3.Если optstring содержит символ «W», за которым следует точка с запятой («;»), то опция -W foo рассматривается как длинная опция --foo. Опция -
80