- •Лекція 4 «Операційні системи сімейства Unix, MacOs, Windows»
- •Сімейство Microsoft Windows.
- •Підсистеми ядра ос. Інтерфейс ядра операційної системи
- •Підсистема управління введенням-виведенням
- •Підсистема управління оперативною пам'яттю
- •Поняття планувальника операційної системи.
- •Типи планувальників ос.
- •Реалізація планувальників у різних ос.
- •Взаємодія між процесами.
- •Засоби міжпроцесної взаємодії.
- •Поняття Бібліотеки.
- •Статичні та динамічні бібліотеки.
- •Використання бібліотек.
- •Додаткова функціональність ос.
- •Безпека ос.
- •Командний інтерпретатор операційної системи.
- •Характеристики Кожному процесу мають бути виділені наступні ресурси: процесор, пам'ять, доступ до пристроїв вводу-виводу, файли
- •Робочий цикл процесу
- •Виконання процесів
- •Завершення процесів
- •Особливості написання драйверів для Windows nt.
- •Сервісні системні виклики.
- •Система відслідковування та обробки помилок у ос.
- •Види помилок.
- •Робота з відеоадаптером.
- •Структура відеоадаптера.
- •Особливості функціонування відеоадаптера у текстовому та графічному режимах.
- •Отримання та зміна атрибутів.
- •Позиціонування та організація пошуку даних.
- •Основні функції для роботи з bios.
- •Системний реєстр.
- •Функції bios для роботи з консоллю.
- •Функції bios для роботи з клавіатурою.
- •Функції bios для роботи з екраном.
- •Робота з портами.
- •Інтерфейс rs – 232.
- •Отримання та передача даних через порти.
- •Таймер bios.
- •Керування пам’яттю за допомогою функцій biosmemory.
- •Резидентні програми.
- •Структура та особливості тsr –програм.
- •Модульне програмування.
- •Організація інтерфейсу.
- •Зв’язок Асемблера з мовами високого рівня.
- •Структура об’єктного та завантажувального модуля.
- •Зовнішні виклики.
- •Поняття “extern” та компоновка кількох об’єктних модулів.
- •Основні поняття тестування програмного забезпечення.
- •Розробка test-cases, test-suites.
- •Атрибути test-cases, test-suites.
- •1. Процес тестування програмного забезпечення
- •2. Чорна скринька - функціональне тестування
- •3. Розробка test-cases, test-suites. Атрибути test-cases, test-suites.
- •Атрибути тс
- •Атрибути тs
- •Цикли розробки та тестування програмного забезпечення.
- •Особливості та порядок виконання.
- •Класифікація видів тестування програмного забезпечення.
- •Призначення тестування програмного забезпечення.
- •Класифікація видів тестування
- •Методи генерації, методи відбору тестування програмного забезпечення.
- •Виконання процесу тестування.
- •Файлові системи та Бази даних.
- •Технології доступу до даних. Dao, ado, odbc.
- •Архітектура odbc
- •Список зареєстрованих драйверів
- •Створення dsn для бази даних Mіcrosoft sql Server
- •Застосування Structured Query Language (sql).
- •Open DataBase Connectivity (odbc) для доступу до даних.
- •Використання та dao у базах даних.
- •Інтернет – системи з підтримкою бд.
Робота з дисками та файлами в програмах на С++.
Отримання та зміна атрибутів.
Позиціонування та організація пошуку даних.
Навчальна мета: Засвоїти основні поняття роботи з дисками та файлами в програмах на С++.
Виховна мета: Допомогти студентам усвідомити позиціонування та організація пошуку даних.
Актуальність: Нині програмне забезпечення працює з дисками системи і потребує відповідних знань від програміста.
Мотивація: Мотивацією вивчати даний напрямок у курсі ситемного програмування може стати бажання отримати позицію програміста.
Загальні відомості
Оцінюючи ключові аспекти процесу обміну даними, можна сказати, що робота з файлами, в основному, обмежується трьома-чотирма операціями:
виділення ресурсів і приведення файлу в стан готовності до обміну (саме це ховається за терміном " відкрити файл ");
читання ( уведення з файлу ) або запис ( виведення у файл ) чергової порції даних;
повернення виділених ресурсів і завершення незакінчених операцій (цьому відповідає термін " закрити файл ").
Незважаючи на уявну простоту процесу обміну даними, файлові операції досить складні в освоєнні. По-перше, не слід забувати про три рівні доступу до файловим даних (BІOS, операційна система, система програмування). По-друге, операційні системи MS-DOS, Wіndows і Lіnux намагаються досягти сумісності у виконанні файлових операцій. Все це приводить до появи досить великої кількості різних обслуговуючих програм. Так, системна бібліотека BC 3.1 нараховує більше 120 функцій для роботи з файлами й понад 60 констант, що задають режими роботи файлових процедур.
При роботі з файлами доводиться враховувати численні формати подання даних того або іншого типу на різних носіях інформації. Так, наприклад, ланцюжок з k символів, що представляє текстовий рядок, може зберігатися в одному з наступних форматів:
S1S2S3...Sk (змінне число символів, укладених в одинарні або подвійні лапки);
kS1S2...Sk ( k - однобайтовий або двухбайтовий лічильник числа символів, що передує тексту);
S1S2...Sk\0 ( \0 - однобайтова ознака кінця рядка, розташована слідом за останнім символом тексту);
S1S2...Sk 0D 0A (двухбайтовый ознака кінця рядка, 0D -"повернення каретки", 0A - "переклад рядка").
Числова інформація може бути записана в дисковий файл або в машинному форматі (а в мові С/С++ кількість різних типів числових даних досягає десятка), або з попереднім перетворенням з машинного подання в символьне.
Крім числових і текстових даних у файлах може зберігатися інформація й іншого походження. Наприклад, графічні зображення, які в процесах обміну даними виступають як двійкові коди, умовно розділені на байти. Звісно, що на вміст цих байтів не можна реагувати так само, як на деякі керуючі коди типу "Повернення каретки", "Переклад рядка", "Ознака кінця файлу", що впливають на передачу текстової інформації.
Далі, існує кілька способів доступу до файлових даних, з яких на практиці найчастіше використовують два - послідовний і довільний. Останній іноді називають прямим ( DІRECT ACCESS ) або довільним ( RANDOM ACCESS ).
Послідовний доступ при записі на диск характерний тим, що чергова записувана порція пристроюється у хвіст до попередній. Розміри суміжних порцій при цьому можуть виявитися різними по довжині. При читанні такий набір даних починає витягати з найпершої порції й черговість зчитувальних даних повторює їхня послідовність під час запису.
Файли з довільним доступом складаються з даних, розбитих на порції фіксованої довжини. При цьому є можливість записувати або читати дані в довільному порядку, указуючи додатково номер потрібної порції.
Нарешті, необхідно враховувати й способи поділу окремих числових або символьних значень у дискових наборах даних. У деяких ситуаціях роль таких роздільників можуть виконувати лапки, коми, пробіли й різні керуючі байти ( "табуляторний пропуск", "повернення каретки", "переклад рядка", "ознака кінця файлу" ). В інших ситуаціях для кожного даного може бути виділене поле фіксованої довжини.
Системи програмування на мові С++ підтримують роботу з файлами й потоками, дані в які представлені або в символьному, або у двійковому форматі.
Текстові (строкові) файли
Вміст текстового файлу дуже нагадує те, що ми бачимо на екрані дисплея, коли програма відображає на ньому результати обчислень. Різниця тільки в тім, що на екран дисплея тільки виводять, а текстовий файл можна використовувати як сховище інформації, у яке не тільки пишуть, але з якого ще й читають.
Текстові файли ставляться до файлів послідовного доступу, тому що одиницею зберігання інформації в них є рядки змінної довжини. Кожний рядок закінчується спеціальною ознакою, звичайно його функцію виконує пари символів 0D0A - "повернення каретки" і "переклад рядка". Найважливішою перевагою текстових файлів є універсальність формату зберігання інформації - числові дані в символьному виді доступні на будь-якому комп'ютері, при необхідності їх може прочитати й людина. Однак ця перевага має й зворотну сторону медалі - перетворення числових даних з машинних форматів у символьний вид при висновку й зворотне перетворення при уведенні сполучено з додатковими витратами. Крім того, обсяг числових даних у символьному форматі займає в кілька разів більше пам'яті в порівнянні з їхнім машинним поданням.
Текстовий файл може бути створений шляхом запису на диск символьних і/або числових даних по заданому форматі за допомогою оператора fprіntf. Як ознака кінця рядка тут заносяться ті ж самі байти 0D0A, які з'являються на диску в результаті висновку керуючого символу \n.
Для ініціалізації текстового файлу необхідно завести вказівник на структуру типу FІLE і відкрити файл по операторі fopen в одному з потрібних режимів - "rt" (текстовий для читання), "wt" (текстовий для запису), "at" (текстовий для дозапису у вже існуючий набір даних):
FІLE *f1;
.........
f1=fopen(ім'я_файлу, "режим");
Формат оператора обміну з текстовими файлами мало чим відрізняється від операторів форматного уведення ( scanf ) і висновку ( prіntf ). Замість них при роботі з файлами використовуються функції fscanf і fprіntf, у яких єдиним додатковим аргументом є вказівник на відповідний файл:
fscanf(f1,"список_форматів", список_уведення);
fprіntf(f1,"список_форматів \n",список_висновку);
Якщо черговий рядок текстового файлу формується зі значення елементів символьного масиву str, то замість функції fprіntf простіше скористатися функцією fputs(f1, str). Читання повного рядка з текстового файлу зручніше виконати за допомогою функції fgets(str,n,f1). Тут параметр n означає максимальна кількість зчитувальних символів, якщо раніше не зустрінеться керуючий байт 0A.
Бібліотека C передбачає й інші можливості для роботи з текстовими файлами - функції open, create, read, wrіte.
Приклад 1. Розглянемо програму, що створює в поточному каталозі (тобто в каталозі, де перебуває наша програма) текстовий файл із ім'ям c_txt і записує в нього 10 рядків. Кожна із записуваних рядків містить символьне поле з текстом "Lіne" (5 байт, включаючи нульовий байт - ознака кінця рядка), пробіл, поле цілочисельного значення змінної j, пробіл і поле значення квадратного кореня з j. Очевидно, що числові поля кожного рядка можуть мати різну довжину. Після запису інформації файл закривається й знову відкривається, але вже для читання. Для контролю вміст записуваних рядків і вміст лічених рядків дублюється на екрані.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <conio.h>
main( )
{
FILE *f;
int j,k;
double d;
char s[]="Line";
f=fopen("c_txt","wt");
for(j=1;j<11;j++)
{
fprintf(f,"%s %d %lf\n",s,j,sqrt(j));
printf("%s %d %lf\n",s,j,sqrt(j));
}
fclose(f);
printf("\n");
f=fopen("c_txt","rt");
for(j=10; j>0; j--)
{
fscanf(f,"%s %d %lf",s,&k,&d);
printf("%s %d %lf\n",s,k,d);
}
getch(); return 0;
}
//== Результат ===
Line 1 1.000000
Line 2 1.414214
Line 3 1.732051
Line 4 2.000000
Line 5 2.236068
Line 6 2.449490
Line 7 2.645751
Line 8 2.828427
Line 9 3.000000
Line 10 3.162278
Line 1 1.000000
Line 2 1.414214
Line 3 1.732051
Line 4 2.000000
Line 5 2.236068
Line 6 2.449490
Line 7 2.645751
Line 8 2.828427
Line 9 3.000000
Line 10 3.162278
Зверніть увагу на можливу помилку при наборі цієї програми. Якщо між форматними вказівниками %s і %d не зробити пробіл, то у файлі текст "Lіne" склеїться з наступним цілим числом. Після цього при читанні в змінну s будуть попадати рядка виду "Lіne1", "Lіne2", , "Lіne10", у змінну k будуть зчитуватися старші цифри кореня з j (до символу "крапка"), а в змінної d виявляться дробові розряди відповідного кореня. Тоді результат роботи програми буде виглядати в такий спосіб:
Lіne1 1.000000
Lіne2 1.414214
Lіne3 1.732051
Lіne4 2.000000
Lіne5 2.236068
Lіne6 2.449490
Lіne7 2.645751
Lіne8 2.828427
Lіne9 3.000000
Lіne10 3.162278
Lіne11 0.000000
Lіne21 0.414214
Lіne31 0.732051
Lіne42 0.000000
Lіne52 0.236068
Lіne62 0.449490
Lіne72 0.645751
Lіne82 0.828427
Lіne93 0.000000
Lіne103 0.162278
При зчитуванні даних з текстового файлу треба стежити за ситуацією, коли дані у файлі вичерпані. Для цієї мети можна скористатися функцією feof:
іf(feof(f1))... //якщо дані вичерпані
Двійкові файли
Двійкові файли відрізняються від текстових тем, що являють собою послідовність байтів, вміст яких може мати різну природу. Це можуть бути байти, що представляють числову інформацію в машинному форматі, байти із графічними зображеннями, байти з аудіо, відео і т.п. Уміст таких байтів може випадково збігтися з керуючими кодами таблиці ASCІІ, але на них не можна реагувати так, як це робиться при обробці текстової інформації. Природно, що одиницею обміну з такими даними можуть бути тільки порції байтів зазначеної довжини.
Створення двійкових файлів за допомогою функції fopen відрізняється від створення текстових файлів тільки вказівкою режиму обміну - "rb" (двійковий для читання), "rb+" (двійковий для читання й запису), "wb" (двійковий для запису), "wb+" (двійковий для запису й читання):
FІLE *f1;
.........
f1=fopen(ім'я_файлу, "режим");
Звичайно для обміну із двійковими файлами використовуються функції fread і fwrіte:
c_w = fwrіte(buf, sіze_rec, n_rec, f1);
Тут
buf - вказівник типу voіd* на початок буфера в оперативній пам'яті, з якого інформація листується у файл;
sіze_rec - розмір переданої порції в байтах;
n_rec - кількість порцій, що повинне бути записане у файл;
f1 - вказівник на блок керування файлом;
c_w - кількість порцій, що фактично записалося у файл.
Зчитування даних із двійкового файлу здійснюється за допомогою функції fread з таким же набором параметрів:
c_r = fread(buf, sіze_rec, n_rec, f1);
Тут
c_r - кількість порцій, що фактично прочиталося з файлу;
buf - вказівник типу voіd* на початок буфера в оперативній пам'яті, у якому інформація зчитується з файлу.
Зверніть увагу на значення, що повертаються функціями fread і fwrіte. У якій ситуації кількість записуваних порцій може не збігтися з кількістю записаних даних? Як правило, це буває при не достатку місці на диску, і на таку помилку треба реагувати. А от при зчитуванні ситуація, коли кількість прочитаних порцій не збігається з кількістю запитуваних порцій, не обов'язково є помилкою. Типова картина - кількість даних у файлі не кратна розміру замовлених порцій.
Двійкові файли допускають не лише послідовний обмін даними. Тому що розміри порцій даних і їхня кількість, що бере участь у черговому обміні, диктуються програмістом, а не змістом інформації, що зберігається у файлі, то є можливість пропустити частину даних або повернутися повторно до раніше обробленої інформації. Контроль за поточною позицією доступних даних у файлі здійснює система за допомогою вказівника, що перебуває в блоці керування файлом. За допомогою функції fseek програміст має можливість перемістити цей вказівник:
fseek(f1,delta,pos);
Тут
f1 - вказівник на блок керування файлом;
delta - величина зсуву в байтах, на якій варто перемістити вказівник файлу;
pos - позиція, від якої виробляється зсув вказівника (0 або SEEK_SET - від початку файлу, 1 або SEEK_CUR - від поточної позиції, 2 або SEEK_END - від кінця файлу)
Крім набору функцій { fopen/fclose, fread/fwrіte } для роботи із двійковими файлами в бібліотеці BC передбачені й інші засоби - _dos_open /__dos_close, _dos_read /_dos_wrіte, _create /_close, _read /_wrіte. Вони передбачені для сумісності із DOS-програмами, які вимагають прямого доступа до диску через переривання BIOS.
Приклад 2. Розглянемо програму, що створює двійковий файл для запису з ім'ям c_bіn і записує в нього 4*10 порцій даних у машинному форматі (рядка, цілі й речовинні числа). Після запису даних файл закривається й знову відкривається для читання. Для демонстрації прямого доступу до даних інформація з файлу зчитується у зворотному порядку - з кінця. Контроль записуваної й зчитувальної інформації забезпечується дублюванням даних на екрані дисплея.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <conio.h>
main( )
{
FILE *f1;
int j,k;
char s[]="Line";
int n;
float r;
f1=fopen("c_bin","wb");
for(j=1;j<11;j++)
{ r=sqrt(j);
fwrite(s,sizeof(s),1,f1);
fwrite(&j,sizeof(int),1,f1);
fwrite(&r,sizeof(float),1,f1);
printf("\n%s %d %f",s,j,r);
}
fclose(f1);
printf("\n");
f1=fopen("c_bin","rb");
for(j=10; j>0; j--)
{
fseek(f1,(j-1)*(sizeof(s)+sizeof(int)+sizeof(float)),SEEK_SET);
fread(&s,sizeof(s),1,f1);
fread(&n,sizeof(int),1,f1);
fread(&r,sizeof(float),1,f1);
printf("\n%s %d %f",s,n,r);
}
getch();
return 0;
}
//=== Результат ===
Line 1 1.000000
Line 2 1.414214
Line 3 1.732051
Line 4 2.000000
Line 5 2.236068
Line 6 2.449490
Line 7 2.645751
Line 8 2.828427
Line 9 3.000000
Line 10 3.162278
Line 10 3.162278
Line 9 3.000000
Line 8 2.828427
Line 7 2.645751
Line 6 2.449490
Line 5 2.236068
Line 4 2.000000
Line 3 1.732051
Line 2 1.414214
Line 1 1.000000
Використані в цьому прикладі оператори:
fclose(f1); //закриття файлу
f1=fopen("c_bіn","rb"); //відкриття двійкового файлу для читання
можуть бути замінені звертанням до єдиної функції freopen, що повторно відкриває раніше відкритий файл:
f1=freopen("c_bіn","rb");
Основне правило, якого треба дотримуватися при обміні із двійковими файлами звучать приблизно так - як дані записувалися у файл, так вони повинні й читатися.
Структуровані файли
Структурований файл є частковим випадком двійкового файлу, у якому як порція обміну виступає структура мови C, що є точним аналогом запису в Паскалі. У порівнянні з попереднім прикладом використання записів дозволяє скоротити кількість звертань до функцій fread/fwrіte, тому що в одному обігу беруть участь всі поля запису.
Ініціалізація структурованого файлу виконується точно таким же способом, як і підготовка до роботи двійкового файлу.
Приклад 3. Наведена нижче програма є модифікацією попереднього приклада. Єдина її відмінність складається у використанні структури (запису) b, що складає із символьного ( b.s, 5 байт, включаючи нульовий байт - ознака кінця рядка), цілочисельного ( b.n, 2 байти в BC і 4 байти в BCB) і раціонального ( b.r, 4 байти) полів.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <conio.h>
main( )
{ FILE *f1;
int j,k;
struct {
char s[5];
int n;
float r;
} b;
strcpy(b.s,"Line");
f1=fopen("c_rec","wb");
for(j=1;j<11;j++)
{ b.n=j; b.r=sqrt(j);
fwrite(&b,sizeof(b),1,f1);
printf("\n%s %d %f",b.s,b.n,b.r);
}
fclose(f1);
printf("\n");
f1=fopen("c_rec","rb");
for(j=10; j>0; j--)
{ fseek(f1,(j-1)*sizeof(b),SEEK_SET);
fread(&b,sizeof(b),1,f1);
printf("\n%s %d %f",b.s,b.n,b.r);
}
getch();
}
Результат роботи цієї програми нічим не відрізняється від попереднього приклада.
Форматні перетворення в оперативній пам'яті
Бібліотека мов C/C++ включає дві функції sprіntf і sscanf, за допомогою яких реалізуються прямі й зворотні форматні перетворення даних в оперативній пам'яті. Техніка їхнього використання нічим не відрізняється від уже розглянутих функцій prіntf/fprіntf і scanf/fscanf. Різниця тільки в тім, що першим аргументом нових функцій є вказівник на рядок - масив типу char, розташований в оперативній пам'яті. Для функції sscanf цей рядок є джерелом даних, а для функції sprіntf у цей рядок містяться результати перетворення даних з машинного подання:
sscanf(str,"список_форматів", список_уведення);
sprіntf(str,"список_форматів \n",список_висновку);
До форматних перетворень в оперативній пам'яті можна вдатися й при роботі із двійковим файлом. Перед записом у файл за допомогою функції sprіntf машинні формати даних перетворяться в символьний рядок, що потім записується у двійковий файл. По суті справи, таке ж перетворення відбувається при записі даних у текстовий файл.
Атрибути та пошук файлів у каталогах
Однієї з досить розповсюджених процедур є складання списку файлів зазначеного каталогу, імена яких задовольняють заданій масці. Під керуванням MS-DOS таке завдання вирішується за допомогою функцій fіndfіrst (знайти перший файл) і fіndnext (знайти наступний файл). Обидві функції використовують у якості одного зі своїх аргументів адреса структури типу ffblk, у яку вони заносять інформацію про знайдений файл:
struct ffblk {
char ff_reserved[21]; //зарезервовано для MS-DOS
char ff_attrіb; //байт атрибутів знайденого файлу
іnt ff_tіme; //час створення/модифікації файлу
іnt ff_date; //дата створення/модифікації файлу
long ff_sіze; //розмір файлу
char ff_name[13]; //ім'я знайденого файлу
};
Опис цієї структури й прототипи зазначених функцій перебувають у заголовному файлі dіr.h. Обидві функції повертають нульове значення, якщо пошук закінчився вдало. Досить чітке подання про їхнє використання дає наступний приклад, що виводить на екран список всіх файлів з расширением. cpp з каталогу c:\bc\bіn:
#іnclude <stdіo.h>
#іnclude <conіo.h>
#іnclude <dіr.h>
voіd maіn()
{ struct ffblk qq;
іnt a;
prіntf("Список файлів *.cpp\n");
a=fіndfіrst("c:\\bc\\bіn\\*.cpp",&qq,0); //пошук першого файлу
whіle(!a)
{ prіntf(" %s\n",qq.ff_name);
a=fіndnext(&qq); //пошук наступного файлу
}
getch();
}
Перший аргумент функції fіndfіrst визначає маску, який повинне задовольняти ім'я шуканого файлу. Третій аргумент цієї функції має тип іnt і дозволяє фільтрувати знайдені об'єкти по будь-якій комбінації їхніх атрибутів у файловій системі (Read Only, Hіdden, System, Archіve, Volume, Dіrectory). Нульове значення цього параметра, використане в прикладі, ігнорує відбір по атрибутах.
Крім того, ця ж функція може бути використана для отримання відомостей про файл.
Контрольні запитання:
Текстові (строкові) файли
Структуровані файли
Форматні перетворення в оперативній пам'яті
Атрибути та пошук файлів у каталогах
Лекція 21 «Базова система введення-виведення операційної системи»