Лабораторна робота №4. Контроль виконання програми



Мета:Оволодіння методами контролю помилок при розробці програм. Знайомство з організацією та доступу к системному масиву помилок. Знайомство з функціями обробки помилок. 

 

Завдання для самостійної підготовки:

Вивчити:

− синтаксис та правила вживання функцій обробки помилок;

− організацією та правила доступу к системному масиву помилок.

− правила використання контролю помилок в програмі.

Методичні вказівки

 

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

 

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

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

− нуль, якщо програма завершена успішно (через виклики процедур 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 ("спроба відкрити вивідний файл невдала");}

 

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

 

Функція 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);}

Макрос asssert()

Розроблено методики програмування, що дозволяють виявляти помилки на ранніх стадіях і вирішувати проблеми, що виникають у ході виконання програми.

Найпростіший спосіб виявлення ненормальних ситуацій - стандартний макрос asssert() у мові С.

 

# Include <assert.h>

assert (expression, [error_string])

Його аргументом є логічне вираження.

Якщо

expression = TRUE  програма триває

Якщо

expression = FALSE  програма завершується і видає повідомлення про помилку з зазначенням вихідного файлу, номер рядка, а також тексту вираження, що призвів до помилки. При цьому розробнику надається вибір - остаточно перервати роботу програми (Abort), перемкнутися в вікно отладчика (Retry) або продовжити роботу (Ignore).

Assert реалізований як макрос, тому вираз expression не може містити текстових літералів.

Даний макрос корисний для включення діагностики у програми:

− правильність аргументів функцій,

− виконання вхідних і вихідних умов при виклику функцій.

− наявність непередбачених повертаються значень.

 

Assert() зручний ще тим, що є документацією, яка описує роботу програми безпосередньо в її початковому тексті. Якщо програма містить рядок assert (умова), то будь-який, хто читає вихідний текст, буде знати, що в даній точці програми зазначена умова завжди повинно бути істинним. Якщо ж умова не виконується, то, очевидно, в програмі присутня помилка.

Макрос ASSERT відстежує програмні помилки лише в перевіреній версії програми (#define DEBUG - стоїть за умовчанням). Цей макрос не створює ніякого коду в остаточній версії. Якщо саме в остаточній версії потрібно оцінити вираз, замість ASSERT слід застосовувати макрос VERIFY.

Якщо програма компілюється як реліз (# define NDEBUG), препроцесор прибирає з тіла програми всі виклики макросу assert (). Це має сенс тільки тоді, коли продуктивність є вузьким місцем програми, причому макрос потрібно відключати лише в найбільш критичних файлах.

Визначення ndebug повинно бути перед включенням файлу assert.h, тоді оператори assert () будуть "закоментовані".

Оскільки макрос assert () може віддалятися препроцесором з програми, потрібно ретельно перевірити, чи не мають вираження з ним побічних ефектів. У них не слід викликати функції, присвоювати значення змінним і користуватися модифікуючими операторами типу + +.

Наприклад, нехай у циклі викликається функція funct (). У разі успішного виконання вона повертає 0, інакше - нульове значення. Якщо написати так:

 

for (i = 0; i <100; + + i)

assert (funct () == 0);

програма буде зупинятися при успішному виконанні функції.

 

Припустимо, програма перекомпілювати з макроконстантой NDEBUG. У результаті з програми будуть видалені всі макроси assert (), і функція funct () взагалі не буде викликано. Щоб уникнути цього необхідно використовувати наступний підхід:

 

for (i = 0; i <100; + + i)

{int status = do_something ();

assert (status ==

0);}

В якості додаткового параметра assert може приймати також повідомлення, яке треба надрукувати в разі спрацювання.

 

assert (fopen (sFile), "Couldn't open file \ n");


Дата добавления: 2018-04-05; просмотров: 346; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!