Поради щодо використання assert ()

1. Перевірки наявності порожніх покажчиків, наприклад, в списку аргументів функції. Повідомлення про помилку, що генерується рядком

{ assert (pointer! = NULL)}

Отримане повідомлення про помилку

Assertion 'pointer! = (Void *)O)'failed

більш інформативно, ніж повідомлення, що видається у відповідь на спробу розкриття порожнього покажчика:

Segmentation fault ( core dumped)

2. Перевіряйте значення параметрів функції. Наприклад, якщо у функції передбачається, що параметр foo має тільки позитивні значення, поставте наступну перевірку на самому початку тіла функції:

assert (foo> 0);

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

 

3. Макрос assert () не слід застосовувати для перевірки даних, що вводяться користувачем. Звичайно, вхідні дані завжди потрібно перевіряти, але іншими способами. Макрос assert () призначений лише для внутрішніх перевірок.

 

Приклад

# include <assert.h>

# include <stdio.h>

char * string = "" / * порожній рядок * /

int value = 1;

 

main ()

{assert(value> 0);

printf ("Пройшли assert ( value> 0) \ n ");

assert (* string! = '0');}

 

5. Індівідуальні завдання

Написати програму, що складається з функцію funct () і головного модуля.

1. Функція повинна зчитувати з екрану деяке ціле число і повертати 1 у разі введення позитивного числа і -1 - у разі введення негативного числа. У головному модулі реалізувати обробку значення, що повертається за допомогою макросу assert (). У разі введення негативного числа програма зупиняється і виводить повідомлення на екран.

2. Функція повинна зчитувати з екрану деяке ціле число і повертати 1 у разі, коли введене число кратне 10, інакше - -1. У головному модулі реалізувати обробку значення, що повертається за допомогою макросу assert (). У разі введення числа, не кратного 10 програма зупиняється і виводить повідомлення на екран.

3. Функція повинна зчитувати з екрану деяке ціле число і повертати кількість десятків у цьому числі. У головному модулі реалізувати обробку значення, що повертається за допомогою макросу assert (). У разі введення числа з кількістю десятків менше 5, програма зупиняється і виводить повідомлення на екран.

4. Функція повинна зчитувати з екрану деяке ціле число і виводити на екран рядок виду «НАДАНО N», де N - введене число. У головному модулі за допомогою макросу assert () реалізувати контроль кількості викликів функції. Коли кількість викликів функції перевищить 5, програма зупиняється і виводить повідомлення на екран.

5. Функція повинна зчитувати з екрану деяке ціле число і виводити на екран рядок виду «НАДАНО N», де N - введене число. N передається в головний модуль. У головному модулі Прийняті значення заносяться в масив. Потім треба за допомогою макросу assert () реалізувати контроль виведення певної кількості елементів масиву. Програма зупиняється і виводить повідомлення на екран. Після того, як буде виведено 5 елементів.

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

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

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

Лабораторна робота №5 Файлові API.

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

 

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

Вивчити:

− організацію файлів ОС UNIX;

− бібліотечні функції роботи з файлами ОС UNIX;

− системні виклики роботи з файлами ОС UNIX;

− формування прав доступу до файлів ОС UNIX.

 

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

1 Основні поняття

Файли являють собою області пам'яті на зовнішньому носії (як правило магнітному диску), призначені для зберігання даних.

Файли відрізняються від звичайних масивів тим, що

− вони можуть змінювати свій розмір;

− звернення до елементів цих масивів здійснюється не за допомогою операції індексації [], а при допомогою спеціальних системних викликів і функцій;

− доступ до елементів файлу відбувається в так званій "позиції читання / запису", яка автоматично просувається при операціях читання / запису, тобто файл проглядається послідовно. Є, правда, функції для довільної зміни цієї позиції.

 

Файли мають імена і організовані в ієрархічну деревоподібну структуру з каталогів і простих файлів.

У середовищі UNIX розрізняються різні типи файлів. Така відмінність пов'язана зі способами управління структурою файлів та контро лем за переданням у нього даних.

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

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

Спеціальні файли пристроїв - це файли, що забезпечують передачу даних відповідних пристроїв. Типів таких пристроїв у UNIX чотири: познаковой передачі, передачі по блоках, raw blocks (не буферизованих) і терміналів введення / ви вода. За спеціальним дозволом користувач може отримати доступ на за пись або читання в спеціальні файли за допомогою інструкцій оболонки або че рез системні виклики з програми.

Канали - це файли, що забезпечують напівдуплексному межпроцессное взаємодію. PIPE може містити будь-які типи даних, що поміщаються в них пользовате лем або ОС. Єдине обмеження в них - вони не можуть здобути більше 10 блоків даних. Якщо дані в ці файли поміщаються користувачем, структура такого файлу підвладна контролю з боку користувача. Одна до попередньо таке право треба отримати, використовуючи макроси ініціалізації.

Сокети - це файли, що забезпечують повнодуплексному межпроцессное взаємодію.

Символічна посилання (symbolic link) - це спеціальний файл, який містить шлях до іншого файлу.

 

В ОС UNIX підтримується три способи вказівки імен файлів:

Коротке ім'я. Ім'я, яке не містить спеціальних метасимволів коса риса (/), є коротким ім'ям файлу. За короткому імені можна послатися на файли поточного каталогу.

Відносне ім'я. Ім'я, не починається з символу косою риси (/), але включає такі символи. Воно посилається на файл щодо поточного каталогу.

Абсолютне (повне) ім'я. Ім'я, що починається з символу косою риси (/). Воно посилається на файл щодо кореневого каталогу.

 

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

В ОС UNIX немає теоретичних обмежень на кількість вкладених тек. Тим не менш, у кожній реалізації є практичні обмеження на максимальну довжину імені файлу, що зазначається у командах (як і на довжину командного рядка в цілому).

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

2. Права доступу до файлів

Кожен файл в ОС UNIX містить набір прав доступу, за яким визначається, як користувач взаємодіє з цим файлом. Цей набір зберігається в індексному дескрипторі даного файлу у вигляді цілого значення, з якого зазвичай використовується 12 бітів. Причому кожен біт використовується як перемикач, дозволяючи (значення 1) чи забороняючи (значення 0) той чи інший доступ.

Три перших біта встановлюють різні види поведінки при виконанні. Решта дев'ять діляться на три групи по три, визначаючи права доступу для власника, групи та інших користувачів. Кожна група задає права на читання, запис і виконання.

 

Права доступу можна дізнатися за допомогою команди

ls-l

r - Бит читання для всіх типів файлів має одне і те ж значення: він дозволяє читати вміст файлу (отримувати лістинг каталогу командою ls).

w - Бит запису також має одне і те ж значення: він дозволяє редагувати цей файл. Для каталогу - це можливість змінювати його вміст, тобто створювати і видаляти файли.

x - Якщо для деякого файлу встановлений біт виконання, то файл може виконуватися як команда. У разі встановлення цього біта для каталогу, цей каталог можна зробити поточним (перейти в нього командою cd).

S - Встановлений біт зміни ідентифікатора користувача SUID означає, що доступний користувачеві на виконання файл буде виконуватися з правами (з ефективним ідентифікатором) власника, а не користувача, який викликав файл (як це зазвичай відбувається).

S - Встановлений біт зміни ідентифікатора групи SGID означає, що доступний користувачеві на виконання файл буде виконуватися з правами (з ефективним ідентифікатором) групи-власника, а не користувача, який викликав файл (як це зазвичай відбувається).

Якщо біт SGID встановлений для файлу, не доступного для виконання, він означає обов'язкове блокування, тобто незмінність прав доступу на читання і запис поки файл відкритий певною програмою.

t- Установлений клейкий біт має сенс для каталогу. Файл в цьому каталозі може бути видалений або перейменований тільки в наступних випадках:

− користувачем-власником файла;

− користувачем-власником каталогу;

− якщо файл доступний користувачеві на запис; користувачем root.

 

3. Інтерфейси для файлового введення / виводу

У середовищі програмування UNIX існують два основних інтерфейсу для файлового введення / виводу:

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

2. Стандартна бібліотека вводу / виводу, що пропонує функції буферізірованний введення / виводу.

 

Другий інтерфейс є "надбудовою" над інтерфейсом системних викликів, що пропонує більш зручний спосіб роботи з файлами.

 

Кожен системний виклик - це звернення до ядра операційної системи. Це викликає ряд проблем:

− доступ до диска (читання / запис) набагато (на кілька порядків) повільніше, ніж доступ до даних в оперативній пам'яті,

− при зверненні до ядра відбувається досить велика додаткова робота, яка може значно перевищити корисну роботу,

− системні виклики працюють з файлом як з неструктурованим масивом байт; тоді як зручніше представляти, що файл поділений на рядки.

 

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

 

Системний виклик відбувається в 2 етапи:

1. у користувачевій програмі викликається бібліотечна функція, яка містить команду генерації програмного переривання. Це - головна відмінність від нормальних Сі-функцій - виклик по перериванню.

2. Реакція ядра на переривання:

− перехід в привілейований режим;

− з'ясування, ХТО звернувся до ядра, і переключення контексту відповідного процесу;

− витяг аргументів з пам'яті запросила процесу;

− з'ясування, ЩО ж хочуть від ядра (один з аргументів, невидимий нам - це номер системного виклику);

− перевірка коректності інших аргументів;

− перевірка прав процесу на допустимість виконання такого запиту;

− виклик тіла необхідного системного виклику - це звичайна Сі-функція в ядрі;

− повернення відповіді в пам'ять процесу;

− вимикання привілейованого режиму;

− повернення з переривання.

 

4. Системні виклики роботи з файлами

При програмуванні операцій з файлами мовою Си використовуються бібліотечні виклики мови орієнтовані в першу чергу на операційну систему Unix (для якої мова й розроблявся).

 

Прототипи необхідних для роботи з файлами функцій, використовувані при цьому типи й константи описані в заголовних файлах:

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

 

Для роботи з файлами використовуються наступні функції.

 

Відкриття файлу виконується по функції

int open(const char *path, int flags, mode_t mode);

 

де перший параметр задає ім'я файлу, другий параметр показує, які види доступу до файлу дозволені зухвалому процесу.

 

Параметр flags може приймати наступні значення:

O_RDONLY - відкриття файлу тільки для читання;

O_WRONLY - відкриття файлу тільки для запису;

O_RDWR - відкриття файлу для читання й запису.

 

Значення параметра може логічно складатися з модифікаторми:

O_APPEND - дані додаються в кінець файлу;

O_CREAT - створюється файл, якщо він не існує;

O_TRUNC - якщо файл існує, то його вміст губиться, а розмір

установлюється рівним 0;

O_EXCL - використовується разом із прапором O_CREAT, у

цьому випадку спроба створити файл, якщо він уже

існує, кінчається невдачею.

 

Параметр mode установлює атрибути прав доступу різних категорій користувачів до нового файлу при його створенні. Він обов'язковий, якщо серед заданих прапорів присутній прапор O_CREAT, і може бути опущений у противному випадку. Цей параметр задається як сума наступних восьмеричних значень:

 

0400 - дозволене читання для користувача, що створив файл;

0200 - дозволений запис для користувача, що створив файл;

0100 - дозволене виконання для користувача, що створив файл;

0040 - дозволене читання для групи користувача, що створив файл;

0020 - дозволений запис для групи користувача, що створив файл;

0010 - дозволене виконання для групи користувача, що створив файл;

0004 - дозволене читання для всіх інших користувачів;

0002 - дозволений запис для всіх інших користувачів;

0001 - дозволене виконання для всіх інших користувачів.

 

У стандарті POSIX Ці коди мають ще й мнемонічні імена (використовувані у виклику stat):

Принцип їхнього іменування треба шаблону S_Ipwww, де p визначає режим доступу (R, w або X), a www — кому видається право на цей режим доступу (USR, GRP або OTH).

Наприклад, для восьмеричного числа 755 можна записати:

S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH

 

Існують окремі ідентифікатори для USR, GRP і OTH, які описують повні права доступу. Іменування цих ідентифікаторів треба формі S_IRWXw. Тут символ w визначає, кому видається повне право доступу до файлу (від англ, «whom» - «кому») - U, G або О. Таким чином, що предыдет приклад може бути записаний у наступному виді:

S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH

 

Використання ідентифікаторів дає розроблювачам ОС волю вибору порядку проходження біт, що описують права доступу до файлу.

 

mode завжди повинен бути зазначений при використанні O_CREAT, у всіх інших випадках цей параметр ігнорується.

 

При створенні файлу реально встановлюваного права доступу виходять зі стандартної комбінації параметра mode і маски створення файлів поточного процесу umask, а саме - вони рівні mode & umask.

 

Маска звичайно встановлюється під час реєстрації користувача в системі (командою umask) або системним викликом umask. Фраза «логічне множення на логічне доповнення» означає: якщо якийсь біт маски встановлений, те відповідний йому біт в остаточному наборі прав доступу скидається.

Після успішного відкриття файлу функція повертає значення дескриптора файлу.