Приклад програми, що використовує файлові системні виклики
А тепер розглянемо просту UNIX-програму, яка копіює один файл з файлу-джерела у файл-приймач. Текст програми показаний в лістингу 1.1. У цієї програми мінімальні функціональні можливості, але вона дає досить чітке уявлення про деякі системні виклики, що відносяться до роботи з файлами.
Лістинг 1.1. Проста програма для копіювання файлів
/* Програма для копіювання файлу. Контроль помилок і повідомлення про їх появу зведені до мінімуму. */
#include <sys/types.h> /* включаємо необхідні заготовочні файли*/
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
int main (int argc, char *argv[]); /* ANSI прототип */
#define BUF_SIZE 4096 /* використання буферу розміром 4096 байт */
#define 0UTPUT_M0DE 0700 /* біти забезпечення безпеки для вихідного файлу */
int main(шnt argc, char *argv[])
{
int in_fd, out_fd, rd_count, wt_count;
char buffer[BUF_SIZE];
if (argc != 3) exit(l); /* якщо argc не рівний 3, виникає синтаксична пмилка */
/* Відкриття вхідного и створення вихідного файлу */
in_fd = open(argv[l], 0_RDОNLY); /* відкриття вихідного файлу */
if (in_fd < 0) exit(2); /* якщо він не відкривається, вийти */
out_fd = creat(argv[2], 0UTPUT_M0DE); /* створення файлу-приймача */
if (out_fd < 0) exit(3); /* якщо він не створюється, вийти */
/* Цикл копіювання */
while (TRUE) {
rd_count = read(in_fd, buffer, BUF_SIZE); /* читання блоку даних */
if (rd_count <= 0) break; /* в кінці файлу або при помилці - вийти із циклу */
wt_count = write(out_fd, buffer, rd_count); /* записування даних */
if (wt_count <= 0) exit(4); /* при wt_count <= 0 виникає помилка */
}
/* Закриття файлів */
close(in_fd);
close(out_fd);
if (rd_count ==0) /* при останнім читанні помилки не виникло */
exit(0);
else
exit(5); /* помилка при останньому читанні */
}
Ця програма з іменем copyfile може бути викликана, наприклад, з терміналу: copyfile abc xyz щоб скопіювати файл abc в файл xyz. Якщо файл xyz вже існує, то він буде переписаний. Якщо цей файл не існує, то він буде створений. Програма повинна бути викликана з обов'язковим зазначенням двох аргументів, які є допустимими іменами файлів. Перший аргумент повинен бути ім'ям файлу-джерела, а другий - ім'ям вихідного файлу.
Завдяки чотирьом операторам #include на самому початку програми, в неї включається велика кількість визначень і прототипів функцій. Це потрібно для сумісності програми з відповідними міжнародними стандартами, але більше це нас цікавити не буде. Наступний рядок, згідно з вимогою стандарту ANSI С, містить прототип функції main, але зараз це нас теж не цікавить.
Перший оператор #define є макровизначенням рядка BUF_SIZE як макросу, який при компіляції замінюється в тексті програми числом 4096. Програма буде зчитувати і записувати дані блоками по 4096 байт. Створення таких констант і використання їх замість безпосередньої вказівки чисел в програмі вважається хорошим стилем програмування. При цьому програму не тільки зручніше читати, але і простіше змінювати в разі потреби. Другий оператор #define визначає коло користувачів, які можуть отримати доступ до вихідного файлу.
В основної програми, яка називається main, є два аргументи - argc і argv. Значення цих аргументів присвоюються операційною системою при виклику програми. У першому аргументі вказується кількість строкових значень, присутніх в командному рядку, що викликає програму, включаючи ім'я самої програми. Його значення має бути рівним трьом. Другий аргумент являє собою масив покажчиків на аргументи командного рядка. У наведеному вище прикладі, елементи цього масиву будуть містити покажчики на
наступні значення:
argv[0] = "copyfile"
argv[l] = "abc"
argv[2] = "xyz"
Через цей масив програма отримує доступ до своїх аргументів. У програмі оголошуються п'ять змінних. У перших двох змінних, in_fd і out_fd, будуть зберігатися дескриптори файлів, невеликі цілі числа, що повертаються при відкритті файлу. Наступні дві змінні, rd_count і wt_count, є байтовими лічильниками, що повертаються відповідно процедурами read і write. Остання змінна, buffer, використовується для зберігання зчитаних
та надання записуваних даних.
Перший виконуваний оператор перевіряє, чи не рівне значення лічильника аргументів argc трьом. Якщо лічильник argc не дорівнює трьом, програма завершується з кодом 1. Будь-який код завершення програми відмінний від 0 означає, що сталася помилка. Єдиний застосовуваний в цій програмі спосіб повідомлення про помилки - це код завершення програми. Остаточний варіант цієї програми виводив би повідомлення про помилки.
Потім програма намагається відкрити вхідний файл і створити вихідний файл. Якщо відкриття файлу проходить успішно, операційна система привласнює змінній in_fd невелике цілочисельне значення, щоб ідентифікувати файл. Це ціле число може включатися в наступні виклики, щоб система знала, який файл їм потрібен. Аналогічно цьому, якщо успішно створюється вихідний файл, змінній out_fd також присвоюється значення, що його ідентифікує. Другий аргумент процедури creat встановлює код захисту створюваного файлу. Якщо не вдається відкрити файл або не вдається його створити, то значення відповідного дескриптора файлу встановлюється в -1 і здійснюється вихід з програми з відповідним кодом помилки.
Потім настає черга циклу копіювання, який починається з спроби
зчитати в буфер buffer 4 Кбайт даних. Це робиться шляхом виклику бібліотечної процедури read, яка насправді здійснює системний виклик read. Перший параметр ідентифікує файл, другий вказує буфер, а третій повідомляє, скільки байтів потрібно зчитати. Значення, присвоєне rd_count, дає кількість реально зчитаних байтів. Зазвичай це значення дорівнює 4096, за винятком того випадку, коли у файлі залишиться менше байтів. При досягненні кінця файлу це значення дорівнюватиме 0. Як тільки значення rd_count стане нульовим або
негативним, копіювання не зможе продовжуватися, тому для припинення циклу (Який в іншому випадку був би нескінченним) виконується оператор break. Звернення до процедури write приводить до виводу вмісту буфера в вихідний файл. Перший параметр ідентифікує файл, другий вказує буфер, а третій повідомляє, скільки байтів потрібно записати, аналогічно параметрам процедури read. Слід зауважити, що лічильник байтів містить кількість реально
зчитаних байтів, а не значення BUF_SIZE. Це важливо, оскільки при останньому зчитуванні не буде повернуте число 4096, якщо тільки довжина файлу не виявиться кратній 4 Кбайт.
Після обробки всього файлу при першому ж виклику, що виходить за межі
файлу, в rd_count буде повернуто значення 0, яке і змусить програму вийти з циклу. Після цього обидва файли закриваються, і виконається вихід з програми зі статусом, що свідчить про її нормальне завершення.
Незважаючи на те що системні виклики Windows відрізняються від системних
викликів UNIX, загальна структура програми Windows, призначеної для
копіювання файлів і запускається з командного рядка, приблизно така ж, як
у програми, показаної в лістингу 1.1.
Дата добавления: 2015-04-03; просмотров: 864;