2. Використання системних функцій
2.1. Системна функція fork ()
Системна функція (або системний виклик) fork () створює новий процес, який є дублікатом процесу який його викликав, тобто його батька. При успішному виконанні функція fork () повертає батьківському і дочірньому процесам два різних значення. Дочірньому процесу число 0, а батьківському – значення PID дочірнього процесу. Батьківський і дочірній процеси продовжують виконуватися з інструкції, безпосередньо наступній за функцією fork (). У випадку невдалого виконання (це коли дочірній процес не був створений) батьківському процесу повертається число -1.
Синопсис
# include <unistd>
pid_t fork (void);
Невдале виконання функції fork () можливе у випадку, коли система не володіє ресурсами для створення ще одного процесу. Це відбувається при перевищенні обмежень (якщо вони існують) на кількість дочірніх процесів, які може породжувати батько, або на кількість процесів, які виконуються в межах всієї системи. В цьому випадку встановлюється змінна error, яка означає наявність помилки.
2.2. Системні функції exec
Сімейство функцій exec призначене для заміни образу процесу, який його викликає, образом нового процесу. При виклику функції fork () створюється новий процес, який точною копією батьківського процесу, а функція exec () замінює образ скопійованого процесу образом копії. Образ нового процесу є звичайний виконуваний файл, який миттєво завантажується на виконання. Цей файл можемо задати за допомогою імені і шляху доступу до нього. Функції сімейства exec можуть передавати новому процесу аргументи командної стрічки, а також встановити змінні середовища. Якщо функція виконалася успішно, вона не повертає ніякого значення, оскільки образ процесу, який містить звертання до функції exec, вже пере записано. У випадку невдачі процесу, який викликає, повертається число -1. Всі функції exec () можуть мати невдале закінчення при наступних умовах:
дозволи не визнано;
дозвіл на пошук відкидається для каталогу файлів, що виконуються;
дозвіл на виконання відкидається для файлу, який виконується;
файли не існують;
файл, який виконується не існує;
каталог не існує;
файл неможливо виконати;
файл неможливо виконати, оскільки він відкритий для запису іншим процесом;
файл не є виконуваним;
проблеми з символічними посиланнями;
при аналізі шляху до файлу, що виконується, символічні посилання створюють цикл;
символічні посилання роблять шлях до файлу, який виконується, надто довгим.
Функції сімейства exec використовуються разом з функцією fork (). Функція fork () створює та ініціює дочірній процес за образом і подібністю батьківського. Образ дочірнього процесу в подальшому замінює образ свого предка за допомогою виклику функції exec (). Приклад використання функцій fork () і exec () показано в лістінгу 1.
Лістінг 1. Використання системних функцій fork () і exec ()
. . .
RtValue = fork ();
if (RtValue = = 0) {
execl (/path/direct, direct, . );
}
В лістінгу 1 показано виклик функції fork (). Значення, які вона повертає, зберігається у змінній RtValue. Якщо значення RtValue рівне 0, отже, це – дочірній процес, і в ньому викликається функція execl () з параметрами. Перший параметр містить шлях до модуля, який виконується, другий – інструкцію, для виконання, третій – аргумент. Другий параметр, direct, являє собою ім'я утиліти, яка перераховує всі каталоги і підкаталоги з даного каталогу. Всього існує шість версій функції exec, передбачених для використання різних угод про виклики.
2.3. Функції execl ()
Функції execl (),execle () і execlp () передають функції командної стрічки у вигляді списку. Кількість аргументів командної стрічки повинна бути відома під час компіляції.
int execl (const char *path, const char *arg0, . . ./*, (char *)0 */);
Тут path – дорожнє (шляхове) ім'я програми, яка виконується. Його можна подати у вигляді повного складеного імені або відносного складеного імені з біжучого каталогу. Наступні параметри є списком аргументів командної стрічки, від arg0 до argn. Всього може бути n аргументів. Даний список завершується NULL-вказівником.
int execle (const char *path, const char *arg0, . . ./*, (char *)0 *, char * const envp[]*/);
Дана функція аналогічна функції execl () з однією відмінністю: вона має додатковий параметр, envp[]. Цей параметр вказує на нове середовище для нового процесу, тобто envp[] – це вказівник на стрічковий масив з завершальним нульовим символом. Кожна його стрічка, також завершується нульовим символом, має наступну форму
name = value
Тут name – ім'я змінної середовища, а value – стрічка що зберігається. Значення параметру envp[] можемо присвоїти наступним чином:
char *const envp[]={“PATH=/opt/kde2:/sbin”, “HOME = /home”, NULL};
Тут PATH і HOME – змінні середовища.
int execlp (const char *fale, const char *arg0, . . ./*, (char *)0 */);
Тут fale – ім'я програми, яка виконується. Для визначення місця знаходження програм, що виконуються, використовується змінна середовища PATH. Останні параметри є списком аргументів командної стрічки (дивись опис функції execl ()).
Приклад застосування синтаксису функції execl () зрізними аргументами:
char *const args[] = {“direct”, “.”, NULL};
char *const envp[] = {“fales=50”, NULL};
execl (“path/direct”, “direct”, “.”,NULL);
execle (“path/direct”, “direct”, “.”,NULL, envp);
execlp (“direct”, “direct”, “.”,NULL);
Тут в кожному прикладі виклику execl-функції активізований процес викликає програму direct.
Синопсис
# include <unistd.h
int execl (const char *path, const char *arg0, . . ./*, (char *)0 */);
int execle (const char *path, const char *arg0, . . ./*, (char *)0 *, char * const envp[]*/);
int execlp (const char *fale, const char *arg0, . . ./*, (char *)0 */);
int execv (const char *path, char *const arg[]);
int execve (const char *path, char *const arg[], char *const envp[]);
int execvp (const char *fale, char *const arg[]);
2.4. Функція execv ()
Функції execv (),execve () і execvp () передають аргументи командної стрічки у вектори вказівників на стрічки із завершальним нульовим символом. Кількість аргументів командної стрічки повинно бути відоме під час компіляції. Елемент argv [0] як правило є командою.
int execv (const char *path, char *const arg[]);
Тут path –дорожнє (шляхове) ім'я програми, що виконується. Його можемо задати у вигляді повного складеного імені або відносного складеного імені з біжучого каталогу. Наступний параметр є вектором (з завершальним нульовим символом), який містить аргументи командної стрічки, подані у вигляді стрічок з завершальним нульовим символом. Всього може бути n аргументів. Даний вектор завершується NULL-вказівником. Елементу arg[] можемо надати значення наступним чином:
char *const arg[] = {“traverse”, “.”, “>”, “1000”, NULL};
Приклад виклику цієї функції:
execv (“traverse”, arg);
В цьому випадку утиліта traverse перерахує всі файли в біжучому каталозі розмір яких більше 1000 байт.
int execve (const char *path, char *const arg[], char *const envp[]);
Дана функція аналогічна функції execv (), за однією відмінністю: вона має додатковий параметр, envp[], який описано вище.
int execvp (const char *fale, char *const arg[]);
Тут fale – ім'я програми яка виконується. Наступний параметр є вектором (з завершальним нульовим символом), який містить аргументи командної стрічки, подані у вигляді стрічок з завершальними нульовими символами. Всього може бути n аргументів. Даний вектор завершується NULL-вказівником.
Подано приклад застосування синтаксису функції execvp () з різними аргументами:
char *const arg [ ] = {“traverse”, “.”, “>”, “1000”, NULL};
char *const evenp [ ] = {“files = 50”, NULL};
execv (“/path/traverse”, arg};
execve (“/path/traverse”, arg, envp};
execv (“traverse”, arg};
Тут в кожному прикладі виклику execv-функції активізований процес виконує програму traverse.
2.5. Визначення обмежень для функції exec ()
Існують обмеження на розміри вектора argv [ ] і масиву envp [ ], які передаються функціям сімейства exec. Для визначення максимального розміру аргументів командної стрічки і розміру змінних середовища при використанні exec-функцій (які приймають параметр envp [ ] можемо використати функцію sysconf ()). Щоб дана функція повернула розмір, її параметру name необхідно передати значення _SC_ARG_MAX.
Синопсис
# include < unistd.h >
long sysconf (int name);
Ще одним обмеженням при використанні функцій сімейства exec та інших функцій, які застосовуються для створення процесів, є максимальна кількість процесів, що виконуються одночасно, яке доступне для одного користувача. Щоб функція sysconf () повернула дане число, її параметру name необхідно передати значення _SC_CHILD_MAX.
2.6. Читання та встановлення змінних середовища
Змінні середовища, це стрічки з нульовими символами в кінці, в яких зберігається наступна системна інформація: шляхи до каталогів, які містять команди, бібліотеки, функції і процедури які використовує процес. Їх можна також використовувати для передачі довільних визначених користувачем даних між батьківським і дочірнім процесами. Вони забезпечують механізм надання процесу спеціальної інформації без необхідності жорсткої її сполучення з кодом програми. Змінні середовища визначені системою і спільно використовуються всіма її оболонками та процесами. Ці змінні ініціюються файлами запуску. Частіше всього використовуються наступні системні змінні.
$HOME |
Повне складене ім'я користувача |
$PATH |
Список каталогів для файлів, що виконуються при виконанні команд |
Повне складене ім'я поштового ящика користувача |
|
$USER |
Ідентифікатор (id) користувача |
$SHELL |
Повне складене ім'я командної оболонки зареєстрованого користувача |
$TERM |
Тип терміналу користувача |
Змінні середовища можуть зберігатися у файлі або у списку, які належать середовищу. Даний список середовища містить вказівники на стрічки з нульовими символами в кінці. Коли процес починає виконання, змінна
extern char **environ
буде вказувати на список середовища. Стрічки які складають список середовища, мають наступний формат:
name = value
Процеси ініційовані за допомогою функцій execl (),execlp (),execv () і execvp (), успадковують конфігурацію середовища батьківського процесу. Процеси, ініційовані за допомогою функцій execve () і execle (), самі встановлюють середовище.
Існують функції та утиліти, які дозволяють опитати, добавити або модифікувати змінні середовища. Функція getevn ()використовується для визначення факту встановлення заданої змінної. Змінна, яка вас цікавить задається за допомогою параметру name. Якщо задана змінна не встановлена, функція повертає значення NULL. В інакшому випадку (якщо змінна встановлена), функція повертає вказівник на стрічку, яка містить її значення.
Синопсис
# include < stdlib.h >
char *getenv ( const char *name );
int setenv (const char *name, const char *value, int overwrite );
void unsetenv ( const char *name );
Розглянемо приклад:
string Path;
Path = getenv (“PATH”);
Тут стрічці Path присвоюється значення, яке міститься у вбудованій змінній PATH.
Функція setenv () використовується для зміни значення існуючої змінної середовища або добавлення нової змінної в середовище процесу, який викликає. Параметр name містить ім'я змінної середовища, яку необхідно добавити або змінити. Заданій змінній присвоюється значення, яке передається через параметр value. Якщо змінна, задана параметром name, вже існує, її попереднє значення замінюється значенням, заданим параметром value при умові, якщо параметр overwrite містить ненульове значення. Якщо ж overwrite рівне 0, вміст змінної середовища модифікується. Функція setenv () повертає 0 при повертає значення 0 при вдалому виконанні, в інакшому випадку – значення -1. Функція unsetenv () видаляє змінну середовища, яка була задана параметром name.
2.7. Використання функції system () для породження процесів
Функція system () використовується для виконання команд або запуску програми. Функція system () виконують функцію fork (), а потім дочірній процес викликає функцію exec () з оболонкою, виконуючи задану команду або програму.
Синопсис
# include < stdlib.h >
int system (const char *string);
В якості параметру string можемо передати системну команду або ім'я файлу, який виконується. При вдалому виконанні функція повертає статус завершення команди або значення, яке повертається програмою (якщо таке передбачено). Помилки можуть виникати на декількох рівнях, тобто помилка може відбутися при виконанні функції fork () або exec () чи задана оболонка може бути невідповідною для виконання команди або програми.
Функція system () повертає значення батьківському процесу. При невдалому виконанні функція exec () повертає число 127, а при виявленні інших помилок – число -1. Ця функція не впливає на стан очікування дочірніх процесів.
