Коди завершення програми

 

Коли програма завершує роботу, вона повідомляє операційну систему про свій з стоянні, посилаючи їй код завершення. У більшості інтерпретаторів команд код завершення останньої виконаної програми міститься в спеціальній змінній $?.

Код завершення дорівнює (за існуючою угодою):

− нуль, якщо програма завершена успішно (через виклики процедур ABORT або EXIT або закінчиться робота процедури main)

− не рівне нулю 16-розрядне ціле число при наявності помилки.

 

Зручно різними ненульовими кодами позначати різні помилкові ситуації.

 

Основні ситуації, що призводять до збоїв системних викликів:

1. Ліміт ресурсів. У системі можуть закінчитися ресурси або програма може вичерпати ліміт ресурсів, накладений на неї системою. Наприклад, програма може запросити занадто багато пам'яті, записати надто великий обсяг даних на диск або від крити надмірна кількість файлів одночасно.

2. Обмеження прав доступу. Про грама намагається виконати операцію за відсутності належних привілеїв. На приклад, програма може спробувати здійснити запис в доступний тільки для читання файл, звернутися до пам'яті іншого процесу або знищити програму іншого користувача.

3. Помилкові аргументи. Аргументи системного виклику можуть виявитися неправильними або через помилково введених користувачем даних, або із-за помилки самої програми. Наприклад, програма може передати системному виклику неправильна адреса пам'яті або невірний дескриптор файлу. Інший варіант помилки - спроба відкрити каталог замість звичайного файлу або передати ім'я файлу системного виклику, очіку дає ім'я каталогу.

4. Помилки пристроїв. Системний виклик може аварійно завершитися з причин, не залежних від са мій програми. Найчастіше це відбувається при доступі до апаратних пристроїв. Пристрій може працювати некоректно або не підтримувати потрібну операцію, або в дисковод просто не вставлений диск.

5. Зовнішні впливи. Виконання системного виклику іноді переривається зовнішніми подіями, до како вим відноситься, наприклад, отримання сигналу. Це не обов'язково означає помилку, але відповідальність за перезапуск системного виклику покладається на програму.

 

У випадку помилки системні виклики, як правило, повертають -1, покажчик NULL, або константу типу EOF. Але це значення повернення повідомляє тільки те, що помилка сталася. Але для більш спеціалізованої обробки помилок необхідні додаткові відомості

Більшість системних викликів (приблизно 80%) зберігають у спеціальної змінної errno розширену інформацію про помилку, що відбулася. Змінна errno містить номер помилки системи. Оскільки всі системні виклики працюють із однієї й т же змінної, змінна errno модифікується після кожного системного виклику.

Можливі значення errno задаються макроконстантами препроцесора, які, записуються прописними буквами й починаються з літери "E", наприклад EACCES і EINVAL. При роботі значеннями змінної errno варто завжди використовувати макроконстанти, а не реальні числові значення помилок. Всі ці константи визначені у файлі <errno. h>.

Текст діагностичного повідомлення зберігається у визначеному масиві sys_errlist, що зберігається в стандартній бібліотеці.

Змінна sys_nerr містить розмір цього масиву. Змінна errno може використовуватися як індекс у цьому масиві.

Стандарт POSIX визначає наступні символьні імена помилок:

E2BIG - Список аргументів занадто довгий

EACCES - Відмова в доступі

EAGAIN - Ресурс тимчасово недоступний

EBADF - Неправильний дескриптор файлу

EBADMSG - Неправильне повідомлення

EBUSY - Ресурс зайнятий

ECANCELED - Операція скасована

ECHILD - Немає дочірнього процесу

EDEADLK - Обхід тупика ресурсів

EDOM - Помилка області визначення

EEXIST - Файл існує

EFAULT - Неправильна адреса

EFBIG - Файл занадто великий

EINPROGRESS - Операція в процесі виконання

EINTR - Перерваний виклик функції

EINVAL - Неправильний аргумент

EIO - Помилка уведення-висновку

EISDIR - Це каталог

EMFILE - Занадто багато відкритих файлів

EMLINK - Занадто багато зв'язків

EMSGSIZE - Невизначена довжина буфера повідомлення

ENAMETOOLONG - Ім'я файлу занадто довге

ENFILE - Занадто багато відкритих файлів у системі

ENODEV - Немає такого пристрою

ENOENT - Немає такого файлу в каталозі

ENOEXEC - Помилка формату файлу, що виконується

ENOLCK - Блокування недоступне

ENOMEM - Недостатньо пам'яті

ENOSPC - Пам'яті на пристрої не залишилося

ENOSYS - Функція не реалізована

ENOTDIR - Це не каталог

ENOTEMPTY - Каталог непустий

ENOTSUP - Не підтримується

ENOTTY - Невизначена операція керування уведенням-

висновком

ENXIO - Немає такого пристрою або адреси

EPERM - Операція не дозволена

EPIPE - Зруйнований канал

ERANGE - Результат занадто великий

EROFS - Файлова система тільки на читання

ESPIPE - Неправільне позіціонування

ESRCH - Немає такого процесу

ETIMEDOUT - Операція затримана

EXDEV - Невизначений зв'язок

Приклад.

 

#include <errno.h> /* коди помилок */

extern int errno;

extern char *sys_errlist[];

int value;

…….

if((value = sys_call(...)) < 0 ){

printf("Error:%s(%d)\n",sys_errlist[errno],errno );

exit(errno); /*примусове завершення програми*/

}

 

sys_call(...)-деякий системний виклик

 

2. Функції обробки значення errno

 

У бібліотеці UNIX є зручні функції обробки значення errno

 

Функція strerror() - повертає строковий еквівалент коду помилки. Ці рядки можна включати в повідомлення про помилки. Оголошення функції перебуває у файлі <string. h>.

 

У наступному фрагменті програми робиться спроба відкрити файл. Якщо це не виходить, виводиться повідомлення про помилку й програма завершує свою роботу. У випадку успіху операції функція open() повертає дескриптор відкритого файлу, інакше - -1.

 

fd = open("inputfile.txt",O_RDONLY);

if(fd == -1){

fprintf(stderr,"error opening file: %s\n", strerror(errno)); exit (1);

}

 

У наступному фрагменті оголошена struct stat statbuf. Відкривається деякий файл, змінної fd привласнюється номер файлового дескриптора. C допомогою fstat робиться спроба зчитувати інформацію про файл(за значенням fd) у структуру statbuf

if (fstat ( fd, &statbuf)==-1)

{ printf("Fstat failed; %s\n",strerror(errno)); }

 

Функція perror() - за значенням errno бере з таблиці sys_errlist відповідне значення й записує його безпосередньо в потік stderr. (оголошена у файлі <stdio.h>). Перед властиво повідомленням варто розміщати строковий префікс, що містить ім'я функції або модуля, що стали причиною збою.

 

Приклади

1.

# Include <stdio.h>

int main (void)

{chars [] = "Hello, World!";

If (printf ("% s \ n", s)> 0)

{perror("Can ' t print ");

return -1;}

return 0;}

2.

# Include <stdio.h>

int main (void)

{FILE * fp;

fp = fopen ("perror.dat", "r");

if (! Fp)

perror ("Не можу відкрити файл для читання. \ n ");

return 0;}

 

3.

# Include <unistd.h>

# include <fcntl.h>

# include <stdio.h>

int main (void)

{int fn1, fn2;

fn1 = open ("data1", O_RDONLY);

if (fn1 == -1)

perror ("спроба відкрити вступної файл невдала");

fn2=open("data2",O_WRONLY|O_TRUNC | O_CREAT,

S_IREAD | S_IWRITE);

if (fn2 == -1)

perror ("спроба відкрити вивідний файл невдала");}

 

Залежно від особливостей програми й використовуваного системного виклику конкретні дії, що вживаються у разі помилки, можуть бути різними: поява повідомлення про помилку, скасування операції, аварійне завершення програми, повторна тортури і навіть ігнорування помилки. Тим не менш, важливо включити в програму код, що обробляє всі можливі варіанти помилок.

 

3. Функція atexit()

У програмі, написаній на C або C + + код повернення вказується у операторі return у функції main або при виконанні виклику функції exit (). Процес може бути завершений і з незалежних від неї обставин, наприклад, внаслідок отримання сигналу. У цьому випадку функція exit () буде викликана ядром від імені процесу.

Системний виклик exit () виглядає наступним чином:

# include <unistd.h>

void exit (int status);

Аргумент status - код повернення програми - повертається батьківському процесові.

Наявність коду повернення дозволяє програмам взаємодіяти один з одним.

При успішному завершенні програми крім передачі коду повернення, функція exit (0) виробляє ряд додаткових дій, зокрема виводить буферизованная дані і закриває потоки введення-виведення.

Такі дії називаються обробкою виходу. Завдання може зареєструвати до 32 обробників виходу (exit handler), - функції, які викликаються після виклику exit (), але до остаточного завершення процесу. Викликаються ці обробники за принципом LIFO і реєструються за допомогою функції atexit ():

ATEXIT - реєстрова процедура, яка реєструє функцію, адреса якої передається їй як параметр, тобто через регістр (E) AX, як функцію завершення програми. При успішній реєстрації ATEXIT повертає 0. Завершальні функції не повинні мати параметрів і повернення.

 

# Include <stdlib.h>

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

Приклад:

# include <stdio.h>

# include <stdlib.h>

void handler1 ()

{printf

("handler1 \

n");}

void handler2 ()

{printf

("handler2 \

n");}

void handler3 ()

{printf

("handler2 \

n");}

int main ()

{atexit

(& handler1);

atexit (& handler2);

atexit (& handler3);

return

(0);}