Теорія операційної системи

:: Меню ::

Головна
Представлення даних в обчислювальних системах
Машинні мови
Завантаження програм
Управління оперативною пам'яттю
Сегментна і сторінкова віртуальна пам'ять
Комп'ютер і зовнішні події
Паралелізм з точки зору програміста
Реалізація багатозадачності на однопроцесорних комп'ютерах  
Зовнішні пристрої
Драйвери зовнішніх пристроїв
Файлові системи
Безпека
Огляд архітектури сучасних ОС

:: Друзі ::

Карта сайту
 

:: Статистика ::

 

 

 

 

 

Функції драйверів

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

  • int _init(void) — ініціалізація драйвера. Ця функція викликається при завантаженні модуля. Драйвер повинен зарезервувати всі необхідні йому системні ресурси і проїніциалізіровать власні глобальні змінні. Ініціалізація пристрою на цьому етапі не відбувається.
  • int probe (dev_info_t *dip) — перевірити наявність пристрою в системі. У багатьох системах ця функція реалізується не самим драйвером, а спеціальним модулем-"сніффером" (sniffer — дослівно, "нюхач")використовуваним програмою автоконфігурації.
  • int attach (dev_info_t * dip, ddi_attach_cmd_t crtid) — ініціалізація копії драйвера, що управляє конкретним пристроєм. Цю функцію можна розглядати як аналог конструктора об'єкту в об'єктно-орієнтованому програмуванні. Якщо в системі присутньо декілька пристроїв, керованих одним драйвером, деякі ОС завантажують декілька копій коди драйвера, але в системах сімейства Unix функція attach просто викликається багато разів.


Кожна з копій драйвера, що ініціалізували, має власний блок локальних змінних, в яких зберігаються змінні достатки пристрою. При виклику attach драйвер повинен прочитати конфігураційний файл, де записані параметри пристрою (номенклатура цих параметрів залежить від пристрою і від драйвера), розмістити і проїніциалізіровать блок змінних достатку, зареєструвати обробники переривань, проїніциалізіровать само пристрій і, нарешті, зареєструвати пристрій як доступне для призначених для користувача програм, створивши для нього мінорний запис (minor node). У ряді випадків драйвер створює для одного пристрою декілька таких записів.
Наприклад, кожен жорсткий диск в Unix Svr4 повинен мати 16 записів — по дві (далі ми зрозуміємо, для чого вони потрібні) для кожного з восьми допустимих слайсов (логічних розділів, див. разд. Завантаження самою ОС ) диска. Інший приклад: у більшості систем сімейства Unix стрічкопротяжні пристрої мають два мінорні записи. Один з цих пристроїв при відкритті перемотує стрічку до початку, інше не перемотує. Насправді обидва пристрої управляються одним і тим же драйвером, який визначає поточний режим роботи залежно від вказаного мінорного запису.
Сучасні системи Unix, зокрема Solaris, використовують відкладену ініціалізацію, коли для багатьох пристроїв attach викликається лише при першій спробі доступу призначеної для користувача програми до пристрою.

  • int detach(dev_info_t *dip, ddi_detach_cmd_t cmd) — аналог деструкції об'єкту в ООП. Втім, на відміну від деструкції, ця операція не безумовна — якщо не удається нормально завершити оброблювані в даний момент операції над пристроєм, драйвер може і навіть зобов'язаний відмовитися дєїніциалізіроваться. При дєїнгщиалізациі драйвер повинен визволити всі системні ресурси, які він зайняв при ініціалізації і в процесі роботи (у тому числі і знищити мінорний запис) і може, якщо це необхідно, провести якісь операції над пристроєм, наприклад, вимкнути приймач, запарковать голівки читання-запису і так далі Після того, як всі пристрої, керовані драйвером, успішно дєїніциалізіровани, система може його вивантажити.
  • int _fini (void) — функція, що викликається системою перед вивантаженням дуля. Драйвер зобов'язаний визволити всі ресурси, які він зайняв на етапі ініціалізації модуля, а також всі ресурси, зайняті їм під час роботи на рівні модуля (не прив'язані до конкретного керованого пристрою).

Після того, як драйвер проїніциалізіровался і зарегистріроват міно ную запис, призначені для користувача програми можуть починати звертатися до не му і до керованих ним пристроїв. Зрозуміло, що забезпечити єдиний ін терфейс до всіляких категорій пристроїв, перерахованих в главі 9 щонайменше складно. Найрадикальніше підійшли до цієї проблеми розробники системи UNIX, що розділили всі пристрої на два класса-блочниє (високошвидкісні пристрої пам'яті з довільним доступом в першу чергу, дискові пристрої) і послідовні або символьні пристрої (все останнє) (насправді, в сучасних систем сімейства Unix типів драйверів дещо більше, але про це далі).
Над послідовними пристроями визначений наступний набір операцій, які можуть здійснюватися прикладною програмою (у простих випадках ці операції безпосередньо транслюються у виклики функцій драйвера).

  • int open (char * fnarne, int flags, mode_t mode) — Процедура відкриття
    пристрої. В деяких випадках вона може містити і додаткові кроки ініціалізації пристрою — наприклад, для лентопротяжек ця процедура може включати перемотування стрічки до початку. Функція повертає цілочисельний ідентіфікатор-"ручку" (handle), часто званий також дескриптором файлу, який використовується програмою при всіх подальших зверненнях до пристрою.
  • int readfint handle, char * where, size_t how_much) — читання даних з пристрою. Якщо пристрій пристосований лише для виводу (наприклад, принтер), ця функція може бути не визначена.
  • int write (int handle, char * what, size_t how_much) — запис даних
    на пристрій. Якщо пристрій пристосований лише для введення, (наприклад, перфоленточний введення або миша), ця функція також може бути не визначена.
  • void dose (int handle) — процедура закриття (звільнення) пристрою.
  • int ioctitint handle, int cmd ...) — процедура завдання спеціальної команди, яка не може бути зведена до операцій читання і запису. Набор таких команд залежить від пристрою. Наприклад, для растрових графічних пристроїв можуть бути визначені операції установки відеорежиму; для послідовних портів Rs232 це можуть бути команд^ установки швидкості, кількості бітів, обробки біта парності і т. д., для дисководів — команди форматування носія.
  • off r lseek<int handle, off_t offset, int whence)long seek — команда переміщення голівки чтенія/запіси до заданої позиції. Драйвери пристроїв, що не є пристроями пам'яті, наприклад модему або Принтера, як правило, не підтримують цю функцію.

Слово long в назві функції з'явилося по історичних причинах: версіях Unix для 16-розрядних машин індекс позиції не міг обозначать-я словом, бо це обмежувало б логічну довжину пристрою неприпустимо малим значенням 65334 байт. Тому необхідно було використовувати подвійне слово, що відповідало типові long мови С. Современниє системи використовують 64-розрядний off_t.

  • caddr_t rranap (caddr_t addr, size_t len, int prot, int flags, int handle, off_t offset) memory map — відображення пристрою в адресний простір процесу. Параметр prot задає права доступу до ділянки, що відображує: на читання, на запис і на виконання. Відображення може відбуватися на задану віртуальну адресу, або ж система може вибирати адресу для відображення сама.

Ця функція була відсутня в старих версіях системи, але більшість сучасних систем сімейства (BSD 4.4, ряд спадкоємців BSD 4.3, Svr4 і Linux) підтримують її.
Йдеться про відображенні в пам'ять даних, що зберігаються на пристрої. Для пристроїв введення-виводу, наприклад, для принтера або терміналу, цю функцію неможливо реалізувати розумним чином. Навпаки, для стрічок і інших послідовних пристроїв пам'яті, що підтримують функцію Iseek відображення може бути реалізоване з використанням апаратних засобів віртуалізації пам'яті і операції read і write. Необхідність спеціальної функції відображення з'являється в драйверів пристроїв, що використовують великі об'єми пам'яті, що відображує в адресний простір системної шини, наприклад, для растрових відеоадаптерів, деяких звукових пристроїв або сторінок спільної пам'яті (backpane memory — двопортовій пам'яті, використовуваній як високошвидкісний канал обміну даними в багатопроцесорних системах).
Механізм відображення доступних прикладній програмі системних викликів у функції драйвера відносно складний. Цей механізм повинен включати наступне.

  • Зміна способу ідентифікації пристрою. "Ручка" є специфічним для призначеного для користувача процесу номером, тоді як до драйвера можуть звертатися різні процеси. У системах сімейства Unix для ідентифікації пристрою використовується згаданий вище мінорний запис, який повинен містити покажчик на блок змінних достатку пристрою.
  • Передачу або відображення даних з призначеного для користувача адресного простору в системне і назад.
  • Взаємодія потоків призначеного для користувача процесу (а в спільному випадку -- декількох призначених для користувача процесів, що одночасно використовують пристрій) з потоками драйвера.

Способи, якими ці питання вирішуються в сучасних операційних системах, обговорюються в подальших розділах. А поки що ми детальніше обговоримо, які саме операції над пристроєм слід визначити і чому.
Видно, що пропонований системами сімейства Unix набір операцій розглядає пристрій як неструктурований потік байтів (або, ддя пристроїв введення-виводу, два різноспрямовані потоки — для введення і для виводу). Такий розгляд природний для пристроїв алфавітно-цифрового введення-виводу і простих пристроїв, що запам'ятовують, наприклад магнітних стрічок, проте далеко не настільки природно для складніших пристроїв.
Стандартна відповідь Unix-культуры в цьому випадку така: будь-яка, скільки завгодно складна структура даних може бути серіалізована — перетворена в послідовний потік байтів. Наприклад, зображення може бути перетворене на послідовний потік байтів у вигляді растрової бітової карти або послідовності описів графічних примітивів — ліній, прямокутників і ін. Прикладами такої серіалізациі для зображень можуть бути мова Postscript [partners.adobe.com] і протокол розподіленої віконної системи X Window [www.x.org] (обидва протоколи підтримують як растрові образи, так і досить багаті набори векторних примітивів).
Нерідкі, втім, ситуації, коли нам цікава не лише структура даних, що поступають, але і час їх вступу (у попередній главі ми запропонували класифікувати пристрої, які можуть бути використані так само, як генератори подій) — це буває в додатках реального часу, а також в завданнях, які зараз почало модно називати "завданнями м'якого реального часу", — мультимедійних програмах, що генерують потік звуку, синхронізованного із зображенням, і, особливо, в комп'ютерних іграх.
Для роботи з таким пристроєм прикладна програма, так або інакше, повинна зареєструвати обробник тих, що поступають від пристрою події. У системах Unix така реєстрація полягає у відкритті пристрою для читання, а чекання події полягає у виконанні над цим пристроєм операції читання. Для послідовних пристроїв введення операція читань розблоковується, коли з пристрою вчинять хоч якісь дані (а не тоді, коли буде заповнений весь буфер), тому, якщо прийшла лише одне подія, ми його не пропустимо. Драйвери багатьох пристроїв, здатних працювати генераторами подій, мають команди ioctl що дозволяють тонше управляти умовою розблокування функції read.
Для того, щоб під час чекання події від генератора, займатися ще якоюсь корисною роботою, пропонується або виділити чекаючий події виклик read у окрему нитку, або користуватися системними викликами lect і pollщо дозволяють чекати подій на декількох пристроях (а також засобах межпроцессного взаємодії) одночасно.
Інші ОС надають для роботи з пристроями-генераторами подій складніші механізми, частенько засновані на callback (дослівно — "виклик назад"; механізм взаємодії підсистем, коли підсистема, що запрошує сервіс, передає обслуговуючій підсистемі покажчик на функцію, яку необхідно викликати при настанні певної події).
Робота з генераторами подій вимагає рішення ще однієї задачі — зберігання подій, що поступають, в періоди, коли призначена для користувача програма їх не встигає обробляти. Необхідність відносно складних схем роботи з потрібними для цього буферами змусила розробників Unix System V Release 3 ввести ще одного типа драйверів — потокові (STREAMS) [docs.sun.com 805-7478-10]. Для прикладної програми потоковий драйвер не відрізняється від звичайного символьного пристрою, але відзнак з точки зору системи досить багато. Деякі з цих відзнак розглядатимуться далі.
Unix System V Release 3 (SCO Open Desktop, SCO Openserver), Release (SCO Unixware, SGI Irix, Sun Solaris) і системи, що випробували вплив OSF Unix (IBM AIX, Hp/ux) використовують потокові драйвери для реалізації таких важливих псевдопристроїв, як труби і сокети Tcp/ip. Крім того, потоковими в цих системах є драйвери мережевих адаптерів і термінальних пристроїв.
З іншого боку, в Os/2 і Windows Nt/2000/xp існують обширні номенклатури типів драйверів з різними наборами функцій. Так, в Os/2 використовуються драйвери фізичних пристроїв наступних типів:

  • прості драйвери послідовних пристроїв введення-виводу, аналогічні драйверам символьних пристроїв в Unix;
  • Драйвери пристроїв прямого доступу, що запам'ятовують, аналогічні драйверам блокових пристроїв в Unix;
  • Драйвери відеоадаптерів, використовувані графічною віконною системою Presentation Manager (PM);
  • Драйвери позиційних пристроїв введення (мишей і ін.), також використовувані РМ;
  • Драйвери принтерів і інших пристроїв виведення твердої копії;
  • Драйвери звукових пристроїв, використовувані підсистемою "мультимедіа" Mmos/2;
  • драйвери мережевих адаптерів стандарту NDIS, використовувані сетевк програмним забезпеченням фірм IBM і Microsoft;
  • драйвери мережевих адаптерів стандарту ODI, використовувані програмним забезпеченням фірми Novell;
  • DMD (Device Manager Driver - - драйвер-менеджер класу пристроїв (у разд. 10.2 ми детальніше розберемося з призначенням драйверів етог типа);
  • різного роду "фільтри", наприклад, ODINSUP.SYS — преобразоватєї ODI-интерфейса в NDIS.
 

:: Реклама ::

 

:: Посилання ::


 

 

 


Copyright © Kivik, 2017