Студопедия
Случайная страница | ТОМ-1 | ТОМ-2 | ТОМ-3
АрхитектураБиологияГеографияДругоеИностранные языки
ИнформатикаИсторияКультураЛитератураМатематика
МедицинаМеханикаОбразованиеОхрана трудаПедагогика
ПолитикаПравоПрограммированиеПсихологияРелигия
СоциологияСпортСтроительствоФизикаФилософия
ФинансыХимияЭкологияЭкономикаЭлектроника

- системне ансамблювання – переводить початковий файл з команди в завантажувальний;



1.До задач СП відносять:

- системне ансамблювання – переводить початковий файл з команди в завантажувальний;

- завантажувачі – абсолютний – записує об’єктну програму в ОП і передає управління на адресу початку її виконання; зв’язний – забезпечує переміщення програми в б-яку обл..памяті

- макропроцесори – дозволяють групи команд що часто повторюються замінювати однією макроконструкцією;

- ОС – інтерфейс;

- драйвер – робота периферійних пристроїв.

2. Класична структура програми має таку структуру:


1) реєстрація класу вікна чи вікон. Здійснюється функцією RegisterClassA(). Дана функція має єдиний параметр, який є вказівником на структуру WNDCLASS. В цій структурі міститься вся інформація про те, яке саме вікно буде створюватись;

2) створення головного вікна. Здійснюється функцією CreateWindowA() або CreateWindowExA();

3) створення циклу обробки черги повідомлень. Вилучення повідомлень з черги і спрямування їх відповідним вікнам здійснює функція WinMain. Цей процес відбувається так:

- повідомлення вибирається з черги з допомогою функції GetMessage чи PeekMessage;

- потім повідомлення транслюється з допомогою функції TranslateMessage;

- потім його надсилають вікну з допомогою функції DispatchMessage (диспетчеризація).

Як правило, всі сучасні пакети підтримують спрощену сегментацію, яка значно спрощує письмовий об’єм програми. Цими сегментами є:. DATA – містить ініціалізовані дані програми;.DATA? – містить неініціалізовані дані програми;.CONST – містить константи програми;.COD – містить код програми; Є такі моделі пам’яті: 1) Tiny – дуже маленька – дані і код після компіляції знаходяться тільки в 1 сегменті.2) Small – маленька – дані і код після компіляції знаходяться в 1 сегменті, але в програмному коді можуть бути розділені3) Compact – код знаходиться в 1 сегменті, а кожна змінна фактично в своєму власному сегменті. Застосовують переважно при роботі з відео. 4) Medium – протилежна до Compact. Для хорошу оптимізацію при великих обсягах прогр. коду; 5) Large – розподіл коду по сегментах відбув. за вибором програміста;6) Flat – програма взагалі не ділиться на сегменти, але може містити великі обсяги даних.

3. Вказівники можуть мати конкретний тип незалежно від вибраної моделі пам’яті.

- near (16 біт) – ближчі. При їх використанні для обчислення адреси використовують 1 сегментний регістр. Легко з ним працювати, але придатний для локал. програм. Вони визначають адреси об’єктів у конкретному сегменті, оскільки near-вказівник зберігає лише зміщення повної адреси.



- far (32 біти) – дальні. Містять і адреси самого сегменту (на відміну від near) та зміщення, дозволяють мати декілька сегментів коду і можуть використ. в більших програмах.

- huge (32 біти) – нормалізовані. На відміну від far-вказівників, huge-вказівники містять лише нормалізовані адреси, тобто адреса довільного об’єкта в пам’яті відтворена єдиним сполученням значень його сегментованої частини та зміщення. Тому їх використовують при порівняннях. Але їм треба більше затрат часу і працюють повільніше в решті арифм. операцій.

4. Консольні програми є дуже зручними, коли не цікавить інтерфейс з користувачем і треба виконувати великі об’єми робіт (обчислень). При створенні консолей бажано створювати свою власну консоль AllocConsole().

При завершенні роботи програми всі консолі автоматично звільняються. Проте це можна робити примусово – FreeConsole(). Рекомендовано використ. на початку кожної консольної програми.

Щоб отримати ідентифікатор консольного вікна, використ. функцію GetStdHandle(). Аргумент: STD_INPUT_HANDLE (equ -10),STD_OUTPUT_HANDLE (equ -11),STD_ERROR_HANDLE (equ -12) Для читання з буфера консолі використ. ReadConsole(). Заголовок вікна консолі – SetConsoleTitle(). Встановлення позиції курсору в консолі – SetConsoleCursorPosition(). Має 2 параметри: 1-й – дескриптор вхідного буфера консолі, 2-й – вказівник на структуру COORD STRUC: COORD STRUC X WORD? Y WORD? СOORD ENDS Встановлення кольору тексту – ConsoleTextAttribute(). Встановлення розмірів консолі – SetConsoleScreenBufferSize(). 5. Для отримання інформації про подію від клавіатури чи мишки є функція ReadConsoleInput(). Має 4 параметри: 1-й – дескриптор вхідного буфера консолі, 2-й – вказівник на структуру чи їх масив, 3-й – кількість структур, 4-й – вказівник на подвійне слово, що містить к-сть реально отриманих структур.Саме 4-ий параметр (його молодше слово) визначає тип події. Зарезервовано 5 типів подій:KEY_EVENT (equ 1h), MOUSE_EVENT (equ 2h)WINDOW_BUFFER_SIZE_EVENT (equ 4h), MENU_EVENT (equ 8h)FOCUS_EVENT (equ 10h) KEY_EVENT: +4 – натиснення клавіші +8 – кількість повторів при натисненні клавіші+10 – віртуальний код клавіші +12 – скан-код клавіші+14 – молодший байт = ASCII-коду клавіші +16 – міститься стан керуючих клавіш:

· RIGHT_ALT_PRESSED equ 1h;

· LEFT_ALT_PRESSED equ 2h;

· RIGHT_CTRL_PRESSED equ 4h;

· LEFT_CTRL_PRESSED equ 8h;

· SHIFT_PRESSED equ 10h;

· NUMLOCK_ON equ 20h;

· SCROLLLOCK_ON equ 40h;

· CAPSLOCK_ON equ 80h;

· ENHANCED_KEY equ 100h.

MOUSE _EVENT: +4 – молодше слово – Х-координата мишки, старше - У-координата+8 – описує стан кнопок мишки: 1-й – ліва, 2-й – права, 3-й – середня (біти нумеруються 0-2). Якщо біт = 1, кнопка нажата+12 – стан керуючих клавіш (аналогічно +16 в KEY_EVENT) +16 – містить 2 знач.

· MOUSE_MOV equ 1h; //був рух мишки

· DOUBLE_CL equ 2h; //був подвійний клік

по зміщенню +4 знаходиться подвійне слово, яке містить новий розмір консольного вікна. Молодше слово - розмір по X, старше слово - розмір по Y. Коли йде мова про консольне вікно, всі розміри і координати даються в "символьних" одиницях.

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

- завантажується в пам’ять лише при зверненні до нього;

- підтримуються системою автоматично;

Опис ресурсів зберігається в текстовому файлі *.rc і компілюється транслятором ресурсів rc.exe в файл з розширенням *.res. У виконавчий файл ресурс підключається компоновщиком LINK/subsytem:windows *.obj *.res

Піктограми. Можуть розміщатись як в файлі ресурсів, так і в окремому *.ico-файлі. Додавання піктограми у файлі ресурсів:

#define HM_ICON1 1 //ідентифікатор піктограми

HM_ICON1 ICON "rl.ico" //ідентифікатор файлу

Завантаження піктограми в програмі:

PUSH HM_APPLICATION

PUSH 1

CALL LoadIcon@8

MOV [WC.CLSHICON], EAX Курсори, Бітові малюнки. Аналогічно:

Завантажуються рядки з файлу функцією LoadString().

7. Діалогове вікно не містить ідентифікатора. Звертання до діалогу відбув. за його іменем.

Діалогове вікно дуже похоже на звичайне вікно. Воно має свою процедуру, яка має ті самі параметри, що і процедура звичайного вікна. Замість повідомлення WM_CREATE приходить повідомлення WM_INITDIALOG.

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

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

Опис в файлі ресурсів:

MENUP MENU { POPUP "&Первый пункт" { MENUITEM "&Первый", 1 MENUITEM "В&торой", 2 } } & - виклик клавішами.Пункти меню можуть містити додаткові параметри, які визначають додаткові властивості цих пунктів:

· CHECKED – пункт відмічений галочкою;

· GRAYED – елемент недоступний (має сірий колір);

· HELP – пункт може бути зв’язаний з допомогою;

· MENUBARBREAK – для горизонтального пункту це означає, що поначинаючи з нього горизонтальні пункти розміщуються в новій стрічці. Для вертикального пункту – в новому стовпці. При цьому може бути розділова лінія;

· MENUBREAK - аналогічно попередньому, але розділової лінії немає;

· INACTIVE - неактивний;

На початку роботи програми MENU треба завантажити LoadMenu() і встановити SetMenu().

 

8. Акселератори. Дозволяють вибрати меню просто комбінацією клавіш. Таблиця акселераторів є ресурсом, ім’я якого має збігатись з ім’ям меню, для якого вона призначена.

Ім’я ACCELERATORS{ Клавіша N, ідентифікатор пункту меню (N) [, тип] [, параметр]}Клавіша – це або символ в «», або ASCII-код, або віртуальна клавіша. Якщо спочатку стоїть код символу, то тип задається як ASCII. Якщо використ. віртуальна клавіша, то тип є VIRTUAL. Параметр може приймати значення:- NOINVERT – вибраний пункт меню не буде підсвічуватись- ALT- CONTROL - крім заданих клавіш, треба щоб була нажата ще одна клавіша- SHIFTПри роботі з акселераторами треба:1) завантажити таблицю акселераторів LoadAccelerators();2) повідомлення, яке прийшло від акселератора, треба перетворити в повідомлення WM_COMMAND. Для цього використ. функцію TranslateAccelerator(). Дана функція перетворює повідомлення WM_KEYDOWN і WM_SYSKEYDOWN в повідомлення WM_COMMAND і WM_SYSCOMMAND відповідно. При цьому в старшому слові параметра WPARAM міститься 1, що є ознакою акселератора. В молодшому слові міститься ідентифікатор пункту меню. WM_COMMAND і WM_SYSCOMMAND потрібні для виведення повідомлення.Функція TranslateAccelerator() повертає ненульове значення, якщо перетворення повідомлення було здійснено вдало, інакше NULL. Виклик функції включають в цикл обробки повідомлень: Створення немодального - CreateDialog. Знищення немодального - DestroyWindow. Відображення на екрані немодального вікна – треба вказати йому властивість WS_VISIBLE або після створення діалогу вказати ф-ю ShowWindow.

9. Windows підтримує 3 типи контекста дисплея: контекст класа, приватний і загальний. Перші два використ. в додатках, які виводять на екран велику кількість інформації (відео, графіка). Додатки, які не досить інтенсивно працюють з екраном, використ. загальний контекст. Контекст класу є застарілим і підтримується тільки щоб забезпечувати сумісність роботи старих версій Windows. Не рекомендується його використ. при розробці нових додатків і рекомен. використ. тільки приватний контекст.

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

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

При роботі з контекстами необхідно запам’ятати, що дескриптори контексту за допомогою функції BeginPaint() необхідно отримувати тільки в випадку обробки повідомлення WM_PAINT. У всіх випадках треба використ. функції GetDC() чи GetDCEx().

10. Інформ. контекст – ще одна проміжна ланка. Не є фактично контекстом певного пристрою і служить тільки для отримання інформації про реальний контекст пристрою. Створюється функцією CreateDC. Працює набагато швидше за реальні контексти. Видаляється функцією DeleteDC.

11. Контекст в пам'яті використ. для збереження зображень, що потім будуть скопійовані на пристрій виводу. Сам по собі цей контекст не створюється, він створюється як сумісний з тим пристроєм чи вікном, на яке передається копіювання інформації. Алгоритм роботи з даним контекстом:

1) отримуємо ідентифікатор того контексту пристрою, куди буде виводитись зображення – hDC;

2) отримуємо ідентифікатор зображення, яке буде виводитись – hBitmap;

3) створюємо сумісний з даний пристроєм контекст пам’яті – CreateCompatibleDC;

4) вибираємо зображення з п.2. як поточне для даного контексту пам’яті – hCompatibleDC;

5) копіюємо контекст з пам’яті в контекст пристрою – hCompatibleDС -> hDC;

6) видаляємо сумісний контекст – hCompatibleDC;

7) видаляємо карту зображень – hBitmap;

8) видаляємо контекст пристрою – hDC;

12. Малювання При виводі інформації на екран чи пристрій одиниці логічних координат перетворюються в фізичні (пікселі). Встановлюється режим відображення GetMappingMode, де 1-й – ідентифікатор контексту пристроою, 2-й – визначає режим відображення. Режими: по-замовч це: MM_TEXT

В Windows при виведенні зображення на екран створюється віртуальне вікно. При цьому можна використ. 1 з 3 методів:

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

2) послідовність подій, що формують робочу область зображення можна зберегти

3) в цих 2 методах можна обійтись без віртуального. Тут створюється віртуальне вікно і вивід відбув. за такою схемою:

- створ. вікна (створ. сумісний контекст пристрою – CreateCompatibleDC, створ. сумісну карту зображень – CreateCompatibleBitmap, вибрати перо з кольором, створ. бітовий шаблон – PatBlt;

- вся інформація виводиться на віртуальне вікно – InvalidateRect;

- при отрим. Повідомл. WM_PAINT ми переписуємо вміст віртуал. вікна в реальне вікно – BitBlt. Функції для малювання:

встановлення поточної позиції – MoveToEx., малювання 1 пікселя – SetPixel, колір в RGB-форматі – подвійне слово 0х00bbggrr, LineTo – провести лінію, Arc – малювання дуги., Rectangle – малювання прямокутника., RoundRect – малювання прямокутника із заокругленими кутами., Ellipse, Pie – малювання еліпсів і секторів еліпсів.

Малювання графічних примітивів відбув. за допомогою пер. Є 3 пера: BLACK_PEN, WHITE_PEN, NULL_PEN (прозоре). По замовчуванню - BLACK_PEN. Ідентифікатор кожного з пер можна отримати – GetStackObject.

 

13. Атрибути файлів:

FILE_ATTRIBUTE_READONLY,FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_SYSTEM FILE_ATTRIBUTE_DIRECTORY, FILE_ATTRIBUTE_ARCHIVE

FILE_ATTRIBUTE_NORMAL,FILE_ATTRIBUTE_TEMPORARY FILE_ATTRIBUTE_COMPRESSED, FILE_ATTRIBUTE_OFFLINE

Отримати значення атрибута можна функцією GetFileAttribute(), а встановити - SetFileAttribute().

Характеристики файлів:

- числові. FAT-32 для файлу має 3 числові характеристики - час створення, час останньої модифікації і час останнього доступу. Час відраховується в наносекундах і починається з 12:00 1 січня 1600р. При цьому час зберігається в 2 32-бітних величинах – «універсальних координатах» і для його використ. треба перетвор. в локальний час - FileTimeToLocalFileTime(). Отримати ці числові дані про файл – GetFileTime().

- довжина файлу або в 2-х 32-бітних величинах, або в 1-й 64-бітній. Розмір файлу можна отримати функцією GetFileSize().

- ім'я файлу. Розрізняється довге і коротке ім’я, так само як довгий і вкорочений шлях. Перетворення одне в друге – GetShortPathName(),GetFullPathName().

ОС Windows надає ряд функцій АРІ, які полегшують роботу над групами файлів чи каталогів.

SHStruct

Hwnd Dword? //дукстриптор вікна, де буде результат

wFunc Dword? //код операції – FO_COPS, FO_DELETE, FO_MOVE, FO_RENAME

pFrom Dword? //назва файлу, групи чи каталогу, над яким викон. операція

pTo Dword? //ім’я файлу чи групи імен куди буде запис. Код операції

fFlags Dword? //прапорець, що визначає характер операції

fanyOperationsAborted Dword? //визначає чи переривалась операція (якщо 0 – то так)

hNameWapping Dword? //дескриптор відображуваного в пам’яті файлу

pSizeProgressTitle Dword? //вказівник на рядок заголовок для діалогового статусу

SHENDS Параметри прапорців:

ü FOF_ALLOWUNDO – треба зберегти інфу для повернення у вихідний стан якщо можливо

ü FOF_CONFIRMMOUSE – не реалізоване

ü FOF_FILESONLY – виконувати тільки над тими файлами, для яких встановлений шаблон

ü FOF_MULTIDESTFILES – параметр функції pTo матиме декілька результуючих значень

ü FOF_NOCONFIRMATION – відповідати ствердно на всі запити

ü FOF_NOCONFIRMMKDIR – створювати каталог без запиту

ü FOF_RENAMEONCOLLISION – додавати файлам нові імена якщо файл з даним ім’ям існує

ü FOF_SILENT – не показувати вікно статусу

ü FOF_SIMPLEPROGRESS – показувати вікно статусу, але не показувати імена файлів

14. Головна ознака текстового файлу – містить рядки різної довжини, що відрізняються розділювачами. Найчастіше це послідовність кодів 13 та 10. В Асемблері використ. переважно такі способи роботи з текст. файлами:

1) побайтове читання з файлу

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

3) зчитати в буфер весь файл (або велику частину)

4) використ. файли, які відображаються в пам’ять. Для цього:

- створюємо новий файл – CreateFile().

- відображаємо даний файл в пам’ять – CreateFileMapping().

- копіюємо вміст файлу в відображуваний – MapViewOfFile().

- виконуємо всі дії над файлом.

- якщо потрібно, зберігаємо файл на фізичний носій – FlushViewOfFile().

- закриваємо відображуваний файл - UnMapViewOfFile().

- закриваємо відображуваний і створений файли.

15. Стратегія обробки файлів.

Переваги бібліотеки С:

1) код переноситься легко на різні системи

2) функції С набагато простіші при використанні за АРІ

3) має велику кількість функцій для роботи з символами та рядками

4) не має прямих аналогів серед функцій АРІ

5) функції С здатні працювати в середовищах з багато потоковою підтримкою

Недоліки бібліотеки С:

1) засоби С не забезпечують управління каталогами в обхід дерева каталогів і в більшості випадків не дозволяють встановлювати атрибути файлів

2) не надає можливості відображення файлів, асинхронний ввід/вивід, взаємодію між процесами в файлах

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

Для блокування є функції LickFile() i LockFileEx().

LockFileEx() відноситься до функцій розширеного вводу/виводу. Вона блокує ділянку відкритого файлу для роздільного доступу. Для розблокування використ: UnLockFileEx()

Коли відбув. блокування, треба звертати на увагу на:

1) межі області розблокування повинні повністю співпадати з межами області, що булла заблокована.

2) не дозволяється об’єднання двох чи більше раніше заблокованих областей

3) щойно створена область блокування і існуюча не повинні перекриватись.

4) блокування не успадковується щойно створеними процесами

17. Реєстр – централізована ієрархічна БД, що зберігає інформацію про параметри конфігурації системи і встановлених додатків. Доступ до реєстру здійснюється через ключі реєстру (registery keys), що відіграють роль папок, каталогів у файловій системі.

Розділ може містити або інші розділи, або пари ім’я-значення, в яких між іменем і значенням існує зв’язок, як між іменем і вмістом файлу. В цих парах міститься інформація:

1) номер версії ОС, номер сборки, інформація про зареєстр. Користувачів

2) номер версії програми (додатку), номер сборки, інформація про роботу

3) техн. Інформація: к-сть і тип процесорів, ОП

4) налаштування кожного з користувачів

5) інформація про встановлені служби

6) відображення мережевих адрес на імена, що використ. локал. комп’ютерами

18. Ключі реєстру Доступ до реєстру йде через його ключі. Є деяка к-сть розділів, що відіграє роль точок входу в розділ:

1) HKEY_LOCAL_MACHINE – зберіг. інфа про обладнання локал. комп’ютера і його ПЗ

2) HKEY_USERS – зберіг. інфа про налаштування користувача

3) HKEY_CURRENT_CONFIG – зберіг. інфа про налашт. техн. параметрів

4) HKEY_ CURRENT_USER – містить інфу, що визначається користувачем

19.Функції управління реєстром дозволяють переглядати і змінювати дані, що відносяться до пар ім’я-значення, а також створювати нові підрозділи і нові пари ім’я-значення.

Для вказівки існуючих розділів і створення нових використ. дескриптори типу HKEY.

1) LONG RegOpenKeyEx() – функція відкриття розділу

2) RegCloseKeyEx() – функція закриття. Єдиний параметр – дескриптор відкритого розділу.

3) RegEnumValue(), RegQueryValue() – отримання пар ім'я-значення. Взаємодоповнюючі.

4) RegEnumKeyEx() – отримання імен розділів заданого розділу.

5) LONG RegCreateKeyEx() – функція створення розділу

6) RegDeleteKeyEx() – функція видалення розділу. Має 2 параметри – дескриптор відкритого розділу і ім’я підрозділу.

20. Управління значеннями реєстру відбув. за допомог. RegEnumValue(). Вона повертає рядок, що містить ім’я вказаного параметра і розмір даних. Також отрим. значення вказаного параметра і його тип.

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

Для встановлення значення параметра відкритого розділу використ. функція RegSetValueEx(). Тут задається ім’я параметра, його тип і значення.

Знищити значення – RegDeleteValue().


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

1) віртуальну пам'ять, що ОС для нього виділяє

2) дескриптори відкритого ним файлів

3) список завантажених ним в його пам’ять динам. модулів

4) створені ним підпроцеси чи потоки, що виконуються незалежно один від одного в пам’яті головн. процесу

Створюється функцією CreateProcess(). Її 6 параметр – прапорець, який відповідає за властивості процесу, що породжується. Його можливі значення:

- CREATE_DEFAULT_ERROR_MODE – новий процес не успадковує режим помилок

- CREATE_NEW_CONSOLE – дочірній процес успадковує консоль батьківського

- CREATE_NO_WINDOW – використ. у випадку консолей і коли дана програма запуск. без консольного вікна

- DEBUG_PROCESS – батьківський процес буде розглядатись як відлагоджувач, а дочірній – процес, що відлагоджується батьківським

- DEBUG_ONLY_THIS_PROCESS – якщо викликаючий прапорець відлагоджується ззовні, то дочірній теж відлагоджується

- DETACHED_PROCESS – новий процес не успадковує консоль батьківського

9 параметр – вказівник на структуру, що містить інфу про вікно дочірнього процесу. Одним з параметрів структури є прапорець, який набуває таких значень:

START_USESHOWWINDOW equ1h – додати поле ShowWindow

START_USESIZE equ2h – дозволити змінювати розміри

START_USEPOSITION equ4h – дозволити змінювати позицію курсору

START_USECOUNTCHARS equ8h

START_USEFILLATTRIBUTE equ10h

Часові характеристики процесу –GetProcessTimes()

22. Знищити процес можна 2 функціями: ExitProcess() і TerminateProcess(). У звичайних умовах процес завершується, коли він сам чи один з належних йому потоків викликає ExitProcess() – має 1 параметр (ідентифікатор). При цьому відбув. Такі дії:

1) викликаються функції деініалізації всіх підключених dll-бібліотек

2) закриваються чи знищуються всі об'єкти, відкриті чи створені процесом

3) стан процесу змінюється на звільнений – signaled, що є сигналом для всіх інших потоків чи процесів, які очікують завершення роботи даного процесу

4) лічильник числа користувачів процесу зменш. на 1

Взнати чи є процес остаточно завершеним - GetExitProcessCode()

Завершення роботи процесу не приводить до автоматичного завершення роботи всіх породжених ним процесів.

Аварійний спосіб завершення роботи процесу викликається ззовні. Недоліки: можуть бути незавершені дії, не вилучає бібліотеки dll з пам'яті.

 

23. Потік (Thread) - незалежна одиниця виконання в контексті процесу. Потоки, що належать 1 процесу, розділяють загальні дані і код. Тому:

1) у кожного потоку є власний стек, який він використ. при виклику функцій і обробці даних

2) кожен потік може розприділяти індекси власних локальних областей зберігання даних TLS – Thread Local Storage, які надають у розпорядження потокам невеликі масиви даних і забезп. захист даних 1 потоку від даних іншого.

TLS вигляд. Індекси спочатку є нерозподілені і потік в любий момент може їх розподіляти. Мінімальна рядків любого потоку визнач. значенням TLS_MINIMUM_AVIALABLE.

Функція TLSAlloc() повертає розподілений індекс 0,1,…

Отримати значення можна функцією TLSGetValue за вказівкою індексів таблиці TLS.

Встановити значення TLSSetValue()

Звільнити – TLSFree().

Створення потоку – CreateThread(). При створенні потоку треба вказувати розмір стеку, що буде відводитись під потік.

GetCurrentThread() – повертає псевдо дескриптор потоку

GetCurrentThreadId() – повертає ідентифікатор потоку

GetThreadId() – дозволяє отримати дескриптор потоку, якщо відомий його ідентифікатор

OpenThread() – створює дескриптор потоку, якщо відомий його ідентифікатор

SuspendThread (HANDLE hThread) – призупинення потоку

ResumeThread (HANDLE hThread) – відновлення потоку

В кожного потоку є лічильник і він відновлюється, коли count = 0.

Завершення роботи з потоком – ExitThread(), TerminateThrеad().

Чи потік повністю завершив свою роботу – GetExitCodeThread().

24. Недоліки однопотокових програм:

1) перемикання між процесами, що виконуються, споживає значну частину часових ресурсів і пам’яті, що понижує ефективність програм;

2) крім випадків розподілення пам’яті між процесами існує слабкий зв’язок і організація програм, які мають роз приділяти ресурси без використ. потоків є досить громіздкою задачею;

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

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

25. Недоліки багатопотокових програм:

1) потоки розділяють загальну пам’ять і інші ресурси і при недостатньому плануванні можуть виникати помилки, коли 1 потік втручається в дані іншого потоку і крім виявлення помилок, немає синхронізації;

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


26. Для синхронізації роботи потоків Windows надає такі можливості:

· використ. критичних ділянок коду, використ. Мютексів, використ. семафорів і події.

Це повноцінні об’єкти для синхронізації. Блокування є частковим випадком синхронізації.

Функції взаємоблокування:

InterlockedIncrement(lplong lpaddend) – збільшує значення параметра на 1.

InterlockedDecrement(lplong lpaddend) – зменшує значення параметра на 1.

InterlockedExchangeAdd(lplong lpaddend, long increment) – збільш. Знач. змінної на increment.

InterlockedExchange(lplong lpaddend, long increment) – заміна значення змінної на increment.

Функції є атомарними і їх не можна викликати 2 рази підряд.

27. 5правил безпечного багатопотокового коду:

1) змінні, що є локальними стосовно потоку, не повинні бути статистичними і їх варто розміщати в стеці потоку або в TLS;

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

3) не створювати умов змагання між потоками;

4) потоки не мають змінювати оточення процесу, бо це автоматично вплине на всі інші потоки. Тобто потік не має визначати дескриптори стандартного вводу/виводу і змінні оточення. Це не стосується тільки головного потоку.

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

28. Об’єкт критичної ділянки коду (CS) – ділянка програми, що має виконуватись 1 потоком. Найпростіший механізм – використ. критичних секцій CRITICAL_SECTION. Її об’єкти можна ініціалізувати і видаляти. Вони не мають дескрипторів і не можуть спільно викор. різними процесами. Якщо використ. змінні, то вони мають оголошуватись як змінні CS і коли говорять про CS, то кажуть, що потоки можуть входити і виходити з CS і при цьому виконання коду дозволяється лише 1 потоку. Один і той самий потік може знаход. в різних ділянках CS, якщо вони розташ. в різних ділянках коду.

InitializeCriticalSection - ініціалізація

DeleteCriticalSection – знищення

EnterCriticalSection – блокує потік, якщо на даній CS присутній інший потік. Потік, що очікує, розблокується тоді, коли інший потік покине СS: LeaveCriticalSection

Потік, який володіє об’єктом CS, може повторно заходити в цей самий потік без блокування. Тобто об’єкти CS є рекурсивними. При цьому підтримується лічильник входжень потоку в даний об’єкт і щоб потік поступився правами на даний об’єкт іншому потоку, він повинен залишити об’єкт стільки разів, скільки зайшов.

TryEnterCriticalSection – перевіряє чи не належить об’єкт CS на даний момент іншому потоку.

Оскільки CS не є об’єктом ядра, вони є більш продуктивними і швидкодіючими.

 

29. Об’єкти взаємного виключення (mutual exception – mutex) є об’єктами синхронізації ядра і забезп. більш універсальну функціональність в порівнянні з об’єктом CS. На відміну CS, мютекси мають імена і дескриптори. Тому їх можуть використ. потоки, що належать різним процесам.

Вцілому мютекси є аналогічними CS, але мають інтервал кінцевого очікування. Тобто потік набуває права володіння мютексом чи блокує його шляхом виклику функції WaitForSingleObject(), WaitForMultiObject() з використ. дескриптора мютекса. Потік поступається правами шляхом виклику ReleaseMutex(). Як і у випадку CS, потоки можуть набувати правами володіти мютексом декілька разів і стільки ж разів його звільняти.

CreateMutex()–створ.,якщо не задати мют.ім’я,то можна до нього зверт. за дескрипт..

Відкриття – OpenMutex()

Звільнення мютекса, яким володіє потік, що викликав функцію – ReleaseMutex(). Якщо мютекс не належить потоку, виникає помилка. Якщо потік завершив роботу і не звільнив мютекс, то мютекс «покинутий» (abondened), дескриптор його перейде в сигнальний стан. Перевірити мютекс на те, чи є він покинутим – WaitForSingleObject().

При використ. мютексів і об'єктів CS найбільш небезпечна ситуація – взаємоблокування роботи потоків. Щоб уникнути, треба:

- краще планувати;

- використ. WaitForSingleObject() з чіткою вказівкою кінцевого інтервалу. Якщо по закінченню виявляється, що мютекс належить іншому потоку, потік ніби «засинає» і через деякий час знову пробує отримати мютекс. І так поки не отримає.

- використ. WaitForMultiObject(). Тут прапорець fWaitAll треба встановити в істину і потік після 1 атомарної операції буде захоплювати або всі мютекси, або жодного.

30. Порівняння мютексів і CS (є подібними, відрізн. наявністю дескриптора):

1) мютекси, що покинуті, переходять в сигнальний стан і інші потоки не блокуються. Якщо CS, то блокується;

2) мютексам можна присвоювати іменам і тому їх можуть використ. потоки. В CS не можна.

3) в мютексів є можливість організовувати очікування потоків для отримання потрібного мютекса; в об’єкта CS можна організовувати опитування їх стану;

4) при створенні мютекса потік може зразу отримати право володіти ним, що знизує змагальність між потоками. При створенні об’єкта CS такого немає і потоки змагаються на право володіння;

 


 

31. Семафори є також об’єктом синхронізації, який може бути в сигнальному і несигнальному стані і цей стан залежить від лічильника семафору Для роботи використ. 3 функції.

1) CreateSemaphore() – створює семафор

2) OpenSemaphore() – відкриває існуючий іменований семафор

3) ReleaseSemaphore() – змінює значення лічильника семафора

Функція очікування зменш. значення лічильника семафора на 1.

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

Застосування семафорів:

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

2) дросель (throttle) семафора – використ. коли треба обмежити к-сть робочих потоків для зменш. змагальності між ними. Для цього:

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

- кожен робочий потік повинен очікувати сигнальний стан семафора перш ніж увійти в CS. При вході він зменш. значення лічильна на 1, при виході – збільш.

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

32. Події – об’єкти синхронізації ядра. Використовуються для сигналізування іншим потокам, що наступила певна подія. Подія – будь-що (повідомлення і т.п.). Особливість – перехід в сигнальний стан єдиного об’єкта події здатний вивести з стану очікування одночасно декілька потоків. Є 2 події:

1) manual-reset – скидається вручну – можуть сигналізувати одночасно всім потокам, що чекають на настання цієї події і переводяться в несигнальний стан програми;

2) auto-reset – скидається автоматично – один потік знаход. в несигнальному стані, решта чекають.

Використовується 5 функцій:

1) CreateEvenr()

2) OpenEvent()

3) SetEvent() – встановлення в сигнальний стан. Якщо подія автоматично скидається, то вона автоматично повернеться в несигнальний стан. При відсутності потоків подія буде в сигнальному стані до появи 1 (тоді веде себе як семафор з лічильником 1).

4) ResetEvent() – звільнення події

5) PulseEvent() – звільнення потоків, що очікують настання звільнення події

Подія без імені не використ. потоками інших процесів.

 

 

33. Синхронізація взаємодії процесів забезпечується за допомогою:

1) файлів і файлів, що відобр. в пам’ять

2) сокетів

3) віддаленого виклику процедур

4) використання каналів

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

34. Анонімні канали (Anonimous channels) – забезп. однонаправлену (напівдуплексну) по символьну взаємодію між процесами. Кожен з каналів має 2 дескриптори – дескриптор читання (read handle) і запису (write handle).

CreatePipe() – створення каналу

CreateNamedPipe() – створення іменованого каналу. Повертається його дескриптор.

Процесу потрібно передавати дескриптори для того, щоб він міг зчитувати дані з каналу. Сервер записує, клієнт зчитує (навпаки – ні).

Дескриптор назив. успадкованим, якщо він передається.

35. Іменовані канали забезп. двонаправлену взаємодію. Можливості ширші:

1) оскільки двонаправлені, то за допомогою 1 каналу можна здійснити обмін повідомленнями між 2 процесами

2) орієнтовані на обмін повідомленнями

3) кожна з систем, що підключена до мережі, може звернутись до каналу за іменем і працювати абсолютно однаково з каналом незалежно де знаходяться процеси - на 1 чи різних машинах

4) допускається існування декількох каналів, що мають однакові імена

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

У випадку між платформної взаємодії TCP/IP і використ. мережевих додатків, механізм іменованих каналів краще не використ, а використ. WindowsSockets для взаємодії між процесами.

Для створення 1-го екземпляру іменованого каналу використ. функція CreateNamedPipe, де 1-ий – вказівник на ім’я каналу, 2-й – задає режим, в якому відкривається канал. Можливі варіанти:

- PIPE_ACCESS_DUPLEX – для читання і запису;

- PIPE_ACCESS_INBOUND – для читання;

- PIPE_ACCESS_OUTBOUND – для запису;

- PIPE_READMODE_BYTE – побайтове читання;

- PIPE_READMODE_MESSAGE - PIPE_WAIT, - PIPE_NOWAIT

- FILE_FLAG_OVERLAPPED – читання/запис з перекриттям

3-ий – прапорець. Значення - PIPE_TYPE_BYTE, PIPE_TYPE_MESSAGE, PIPE_READMODE_ BYTE, PIPE_READMODE_MESSAGE, PIPE_WAIT і PIPE_NOWAIT.

При 1-ому виклику функції CreateNamedPipe відбувається створення самого каналу, при повторних – екземпляри.

36. Підключення клієнтів і стан іменованих каналів. Підключення відбув. з 2-х сторін – клієнта і сервера. Вони відрізняються один від одного. Після створення іменованого каналу сервер може очікувати підключення клієнта, використ. Функцію ConnectNamedPipe()

З сторони клієнта відбув. підключення функцією CreateFile, де замість імені – шлях на ім’я каналу.

Сервер створює іменований канал CreateNamedPipe() і встановлює зв’язок з клієнтом функцією ConnectNamedPipe(). Після роботи закриваємо дескриптор.

Клієнт може використ. функцію WaitNamedPipe(), яка використ. клієнтом для синхронізації з’єднань з сервером, тобто функціє перевіряє чи є на сервері незнаверш. виклик даної функції. У випадку наявності, клієнт може створити з’єднання функцією CreateFile() і робити різні зміні з файлом (читання/запис). Після роботи закриваємо дескриптор.

37. Функції стану іменованих каналів:

1) GetNamedPipeHandleState() – повертає для заданого відкритого дескриптора каналу інформацію: в якому режимі працює – блокувальному чи ні, працює з повідомленнями чи масивами байт, яка в каналу кількість екземплярів.

2) SetNamedPipeHandleState() – дозволяє встановити атрибути стану. Параметри задаються не по значенню, а по адресі.

3) GetNamedPipeInfo() – дозволяє встановити кому належить канал – серверу чи клієнту.

38. Функції транзакції іменованих каналів. При типовій конфігурації іменованого каналу клієнт виконує операції:

1) відкриває екземпляр каналу, при цьому створюючи довготривале з’єднання, яке повністю займає канал;

2) періодично посилає запити про те, чи є щось для нього в каналі і чекає відповіді;

3) виконує читання/запис;

4) закриває.

Послідовність викликів читання/запису можна розглядати як 1 транзакцію і викон. за допом. ф-ї:

- BOOL TransactNamedPipe() – об’єднує ReadFile, WriteFile. Якщо канал малого розміру, то використ. функції приведе до збільш. ефективності – 60%, малого – 20%. Створює постійне з’єднання з каналом.

- BOOL CallNamedPipe() – об’єднує CreateFile, ReadFile, WriteFile, CloseHandle. Створює тимчасове з’єднання з каналом.

- BOOL PeekNamedPipe() – перевіряє чи є повідомлення в іменованому каналі. Використ. для опитування стану іменованого каналу. Якщо дані є, то 1-й параметр функції >0. Функція зчитує повідомлення з іменованого каналу, але не видаляє його звідти. Її неможливо блокувати.

 

 

39. Канали Fifo – канали, які передає ОС Uniх. Вони є аналогом іменованих каналів і теж забезп. взаємодію незв’язних між собою процесів. Але, в порівнянні з іменованими каналами, вони:

1) напівдуплексні;

2) діють в межах 1 комп’ютера;

3) орієнтовані на роботу з байтами.

Якщо на комп’ютері використ. канали Fifo, то для кожної відповіді клієнта сервер повинен створити окремий канал, а клієнти можуть посилати запити по одному каналу.

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

1) посилання повідомлення;

2) зчитування повідомлення.

Реалізація такого зв’язку може здійснюватись на фізичному рівні через загал. пам’ять чи апаратну шину. Таку організацію реалізувати не складно.

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

1) створення поштової скриньки;

2) відправлення повідомлень через поштову скриньку;

3) зчитування повідомлення;

4) видалення поштової скриньки.

При НКП є певні особливості:

- 1 процес може встановити зв’язок з багатьма процесами, що мають доступ до даної скриньки;

- кожна пара процесів може мати кілька ліній зв’язку – повідомлення посилаються через різні поштові скриньки;

- зв’язок може бути двонаправленим;

- зв’язок може бути ненаправленим. Може виникнути проблема синхронізації, яку виріш. так:

1) обмежують зв’язок між 2 процесами;

2) дозволити на кожен момент часу отримати повідомлення тільки 1 процесу;

3) зробити способи нотифікації отримувача.

42. Буферизація та черга повідомлень

Комунікацію між процесами можна реалізувати через чергу повідомлень 3 способами:

1) нульова ємність черги повідомлень – повідомлення не можуть зберігатись в черзі і процес-відправник чекає процес-отримувач;

2) обмежена ємність черги повідомлень(найбільш вживано) – при реалізації треба кожен раз перевіряти чи в буфері для черги є місце і коли його немає треба організувати очікування процеса-відправника;

3) необмежена черга повідомлень – використовується коли не треба, щоб процес очікував

43. Операціям вводу/виводу властива повільна швидкість виконання в порівнянні з іншими видами обробки, тому що існують затримки:

- обумовлені витратою часу на пошуки потрібних доріжок і секторів;

- пов’язані з низькою швидкістю обміну даних між фізичн. пристроями і сист. пам’яттю;

- при передачі даних по мережі з використ. файлових серверів і сховищ даних.

В зв’язку з цими недоліки способом прискорення вводу/виводу є асинхронний ввід/вивід. Методи:

1) багато потоковий ввід/вивід – кожен з потоків в процесі чи в їх наборі виконує звичайний синхронний ввід/вивід, але при цьому не блокується робота інших потоків і вони працюють в звичайному штатному режимі;

2) ввід/вивід з перекриттям – потік допускає операцію читання чи запису чи іншу якусь операцію (передачу даних) і продовжує виконання. Якщо йому потрібні дані, то він призупиняється і чекає поки не стане доступний відповідний дескриптор і не наступить задана подія.

3) Розширений ввід/вивід – асинхронний ввід/вивід з використ. процедур завершення – подібний до попереднього, але під час завершення операцій вводу/виводу система викликає спеціальну процедуру завершення, яка виконується всередині потоку.

44. Для організації вводу/виводу що перекривається треба використ. структури OverLapped. Вона використ. в функціях LookFileEx() – блокування файлів, ReadFile(), WriteFile(), TransactNamedPipe(), ConnectNamedPipe(). Для організації такого вводу/виводу треба встановити атрибут перекриття (overlapped attribute), який встановлюється при створенні файлу – CreateFile() і задати прапорець File_Flag_Overlapped.

До використ. структури OverLapped є застереження:

1) не можна повторно використовувати, поки ввід/вивід не завершиться;

2) не можна повторно використав. подію, яка вказана в структурі до заверш. вводу/виводу;

3) якщо існує декілька незакритих запитів, що відносяться до 1 дескриптора, то для синхронізації треба використ. не дескриптори, а події

Наслідки вводу/виводу, що перекривається:

1) операції не блокуються, тобто функції виконуються повернення не чекаючи операції завершення вводу/виводу;

2) значення, що повертаються цими функціями не можуть бути використані як критерій успішності чи неуспішності виконання функції;

3) повернення значення кількості переданих байт теж не є інформативним;

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

GetOverLappedResult() – визначає стан вводу/виводу. За допомогою CanselIO() можна відмінити виконання всіх операцій вводу/виводу що перекривається і пов'язані з дескриптором в якості параметра.

 


 

45. Розширений ввід/вивід з використанням процедур завершення. Тут потік не чекає надходження сигналу завершення, а система ініціює визначену користувачем процедуру завершення. Щоб використати процедуру завершення, треба якось передавати їй адресу. Для цього використ. ReadFileEx(), WriteFileEx(). Перш, ніж процедуру завершення буде виконана, треба щоб виконувалось:

1) повинна бути завершена операція вводу/виводу;

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

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

46. Функції чергового очікування:

1) WaitForSingleObjectEx()

2) WaitForMultiObjectEx()

3) SleepEx()

Всюди останнім параметром є прапорець, який у випадку асинхронного вводу/виводу має бути істиною. Тривалість інтервалів очікування задається в мс. Кожна з функцій робить повернення, коли відбувається 1 з 3 подій:

1) дескриптор переходить в сигнальний стан;

2) закінчується час очікування;

3) процедури очікування всі що є в черзі потоку, припиняють своє виконання, а стан прапорця є істина.

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

Якщо треба передавати певні дані процедурі завершення, то використ. поле події hEvent в структурі OverLapped, оскільки ОС його повністю ігнорує.

47. Таймери очікування є об’єктами ядра. Вони є синхронізуючі і повідомляючі. Синхронізуючий зв’язується з функцією непрямого виклику, яка є аналогом функції завершення, повідомляючий – треба використовувати функцію очікування.

HANDLE CreateWaritableTimer() – створення. Повертає дескриптор таймера. Спочатку таймер створюється в неактивному стані. Для активізації – BOOL SetWaritableTimer(). Тільки дана функція може змінити сигнальний стан таймера. Скасувати таймер - CanselWaritableTimer().

 


Дата добавления: 2015-11-04; просмотров: 24 | Нарушение авторских прав




<== предыдущая лекция | следующая лекция ==>
Приветствую, делюсь с Вами приватной схемой заработка. Всем нам хочется иметь источник дополнительного дохода. При этом не нарушить закон, ну или хотя бы не попасться). | B 15 № 3285. На ри­сун­ке — схема дорог, свя­зы­ва­ю­щих го­ро­да А, Б, В, Г, Д, Е, Ж, З, И, К. По каж­дой до­ро­ге можно дви­гать­ся толь­ко в одном на­прав­ле­нии, ука­зан­ном стрел­кой. Сколь­ко

mybiblioteka.su - 2015-2024 год. (0.107 сек.)