Подсистема ввода-вывода в UNIX. Драйверы

Типы драйверов устройств. Файловый интерфейс, клоны. Встраивание драйверов в ядро. Интерфейс доступа низкого уровня. Архитектура терминального доступа, блочные устройства. Подсистема STREAMS. Управление передачей данных. Доступ к потоку, головной модуль.

Рубрика Программирование, компьютеры и кибернетика
Вид лекция
Язык русский
Дата добавления 27.11.2013
Размер файла 396,9 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

Размещено на http://www.allbest.ru/

Занятие №20

Подсистема ввода-вывода в UNIX. Драйверы

  • Содержание занятия
  • 1. Теоретическая часть. Подсистема ввода-вывода. Драйверы.
  • 1.1 Основные положения
  • 1.2 Драйверы устройств
  • 1.2.1 Типы драйверов
  • 1.3 Файловый интерфейс
  • 1.3.1 Клоны
  • 1.4 Встраивание драйверов в ядро
  • 1.5 Блочные устройства
  • 1.6 Символьные устройства
  • 1.6.1 Интерфейс доступа низкого уровня
  • 1.6.2 Буферизация
  • 1.6.3 Архитектура терминального доступа
  • 1.6.4 Псевдотерминалы
  • 1.6.5 Архитектура терминального лоступа
  • 1.7 Подсистема STREAMS
  • 1.7.1 Модель OSI
  • 1.7.2 Типы сообщений
  • 1.7.3 Передача данных
  • 1.7.4 Управление передачей данных
  • 1.7.5 Головной модуль
  • 1.7.6 Доступ к потоку
  • 1.7.7 Создание потока
  • 1.7.8 Управление потоком
  • Цель работы: знакомство с подсистемой ввода-вывода.

1. Теоретическая часть. Подсистема ввода-вывода. Драйверы

1.1 Основные положения

Фактическая архитектура ввода/вывода скрыта от прикладного процесса несколькими интерфейсами. Один из них -- интерфейс файловой системы был рассмотрен в предыдущей главе. Взаимодействие с удаленными ресурсами обеспечивается сетевыми интерфейсами сокетов или TLI (Transport Layer Interface), которые описываются в главе 6. Однако возможны ситуации, когда прикладному процессу требуется взаимодействие с периферийными устройствами на более низком уровне. Хотя в этом случае роль файловой подсистемы не столь велика, как при работе с обычными файлами, все равно ядро предоставляет процессу унифицированную схему, скрывающую истинную архитектуру того или иного устройства.

В конечном итоге работа всех этих интерфейсов, как высокого уровня, (файловая система), так и более низкого (взаимодействие с физическим устройством), обеспечивается подсистемой ввода/вывода ядра операционной системы.

В данной главе мы ознакомимся с архитектурой этой подсистемы, основным компонентом которой являются драйверы -- модули ядра, обеспечивающие непосредственную работу с периферийными устройствами. Поскольку характеристики периферийных устройств значительно различаются, то UNIX использует два основных типа драйверов -- символьные и блочные. Как следует из названия, драйверы первого типа обеспечивают обмен сравнительно небольшими объемами данных с устройством, что имеет место при работе, например, с терминалами или принтерами. Драйверы второго типа производят передачу данных блоками, что характерно для дисковых носителей данных. Эти типы драйверов входят в традиционную подсистему ввода/вывода и присутствуют во всех версиях UNIX.

Во второй части главы мы подробно остановимся на архитектуре драйверов подсистемы STREAMS, которая является неотъемлемой частью ядра в версиях UNIX System V. Эти драйверы представляют собой отдельный тип, обладающий такими ценными возможностями, как буферизация и управление потоком данных. К подсистеме STREAMS мы также вернемся в следующей главе при обсуждении архитектуры сетевого доступа в UNIX System V.

1.2 Драйверы устройств

Драйверы устройств обеспечивают интерфейс между ядром UNIX и аппаратной частью компьютера. Благодаря этому от остальной части ядра скрыты архитектурные особенности компьютера, что значительно упрощает перенос системы и поддержку работы различных периферийных устройств.

В UNIX существует большое количество драйверов. Часть из них обеспечивает доступ к физическим устройствам, например, жесткому диску, принтеру или терминалу, другие предоставляют аппаратно-независимые услуги. Примером последних могут служить драйверы /dev/kmem для работы с виртуальной памятью ядра /dev/null, представляющий "нулевое" устройство.

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

1.2.1 Типы драйверов

Драйверы различаются по возможностям, которые они предоставляют, а также по тому, каким образом обеспечивается к ним доступ и управление. Можно рассматривать три основные типа драйверов:

Символьные.

Этот тип драйверов обеспечивает работу с устройствами с драйверы побайтовым доступом и обменом данными. К таким устройствам можно отнести модемы, терминалы, принтеры, манипуляторы мышь и т. д.

Доступ к таким драйверам не включает использование буферного кэша, таким образом ввод и вывод как правило не буферизуется. При необходимости буферизации для символьных драйверов обычно используется подход, основанный на структурах данных, называемых clist.

Блочные драйверы

Этот тип драйверов позволяет производить обмен данными с устройством фиксированными порциями (блоками).

Например, для жесткого диска данные можно адресовать и, соответственно, читать только секторами, размер которых составляет несколько сотен байтов. Для блочных драйверов обычно используется буферный кэш, который и является интерфейсом между файловой системой и устройством.

Хотя операции чтения и записи для процесса допускают обмен данными, размер которых меньше размера блока, на системном уровне это все равно приводит к считыванию всего блока, изменению части его данных и записи измененного блока обратно на диск.

Драйверы низкого уровня (raw drivers)

Этот тип интерфейса блочных драйверов позволяет производить обмен данными с блочными устройствами, минуя буферный кэш. Это, в частности, означает, что устройство может быть адресовано элементами, размер которых не совпадает с размером блока.

Обмен данными происходит независимо от файловой подсистемы и буферного кэша, что позволяет ядру производить передачу непосредственно между пользовательским процессом и устройством, без дополнительного копирования.

На рис. 5.1 приведена упрощенная схема взаимодействия драйверов устройств с другими подсистемами операционной системы UNIX.

Жесткий диск Гибкий диск Терминал

Рис. 5.1. Драйверы устройств UNIX

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

/dev/kmem

/dev/ksyms

/dev/mem /dev/nulf

/dev/zero

Обеспечивает доступ к виртуальной памяти ядра. Зная виртуальные адреса внутренних структур ядра, процесс может считывать хранящуюся в них информацию. С помощью этого драйвера может, например, быть реализована версия утилиты ps(1), выводящей информацию о состоянии процессов в системе.

Обеспечивает доступ к разделу исполняемого файла ядра, содержащего таблицу символов. Совместно с драйвером /dev/kmem обеспечивает удобный интерфейс для анализа внутренних структур ядра.

Обеспечивает доступ к физической памяти компьютера.

Является "нулевым" устройством. При записи в это устройство данные просто удаляются, а при чтении процессу возвращается 0 байтов. Примеры использования этого устройства рассматривались в главе 1, когда с помощью /dev/null мы подавляли вывод сообщений об ошибках.

Обеспечивает заполнение нулями указанного буфера. Этот драйвер часто используется для инициализации области памяти.

Драйвер устройства адресуется старшим номером (major number) устройства. Напомним, что среди атрибутов специальных файлов устройств, которые обеспечивают пользовательский интерфейс доступа к периферии компьютера, это число присутствует наряду с другим, также имеющим отношение к драйверу, -- младшим номером (minor number). Младший номер интерпретируется самим драйвером (например, для клонов, оно задает старшее число устройства, которое требуется "размножить"). Другим примером использования младших номеров может служить драйвер диска. В то время как доступ к любому из разделов диска осуществляется одним и тем же драйвером и, соответственно, через один и тот же старший номер, младший номер указывает, к какому именно разделу требуется обеспечить доступ.

Доступ к драйверу осуществляется ядром через специальную структуру данных (коммутатор устройств), каждый элемент которой содержит указатели на соответствующие функции драйвера -- точки входа. Старшее число, по существу, является указателем на элемент коммутатора устройств, обеспечивая, тем самым, ядру возможность вызова необходимой функции указанного драйвера. Таким образом, коммутатор устройств определяет базовый интерфейс драйвера устройств.

Этот интерфейс различен для блочных и символьных устройств. Ядро содержит коммутаторы устройств двух типов: bdevsw для блочных и cdevsw для символьных устройств. Ядро размещает отдельный массив для каждого типа коммутатора, и любой драйвер устройства имеет запись в соответствующем массиве. Если драйвер обеспечивает как блочный, так и символьный интерфейсы, его точки входа будут представлены в обоих массивах.

Типичное описание этих двух массивов имеет следующий вид (назначение различных точек входа мы рассмотрим далее в этом разделе):

struct bdevsw[] { int (*d open)(); int (*d_close) () ; int (*d_strategy)(); int (*d_size) (); int (*d_xhalt) () ;

} bdevsw[];

struct cdevsw[] {

int (*d_open) ();

int (*d^_close) () ;

int (*d_read) <) ;

int (*d_write) ()

int (*d_ioctl) ()

int (*d_xpoll) ()

int {*d_xhalt) ()

struct streamtab *d_str;

} cdevsw[]; Ядро вызывает функцию open {) требуемого драйвера следующим образом:

(*bdevsw[getmajor(dev)].d_open)(dev, ...);

передавая ей в качестве одного из параметров переменную dev (типа dev t), содержащую старший и младший номера. Макрос getmajor() служит для извлечения старшего номера из переменной dev. Благодаря этому драйвер имеет возможность определить, с каким младшим номером была вызвана функция open (), и выполнить соответствующие действия.

Коммутатор определяет абстрактный интерфейс драйвера устройства. Каждый драйвер обеспечивает соответствующую реализацию функций этого интерфейса. Если драйвер не поддерживает каких-либо функций стандартного интерфейса, он заменяет соответствующие точки входа специальными заглушками, предоставляемыми ядром. Когда ядру требуется запросить какую-либо операцию у драйвера устройства, оно определяет элемент коммутатора, соответствующий данному драйверу (используя его старший номер), и вызывает требуемую функцию.

В названиях точек входа драйвера используются определенные соглашения. Поскольку в ядре системы одновременно присутствует большое количество различных драйверов, каждый их них должен иметь уникальное имя во избежание проблем при компиляции (точнее, при редактировании связей) ядра. Каждый драйвер имеет уникальное двухсимвольное обозначение, используемое в качестве префикса названий функций. Например, драйвер виртуальной памяти ядра /dev/kmem имеет префикс mm, таким образом функции этого драйвера будут иметь названия mmopen () , mmclose(),mmread() И mmwrite() .

В табл. 5.1 приведены некоторые точки входа, общие для различных типов драйверов, а символами хх, с которых начинается имя каждой функции, обозначен уникальный префикс драйвера. Стандартные точки входа драйвера отличаются для разных версий UNIX. Например, некоторые версии имеют расширенный коммутатор блочных устройств, включающий такие функции, как xxioctl (), xxread () и xxwrite (). В некоторых версиях включены точки входа для инициализации и сброса шины данных.

Таблица 5.1. Типичные точки входа в драйвер устройства

Точка входа

Сим-вольный

Блоч-

Низкого ный уровня

Назначение

ореп

+ Вызывается при каждой опера-

ции открытии устройства. Обеспечивает необходимую реини-циализацию физического устройства и внутренних данных драйвера. Например, для каждого последующего открытия драйвера могут размещаться дополнительные буферы, обеспечивающие возможность независимой работы с устройством нескольким процессам

close{)

Вызывается, когда число ссылок на данный драйвер становится равным нулю, т. е. ни один из процессов системы не работает с устройством (не имеет открытым соответствующий файл устройства). Может вызывать отключение физического устройства. Например, драйвер накопителя на магнитной ленте может перемотать ленту в начало

xxread()

Производит чтение данных от устройства

xxioctl()

Является общим интерфейсом

управления устройством. Драйвер может определить набор команд, которые могут быть переданы ему, например с помощью системного вызова

xxintr()

Вызывается при поступлении

прерывания, связанного с данным устройством. Может выполнить копирование данных от устройства в промежуточные буферы, которые затем считываются функцией xxread () по запросу прикладного процесса.

ioctl(2).

xxpoll()

Производит опрос устройства.

Обычно используется для устройств, не поддерживающих прерывания, например, для определения поступления данных для чтения

xxhalt()

Вызывается для останова драй-

вера при останове системы или при выгрузке драйвера.

xxstrategy(

Общая точка входа для операций блочного ввода/вывода. Название функции говорит о том, что устройство может обеспечивать собственную стратегию обработки поступающих запросов, например, изменять их порядок для повышения производительности ввода/вывода. Если устройство занято, функция помещает запросы в очередь. В этом случае фактический ввод/вывод инициирует функция обработки прерывания, которая вызывается, когда устройство закончит предыдущую операцию ввода/вывода

xxprint()

Ядро вызывает те или иные функции драйвера в зависимости от запроса. Например, если процесс выполняет системный вызов read(2) для специального файла символьного устройства, ядро вызовет функцию xxread () для соответствующего символьного драйвера. Если же процесс запрашивает ту же операцию для обычного дискового файла, ядро вызовет процедуру xxstrategy() для блочного драйвера, обслуживающего данную файловую систему.

Вообще говоря, можно выделить пять основных случаев, в которых ядро обращается к функциям драйвера:

О Автоконфигурация. Обычно происходит в процессе инициализации UNIX, когда ядро определяет, какие устройства доступны в системе.

О Ввод/вывод. Запрос на операцию ввода/вывода может быть инициирован как прикладным процессом, так и некоторыми подсистемами ядра, например, подсистемой управления памятью.

О Обработка прерываний. Ядро вызывает соответствующую функцию драйвера для обработки прерывания, поступившего от устройства (если устройство способно генерировать прерывания).

П Специальные запросы. Ядро вызывает соответствующую функцию драйвера для обработки специальных команд, полученных с помощью системного вызова ioctl(2).

П Реинициализация/Останов. Некоторые типы аппаратных архитектур могут требовать сброса и реинициализации устройства. Определенные функции драйвера также вызываются при останове операционной системы.

На рис. 5.2 и 5.3 приведены схемы доступа к драйверам символьного и блочного устройств.

Как видно из рисунков, схема обработки запроса ядром UNIX различна для символьных и блочных устройств.

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

П Функция может быть вызвана по запросу процесса. Например, если процесс выполняет системный вызов read(2), ядро вызывает соответствующую точку входа драйвера xxread(), обеспечивающего работу с файлом. В этом случае говорят, что функция имеет контекст задачи.

П Функция может быть вызвана другой подсистемой ядра операционной системы. Например, для блочного драйвера функция xxstrategy () может быть вызвана страничным демоном, для сохранения страниц во вторичной памяти (как правило, на жестком диске). Поскольку страничный демон представляет собой системный процесс, выполняющийся только в контексте ядра, функция xxstrategy () в этом случае имеет системный контекст.

Если функция вызывается в процессе обработки прерывания, то она имеет контекст прерывания -- специальный вид системного контекста. Функции драйвера, отвечающие за обработку прерывания, например xxintr () имеют этот тип контекста.

Рис. 5.3. Доступ к драйверу блочного устройства

Различия в контексте и причинах вызова тех или иных функций драйвера позволяют представить драйвер устройства состоящим из двух частей: верхней части (top half) и нижней части (bottom half). Функции верхней части драйвера имеют синхронный характер, т. е. вызываются по определенным запросам прикладного процесса и выполняются в его контексте. Таким образом, для этих функций доступно адресное пространство и u-area процесса, и при необходимости эти функции могут перевести процесс в состояние сна (вызовом функции sleep (} ядра). Функции ввода/вывода и управления принадлежат верхней части драйвера.

Вызов функций нижней части носит асинхронный характер. Например, момент вызова функции обработки прерываний нельзя предугадать, и ядро не может контролировать, когда эта функция будет вызвана. Выполнение таких функций происходит в контексте ядра и обычно не имеет никакого отношения к контексту текущего процесса. Таким образом, функции системного контекста не имеют права адресовать структуры данных текущего процесса, например его u-area, а также не могут перевести процесс в состояние сна, поскольку это заблокирует процесс, не имеющий непосредственного отношения к работе драйвера.

Две части драйвера требуют синхронизации. Например, в случае, когда функции обеих частей используют одну и ту же структуру данных, функция верхней части при выполнении должна заблокировать прерывания на период работы с "разделяемой" областью памяти. В противном случае, прерывание может поступить в тот момент, когда целостность структуры данных нарушена, что приведет к непредсказуемым результатам.

Все представленные выше функции, за исключением xxhalt (), xxpoll () и xxintr {), принадлежат верхней части драйвера. Функция xxhalt {) вызывается ядром при останове системы и, таким образом, имеет системный контекст, не связанный с контекстом прикладного процесса.

Функция xxpoll () обычно вызывается при обработке ядром прерывания таймера для всех устройств, указанных как опрашиваемые. Это необходимо, в частности, для устройств, которые не могут или "не хотят" использовать аппаратные прерывания. Вместо этого xxpoll () может использоваться для эмуляции прерываний, например вызывая функцию xxintr (} на каждый n-ный тик системного таймера. Поэтому и функция xxpoll () и функция обработки прерывания xxintr () не могут рассчитывать на контекст прикладного процесса. В большинстве версий UNIX функции опроса и обработки прерываний вызываются не через коммутатор устройств, а через специальные таблицы ядра.

В UNIX SVR4 определены две дополнительные точки входа -- init () и start (). Драйвер регистрирует эти функции в таблицах ядра io init [] и io_start[]. Код начальной загрузки системы запускает функции xxinit () перед инициализацией ядра, а функции xxstart () сразу же после инициализации.

1.3 Файловый интерфейс

В главе 4 мы рассмотрели интерфейс т. н. независимой или виртуальной файловой системы, обеспечивающей унифицированный интерфейс работы с различными типами физических файловых систем (например, ufs или s5fs), имеющих разные внутренние структуры и возможности. При этом подходе используется унифицированный формат метаданных активных файлов, которые хранятся в памяти (в in-core -- таблице индексных дескрипторов) и не зависят от конкретной реализации файловой системы. Эти объекты получили название виртуальных индексных дескрипторов или vnode. Для каждого vnode определен набор абстрактных операций, которые реализованы функциями реальных файловых систем. Например, vnode файла, расположенного в файловой системе s5fs, адресует вектор операций (или коммутатор файловых систем, FSS) sSfsops, содержащий конкретные функции этой файловой системы -- s5fs close(), s5f s_open () или s5fs_unlink( ).

Этот подход, используемый в большинстве современных версий UNIX, требует соответствующей архитектуры файлового интерфейса к драйверам устройств. Как уже обсуждалось, доступ к периферии в UNIX осуществляется с помощью специальных файлов устройств, расположенных в корневой файловой системе некоторого типа, например ufs. В соответствии с архитектурой виртуальной файловой системы, все операции с этими файлами будут обслуживаться соответствующими функциями реальной файловой системы, в данном случае -- ufs.

Однако такой схеме недостает традиционного для UNIX изящества. Специальный файл устройства не является обычным файлом системы ufs. Фактически все операции со специальным файлом устройства выполняются драйвером и не зависят от типа файловой системы. Поэтому было бы логичнее отобразить операции vnode не на вектор файловой системы, а непосредственно на коммутатор устройств.

Современные системы ветви System V используют для этого специальный тип файловой системы, называемый devfs или specfs'. Для этого типа файловой системы все операции vnode адресуют соответствующие функции требуемого элемента коммутатора устройств. После первоначального открытия файла, когда создается vnode, все запросы, связанные со специальным файлом устройства, проходят через vnode файловой системы specfs.

В то же время открытие файла, например с помощью системного вызова ореп(2), предусматривает ряд операций, реализованных реальной файловой

В системах SVR4 принята терминология specfs, операционная система SCO UNIX, которая формально является SVR3.2, но фактически имеет многие черты SVR4, называет этот тип файловой системы devfs.

Решение данной проблемы рассмотрим на конкретном примере. Допустим, процесс вызывает функцию ореп(2)для специального файла устройства /dev/kmem для работы с виртуальной памятью ядра. Функция трансляции имени файловой системы ufs -- ufs_lookup() сначала откроет inode файла /dev, а затем, прочитав каталог, обнаружит inode файла kmem. при этом будет размещен vnode этого файла. Однако uf s___lookup () определит, что тип этого файла IFCHR, т. е. специальный файл символьного устройства. Поэтому вместо функции ufsopen (), бессмысленной для этого типа файла, будет вызвана специальная функция файловой системы specfs, которая создаст собственный индексный дескриптор, описываемой структурой snode (от special inode), для этого файла, если таковой уже не находится в памяти. Согласно стандартной процедуре, также будет создан и виртуальный индексный дескриптор vnode, который будет указывать на вектор операций specops, которые специально предназначены для работы с драйверами устройств. Например, функции spec_open(), spec_read() или spec_write () в свою очередь вызовут соответствующие точки входа драйвера -- функции ххореп () , xxread() или xxwrite(). После этого функции ufsopen () будет передан адрес этого vnode, который она, в свою очередь, передаст системному вызову ореп(2). В результате, ореп(2) вернет процессу файловый дескриптор, адресующий vnode файловой системы specfs, а не vnode файла /dev/kmem. Таким образом, все дальнейшие операции с /dev/kmem будут перехватываться файловой системой specfs. Схема связи процесса с этим vnode приведена на рис. 5.4.

Однако изложенная схема является неполной и имеет ряд существенных недостатков. Дело в том, что драйвер конкретного устройства может адресоваться несколькими специальными файлами устройств, возможно, расположенными в различных физических файловых системах. В этом случае ядро бессильно определить фактическое число связей прикладных процессов с данным устройством, что может потребоваться, например, при вызове функции xxclose {), когда все процессы закончили работу с устройством.

Для решения этой проблемы файловая система specfs предусматривает наличие дополнительного snode, позволяющего контролировать доступ к конкретному устройству. Этот объект, получивший название общего snode (common snode), является единственным интерфейсом доступа к драйверу устройства. Для каждого устройства (драйвера устройства) существует единственный common snode, который создается при первом доступе к устройству. Каждый специальный файл устройства, в свою очередь, имеет собственный snode в файловой системе specfs и соответствующий ему vnode, а также inode физической файловой системы, где расположен специальный файл устройства, и соответствующий ему vnode.

Для связи всех этих индексных дескрипторов между собой snode имеет два поля: s_commonvp, указывающее на common snode, и s_realvp, указывающее на vnode специального файла устройства файловой системы, где расположен последний.

Использование тех или иных vnode и связанных с ними inode или snode зависит от конкретных операций, выполняемых процессом с устройством. Большинство из этих операций не зависят от имени специального файла устройства и, соответственно, от реальной файловой системы, в которой он расположен. Эти операции выполняются через vnode, соответствующий common snode. Однако существует ряд операций, выполнение которых зависит от конкретного специального файла устройства, через который процесс взаимодействует с драйвером. Примером может служить проверка прав доступа при открытии специального файла устройства, которые расположены в vnode/inode реальной файловой системы. В этом случае используется vnode соответствующего специального файла устройства.

Схема описанной архитектуры приведена на рис. 5.5.

1.3.1 Клоны

Как уже обсуждалось, старший номер устройства адресует драйвер, в то время как младший номер интерпретируется самим драйвером и может использоваться для различных целей. Например, используя различные младшие номера, процесс может получить доступ к разным разделам жесткого диска, обслуживаемого одним драйвером.

Рис. 5.5. Доступ к устройству через различные специальные файлы

Во многих случаях использование различных младших номеров позволяет нескольким процессам осуществлять одновременную независимую работу с устройством (или псевдоустройством). Каждый младший номер при этом соответствует логическому драйверу, поддерживающему собственные структуры данных при работе с конкретным процессом. Типичным примером могут служить псевдотерминалы. В таких случаях процессу требуется получить доступ к устройству, при этом его не интересует его младший номер, поскольку различие в младших номерах не отражает различие в функциональности. Типичным примером являются сетевые протоколы, чаще всего реализованные в виде соответствующих драйверов. Сетевые соединения, основанные на одном и том же протоколе (и, следовательно, работающие с одним и тем же драйвером), используют различные младшие номера для доступа к драйверу. Это позволяет драйверу обеспечивать обработку нескольких сетевых соединений, для каждого из которых поддерживаются собственные структуры данных. Если процессу необходимо установить сетевую связь, ему безразлично, какой младший номер будет у драйвера, главное, чтобы он еще не использовался.

Возможным сценарием доступа к такому устройству может являться перебор различных младших номеров (соответствующих специальных файлов), пока операция open () не завершится успешно. Это будет гарантировать, что процесс получил в свое распоряжение отдельное логическое устройство. Другой сценарий возлагает всю работу по поиску неиспользуемого младшего номера устройства на специальные драйверы, получившие названия клонов1.

Рис. 5.6. Создание клонов с помощью зарезервированного младшего номера

Clone (англ.) -- размножаться.

Когда процесс открывает специальный файл устройства, происходит инициализация соответствующего snode и вызов функции spec_open (), реализованной в файловой системе specfs, о которой только что говорилось. Эта функция, в свою очередь, вызывает функцию драйвера ххореп (), передавая ей в качестве аргумента указатель на номера устройства, сохраненного в поле sdev snode. Одной из схем реализации клонов является использование зарезервированного младшего номера. Когда процесс открывает специальный файл устройства с этим номером, функция ххореп () выбирает неиспользуемый младший номер и соответственно модифицирует данные snode (с помощью указателя на vnode, передаваемые ей spec_open{)). Поскольку доступ процесса к драйверу осуществляется через vnode файловой системы specfs, все последующие операции будут использовать новый младший номер. Таким образом, процесс получит доступ к новому логическому устройству. Эта схема приведена на рис. 5.6.

Другой подход заключается в использовании специального драйвера, обеспечивающего создание клонов, -- драйвера клонов (clone driver). При этом все драйверы, чье "размножение" обеспечивается таким образом, имеют один и тот же старший номер, адресующий драйвер клонов. Младший номер адресует собственно драйвер, т. е. представляет собой старший номер реального устройства, для которого создается клон. Примеры использования такой схемы можно обнаружить для драйверов системы STREAMS, с помощью которых часто реализуются сетевые протоколы и терминальный доступ, включая псевдотерминалы. Это можно заметить, рассмотрев подробный список файлов, отвечающих за эти устройства:

crw-rw-rw-

crw

crw-rw

crw

crw-rw-rw-crw-rw-rw-

root root root root root root

sys sys

sys sys sys sys

11, 11, 11, 11, 11, 11,

44 5 3 40 42 41

Oct 31 Oct 31 Nov 3 Nov 3 Oct 31 Nov 3

16:36

16: 36

1995

1995

16:36

1995

arp icmp

ip le tcp udp

В данном случае старший номер всех драйверов равен 11 -- это драйвер клонов. Если проанализировать информацию файла, скажем, tcp, To станет понятно, что старший номер драйвера этого протокола равен 42, для файла tcp он представлен младшим номером устройства. Когда процесс открывает этот файл, производится вызов функции clopen() драйвера клонов, которой передаются номера устройства. Функция clopen() использует младший номер для поиска требуемых точек входа драйвера TCP в коммутаторе устройств cdevswf[] После этого clopen () вызывает процедуру хxореn() драйвера, в данном случае tcpopen(), передавая ей указатель на номера устройства и флаг CLONEOPEN. В ответ на это tcpopen() генерирует неиспользуемый младший номер, создает отдельный логический драйвер (т. е. копирует необходимые структуры данных) и соответствующим образом модифицирует поле s_dev индексного дескриптора файловой системы specfs. Таким образом, для получения уникального TCP-соединения процессу нет необходимости самостоятельно производить поиск неиспользуемого младшего номера.

1.4 Встраивание драйверов в ядро

Драйвер устройства является частью кода ядра операционной системы и обеспечивает взаимодействие других подсистем UNIX с физическими или псевдоустройствами. Существует два основных метода встраивания кода и данных драйвера в ядро операционной системы: перекомпиляция ядра, позволяющая статически поместить драйвер, и динамическая загрузка драйвера в ядро в процессе работы системы.

Традиционно для встраивания драйвера в ядро UNIX требуется перекомпиляция ядра и перезапуск системы. Принципиально эта процедура не отличается от компиляции обычной программы, все компоненты ядра являются объектными модулями и редактор связей объединяет их с объектным модулем драйвера для получения исполняемого файла. В этом случае драйвер встраивается в ядро статически, т. е. независимо от фактического наличия устройства и ряда других причин, код и данные драйвера будут присутствовать в ядре UNIX до следующей перекомпиляции.

Однако тенденция развития современных версий операционной системы UNIX заключается в предоставлении возможности динамического расширения функциональности ядра. Это, в частности, относится к файловой системе, драйверам устройств и сетевым протоколам (точнее, драйверам подсистемы STREAMS). Возможность работы с новыми периферийными устройствами без необходимости перекомпиляции ядра обеспечивается загружаемыми драйверами. Вместо того чтобы встраивать модуль драйвера, основываясь на статических таблицах и интерфейсах, ядро содержит набор функций, позволяющих загрузить необходимые драйверы и, соответственно, выгрузить их, когда необходимость работы с данным устройством отпадает. При этом структуры данных для доступа к драйверам устройств также являются динамическими.

Динамическая установка драйвера в ядро операционной системы требует выполнения следующих операций:

П Размещение и динамическое связывание символов драйвера. Эта операция аналогична загрузке динамических библиотек, и выполняется специальным загрузчиком.

G Инициализация драйвера и устройства.

О Добавление точек входа драйвера в соответствующий коммутатор устройств.

П Установка обработчика прерываний драйвера.

Естественно, код динамически загружаемых драйверов сложнее, и содержит, помимо стандартных точек входа, ряд функций, отвечающих за загрузку и выгрузку драйвера, а также ряд дополнительных структур. Пример дополнительных функций и структур данных, которые должны быть определены в динамически загружаемом драйвере операционной системы Solaris 2.5, приведен в табл. 5.2.

Таблица 5.2. Дополнительные функции и структуры данных для загружаемых драйверов

_inito Функция инициализации и установки, вызываемая при за-

грузке драйвера

_fini() Функция, вызываемая перед выгрузкой драйвера, удаляю-

щая его из системы

Таблица 5.2 (продолжение)

inf о ()

struct modlinkage struct modldrv

Функция, возвращающая информацию о драйвере по запросу ядра

Структура, используемая функциями _init(), _fini О и _inf оo при загрузке, выгрузке и получении информации о драйвере

Структура, экспортируемая ядру при загрузке драйвера, в частности, содержит адреса точек входа в драйвер

Помимо этого Solaris 2.5 предоставляет ряд функций ядра для работы с динамически загружаемыми драйверами: mod_install(9F), modremove (9F) и

mod info(9F).

1.5 Блочные устройства

Драйверы блочных устройств предназначены для обслуживания периферийного оборудования, обеспечивающего обмен данными с помощью фрагментов фиксированной длины, называемыми блоками, размер которых значительно превышает один байт. В основном эти драйверы используются файловой подсистемой и подсистемой управления памятью. Например, свопинг характеризуется обменом данными с устройством вторичной памяти, размер которых обычно равен размеру страницы, что составляет 4 или 8 Кбайт. Файловая подсистема производит чтение и запись данных фрагментами, размер которых равен одному или нескольким блокам устройства. Типичными представителями блочных устройств являются жесткий и гибкий диски.

Блочные устройства можно разделить на два типа в зависимости от того, используются ли они для хранения файловой системы или нет. Соответственно различается и схема доступа к этим устройствам. В последнем случае доступ к устройству осуществляется только через специальный файл устройства, представляющий интерфейс низкого уровня. Хотя обращение к устройствам, содержащим файловые системы, может также осуществляться через интерфейс низкого уровня, доступ к таким устройствам, как правило, осуществляется процессом косвенно, через запросы к файловой системе. Например, чтение или запись обычного файла вызывает операции с драйвером блочного устройства (жесткого диска), на котором расположена файловая система, хранящая данный файл. В этом случае обмен данными происходит при активном участии буферного кэша, позволяющего минимизировать число обращений непосредственно к физическому устройству.

Вообще говоря, операции ввода/вывода для блочного устройства могут быть вызваны рядом событий:

? Чтением или записью в обычный файл.

О Чтением или записью непосредственно в специальный файл устройства.

? Операциями подсистемы управления памятью: страничным замеще
нием или свопингом.

Доступ к блочным устройствам осуществляется с помощью трех основных точек входа: ххореп() , xxclose () и xxstrategy () . При этом за фактическое выполнение ввода/вывода отвечает xxstrategy ( ) . Единственным аргументом, передаваемым этой функции, является указатель на структуру buf, представляющую собой заголовок буфера обмена, с которой мы уже встречались в предыдущей главе при разговоре о буферном кэше. Структура buf содержит всю необходимую для операций ввода/вывода информацию. Основные поля структуры buf:

b^fiags Флаги. Определяют состояние буфера (например, b_busy

или B_DONE) и направление передачи данных (b_read,

B_WRITE, B_PHYS)

av__back, av forw

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

b bufsize

b un

. b_addr Виртуальный адрес буфера

bjoikno Номер блока начала данных на устройстве

b_bcount Число байтов, которые требуется передать

b_dev Старший и младший номера устройства

Использование заголовка buf при передачи блока данных показано на

рис. 5.7.

Ядро адресует дисковый блок, указывая vnode и смещение. Если доступ осуществляется к специальному файлу устройства, то смещение является физическим, отсчитываемым от начала устройства. Например, если специальный файл устройства /dev/dsk/cOtOdOsl обеспечивает доступ ко второму разделу жесткого диска, то смещение будет отсчитываться от начала этого раздела. Если vnode представляет обычный файл, то смещение является логическим, отсчитываемым от начала файла.

Таким образом, блок устройства, содержащего файловую систему, может быть адресован двумя способами -- либо через обычный файл и логическое смещение, либо через специальный файл устройства и физическое смещение на этом устройстве. Это, в свою очередь, может привести к различной идентификации одного и того же блока и, как следствие, двум различным копиям блока в памяти. Результатом такого несоответствия может стать потеря или нарушение целостности данных. Поэтому непосредственный доступ к специальному файлу такого устройства возможен только при размонтированной файловой системе.

Поскольку каждый дисковый блок связан с каким-либо файлом и соответственно с его vnode, а его образ в памяти -- с физическими страницами, которые также связаны с vnode (через структуры описания физической памяти -- page в SVR4, pfdat в SVR3), все операции ввода/вывода связаны с подкачкой и сохранением страниц и идентифицируются vnode.

1.6 Символьные устройства

Символьные устройства представляют собой значительную часть периферийного оборудования системы, включая терминалы, манипуляторы (например, мышь), клавиатуру и локальные принтеры. Основное отличие этих устройств от блочных заключается в том, что они, как правило, передают небольшие объемы данных.

Обмен данными с символьными устройствами происходит непосредственно через драйвер, минуя буферный кэш. При этом данные обычно копируются в драйвер из адресного пространства процесса, запросившего операцию ввода/вывода.

Если процесс сделал системный вызов ввода/вывода, например, read(2) или write(2) со специальным файлом символьного устройства, запрос направляется в файловую подсистему. Поскольку доступ к устройству обслуживается файловой системой specfs, рассмотренной ранее, в ответ на выполнение системного вызова процесса ядро выполняет вызов функции spec_read() или spec_write() соответственно для read(2) или write(2). Действия функций spec__read{) и spec_write () похожи. Обе проверяют тип vnode и определяют, что устройство является символьным. После этого с помощью коммутатора ядро выбирает соответствующую точку входа драйвера, используя старший номер, хранящийся в поле v_rdev vnode, и вызывает эту функцию (соответственно xxread () или xxwrite ()), передавая ей в качестве параметров старший и младший номера, ряд дополнительных параметров, зависящих от конкретного вызова, а также явно или неявно адресует область копирования данных в адресном пространстве процесса3.

1.6.1 Интерфейс доступа низкого уровня

Символьные драйверы обеспечивают доступ не только к символьным устройствам, например, к адаптеру последовательного или параллельного портов, манипулятору "мышь", монитору или терминалам. Часть символьных драйверов служит в качестве интерфейса доступа низкого уровня к блочным устройствам, таким как диски или накопители на магнитных лентах.

Большинство таких драйверов отличаются от соответствующих им драйверов блочных устройств характером выполнения операций ввода/вывода. В то время как драйверы блочных устройств производят обмен данными с буферным кэшем, драйверы доступа низкого уровня обеспечивают обмен данных непосредственно с адресным пространством процесса. Отсутствие посредника в виде буферного кэша устраняет необходимость в совершении дополнительных операций копирования (драйвер -- буферный кэш -- буфер процесса), но в то же время лишает процесс услуг кэширования данных, предоставляемых операционной системой.

Интерфейс доступа низкого уровня используется многими системными утилитами обслуживания файловой системы, например, fsck(lM), а также рядом приложений, работающих с накопителями на магнитной ленте, например tar(l) или cpio(l). Этот интерфейс используется некоторыми при-

Несколько иная схема применяется для драйверов подсистемы STREAMS, которые также имеют символьный интерфейс доступа. Эти драйверы будут рассматриваться в данной главе в разделе "Подсистема STREAMS".

ложениями, например СУБД, которые самостоятельно обеспечивают оптимизированные механизмы кэширования данных на уровне задачи.

Поскольку драйверы низкого уровня не используют буферный кэш, они самостоятельно обеспечивают необходимые буферы для совершения операции ввода/вывода. На рис. 5.8 показаны отличия в характере выполнения операции ввода/вывода с блочными устройствами в случаях, когда запрос формируется при участии буферного кэша (драйверы блочных устройств), и когда манипуляция буфером производится драйвером самостоятельно (драйверы низкого уровня).

1.6.2 Буферизация

Очевидно, что побайтная передача данных между драйвером символьного устройства и прикладным процессом весьма неэффективна. При таком режиме работы байт должен быть сначала скопирован в адресное пространство драйвера, затем некоторое время должно пройти, прежде чем драйвер сможет передать этот символ физическому устройству. Если при этом устройство оказывается занятым, процесс должен ожидать завершения предыдущей операции, что, скорее всего, вынудит его перейти в состояние сна и приведет к переключению контекста.

Существует несколько способов преодолеть данную ситуацию, но все они предполагают обеспечение некоторой буферизации данных драйвером устройства. Первый способ заключается в использовании прерываний, когда при поступлении на устройство следующего символа, генерируется аппаратное прерывание, которое обрабатывается функцией xxintr () драйвера независимо от функции xxwrite (). Функция обработки прерывания записывает данные в буфер, которые затем считываются функцией xxwrite ().

Если устройство не поддерживает прерываний, их поступление можно сэмулировать с помощью функции xxpoll () драйвера устройства, которая вызывается ядром через определенные промежутки времени (обычно каждый сигнал таймера). Обычно функция xxpoll (), в свою очередь, вызывает функцию xxintr (), скажем, на каждый десятый сигнал таймера, обеспечивая тем самым независимое считывание и буферизацию данных.

Буферизация данных для символьных устройств осуществляется с помощью специальных структур данных, называемых clist. Каждая структура clist имеет следующие поля:

Поле ссс содержит число символов в буфере cblock. Поля c_cf и с cl указывают, соответственно, на первый и последний элементы cblock, организованные в виде связанного списка и фактически обеспечивающие буферы хранения данных. Каждая структура cblock может хранить несколько символов. Когда буфер хранения заполняется, ядро автоматически выделяет новую структуру cblock и помещает ее в связанный список. Поля структуры cblock и их использование приведены на рис. 5.9.

Пример буферизации с использованием структуры clist в драйвере терминала показан на рис. 5.10.

Рис. 5.10. Пример использования буферов clist в драйвере терминала

1.6.3 Архитектура терминального доступа

Алфавитно-цифровой терминал -- последовательное устройство, и операционная система производит обмен данными с терминалом через последовательный интерфейс, называемый терминальной линией. С каждой терминальной линией в UNIX ассоциирован специальный файл символьного устройства /dev/ttyxx4.

Терминальные драйверы выполняют ту же функцию, что и остальные драйверы: управление передачей данных от/на терминалы. Однако терми-

В зависимости от версии UNIX вместо символов хх в имени файла терминала присутствует идентификатор, позволяющий поставить в соответствии специальному файлу конкретную терминальную линию. Например, в SCO UNIX виртуальные экраны системного монитора имеют имена /dev/ttyOl, /dev/tty02 и т. д.

налы имеют одну особенность, связанную с тем, что они обеспечивают интерфейс пользователя с системой. Обеспечивая интерактивное использование системы UNIX, терминальные драйверы имеют свой внутренний интерфейс с модулями, интерпретирующими ввод и вывод строк. Модуль, отвечающий за такую обработку, называется дисциплиной линии (line discipline).

Существует два режима терминального ввода/вывода:

Канонический режим. В этом режиме ввод с терминала обрабатывается в виде законченных строк.

Неканонический режим, при котором ввод не интерпретируется.

В каноническом режиме интерпретаторы строк преобразуют неструктурированные последовательности данных, введенные с клавиатуры, в каноническую форму (то есть в форму, соответствующую тому, что пользователь имел в виду на самом деле) прежде, чем послать эти данные принимающему процессу. Например, программисты работают на клавиатуре терминала довольно быстро, но иногда допускают ошибки. На этот случай имеется клавиша стирания, и пользователь имеет возможность удалять часть введенной строки и вводить коррективы. Драйвер терминала получает всю введенную последовательность, включая и символы стирания. В каноническом режиме модуль дисциплины линии буферизует информацию в строку (набор символов, заканчивающийся символом возврата каретки) и стирает символы в буфере, прежде чем переслать исправленную последовательность считывающему процессу. В таком режиме, например, работает командный интерпретатор shell.

В режиме без обработки строковый интерфейс передает данные между процессами и терминалом без каких-либо преобразований. Например, текстовый редактор работает с драйвером в неканоническом режиме, благодаря чему любой символ, введенный пользователем интерпретируется самим процессом.

В функции модуля дисциплины линии входят:

Построчный разбор введенных последовательностей.

Обработка символов стирания.

Обработка символов удаления, отменяющих всех предыдущих символов.

Отображение символов, полученных терминалом.

Расширение выходных данных, например, преобразование символов табуляции в последовательности пробелов.

Предоставление возможности не обрабатывать специальные символы, такие как символы стирания, удаления и возврата каретки.

Существует дополнительная возможность обработки данных, получаемых и передаваемых устройству -- отображение вводимых и выводимых символов в символы, определенные таблицей отображения. Данную возможность поддерживает утилита mapchan.

1.6.4 Псевдотерминалы

Псевдотерминалы являются специальным устройством, эмулирующим стандартную терминальную линию. Псевдотерминалы напоминают каналы как средство межпроцессного взаимодействия, позволяющее двум процессам обмениваться данными. Однако в отличие от каналов, псевдотерминалы обеспечивают дополнительную функциональность, специфичную для терминальных линий. Схематически архитектура псевдотерминала представлена на рис. 5.11.

Ярким примером использования псевдотерминалов является регистрация в системе по сети с использованием серверов удаленного доступа rlogin(l) или telnet(l), или использование графического эмулятора терминала xterm в системе X Window System. Когда пользователь регистрируется в системе подобным образом, псевдотерминал эмулирует обычную терминальную линию, поэтому пользователь не видит различия между удаленной и локальной работой с помощью терминала, подключенного по последовательной линии. Например, пользователь может установить различные режимы обработки и использовать соответствующие комбинации клавиш для генерации сигналов, как он это делает в случае обычного терминала.

Рис. 5.11. Взаимодействие процессов с помощью псевдотерминала

Псевдотерминал по существу представляет собой два отдельных драйвера. Один из них выглядит как обычный терминальный драйвер и носит название подчиненного устройства (slave). Второй драйвер называется основным (master).

драйвер ядро интерфейс данные

1.6.5 Архитектура терминального лоступа

Поскольку подчиненное устройство имеет все характеристики терминала, процесс может связать свои стандартные потоки ввода, вывода и вывода ошибок с этим устройством. Однако в отличие от обычного терминала, в случае которого запись процесса приводит к отображению данных на физическом устройстве, а ввод данных пользователем с клавиатуры может быть получен чтением терминальной линии, все данные, записанные в подчиненное устройство, передаются основному и наоборот -- почти так, как работает канал. Однако модуль дисциплины линии позволяет обеспечить дополнительные возможности этого канала, которые могут потребоваться некоторым приложениям, например, командному интерпретатору shell.

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

Пользователь удаленной системы запускает программу удаленного доступа rlogin(l), которая формирует запрос и передает его по сети на требуемый компьютер. Там этот запрос доставляется серверу удаленного доступа rlogind(l), который (после надлежащей проверки) запускает программу login(l). При этом стандартные потоки ввода, вывода и вывода ошибок программы login(l) связываются не с терминальным файлом, как в случае входа в систему с помощью сервера getty(lM), а с подчиненным устройством псевдотерминала. Основное же устройство оказывается связанным с сервером rlogind(l). Программа login(l) запрашивает имя пользователя и его пароль точно так же, как она это делает при входе через getty(lM). Более того, login(l) и "не представляет", что на самом деле работает с эмулятором терминала, а не с традиционной терминальной линией. Весь ввод login(l) поступает серверу rlogind(l) и затем передается по сети клиентской части rlogin(l) на удаленном компьютере. Далее работа ничем не отличается от работы локального пользователя, подключенного к системе с помощью обыкновенного терминала или консоли. Если имя пользователя и пароль были введены правильно, программа login(l) запустит требуемый командный интерпретатор (login shell), который также не заметит подмены. Действительно, по всем характеристикам терминал будет неотличим от традиционной последовательной линии, включая различные установки и генерацию сигналов при нажатии определенных клавиш клавиатуры. Следует, правда, оговориться, что поскольку псевдотерминал не является "полноценным" терминальным устройством, часть установок для него не имеют смысла (например, скорость передачи, четность и т. д.) и просто игнорируются.


Подобные документы

  • Исследование типовой структуры шины персонального компьютера. Подсистема ввода-вывода в ядре операционной системы. Преобразование запросов на ввод-вывод в аппаратные операции. Блочные, символьные и сетевые устройства. Процесс чтения из дискового файла.

    презентация [1,8 M], добавлен 24.01.2014

  • Характеристика, разновидности, архитектура процессоров. Понятие интерфейса, описание видов шин, внешних запоминающих устройств, особенности конструкции. Специфика файловой системы устройства подсистемы ввода/вывода, достоинства, недостатки, база данных.

    курс лекций [747,0 K], добавлен 24.06.2009

  • Изучение подсистемы ввода-вывода и файловой системы ОС семейства Windows NT. Анализ особенностей работы приложения TotalCommander и его взаимодействия с файловой системой и подсистемой ввода-вывода. Взаимодействие TotalCommander с сетевыми адаптерами.

    лабораторная работа [1,1 M], добавлен 12.06.2012

  • Характеристика дискретного управления доступом. Особенности модели тип-домен, основанной на концепции минимальных привилегий. Unix-система права доступа файлов. Контролирование администратором доступа в мандатной системе, проблемы ее использования.

    реферат [253,2 K], добавлен 09.01.2012

  • Разработка и реализация компонентов "Интерфейс администратора", "Виртуальная лаборатория" системы удаленного доступа к вычислительным ресурсам. Определение функций клиента. Построение ER-модели базы данных системы УД и УРВР; архитектура и требования.

    дипломная работа [5,5 M], добавлен 26.05.2015

  • Администратор источников данных ODBC: его запуск и принципы работы, возможности эксплуатации и управления. Вкладка "Пользовательское DSN", ее содержание и структура. Библиотеки для доступа к ODBC, типы используемых данных. Функция SQLAllocHandle.

    презентация [485,0 K], добавлен 06.01.2014

  • Разработка программного обеспечения для упрощения буквенно-цифрового ввода при невозможности использовать функционал стандартной буквенной клавиатуры. Классификация и установка драйверов. Выбор языка и среды программирования. Пользовательский интерфейс.

    курсовая работа [183,0 K], добавлен 12.03.2013

  • Основные классификации операционных систем. Операционные системы семейства OS/2, UNIX, Linux и Windows. Разграничение прав доступа и многопользовательский режим работы. Пользовательский интерфейс и сетевые операции. Управление оперативной памятью.

    реферат [22,8 K], добавлен 11.05.2011

  • Разработка устройств ввода данных. Типичная адаптированная под русский алфавит клавиатура. Графический манипулятор мышь. Устройства вывода данных из компьютера. Сервисные режимы печати на принтерах. Интерфейс для подключения сканера к компьютеру.

    реферат [337,4 K], добавлен 11.01.2011

  • Программное обеспечение системы "DeltaV". Подсистема ввода-вывода. Загрузка данных в контроллер. Интерфейс пользователя, навигация и клавиатурные вызовы. Создание, изменение и удаление модулей и шаблонов составных модулей управляющей стратегии.

    реферат [750,7 K], добавлен 14.04.2016

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.