Файл: Debian Таненбаум Бос.pdf

ВУЗ: Не указан

Категория: Книга

Дисциплина: Операционные системы

Добавлен: 29.10.2018

Просмотров: 48215

Скачиваний: 190

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
background image

10.5. Ввод-вывод в системе Linux   

841

124-й блок (и для этого не надо читать сначала блоки с 0-го по 123-й). Блочные специ-
альные файлы обычно используются для дисков.

Символьные специальные файлы

 (character special files)   обычно используются для 

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

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

 (major device) , предназначенный для его идентификации. Если драйвер 

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

/dev/tty

 драйвер управляет и клавиатурой, и экраном, 

которые часто воспринимаются как единое устройство — терминал.

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

Backspace

, другие любят пользоваться 

клавишей 

Del

. Для удаления всей только что набранной строки тоже имеется большой 

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

Ctrl+U

 или других символов. Особая клавиша 

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

Ctrl+C

, но это не универсальный вариант.

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

10.5.2. Работа с сетью

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

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


background image

842  

 Глава 10. Изучение конкретных примеров: Unix, Linux и Android 

Рис. 10.12. Использование сокетов для работы сети

Каждый сокет поддерживает определенный тип работы в сети, указываемый при со-
здании сокета. Наиболее распространенными типами сокетов являются:

1.  Надежный ориентированный на соединение поток байтов.

2.  Надежный ориентированный на соединение поток пакетов.

3.  Ненадежная передача пакетов.

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

Второй тип сокетов отличается от первого тем, что он сохраняет границы пакетов. 
Если отправитель пять раз сделал системный вызов write, каждый раз отправляя по 
512 байтов, а получатель запрашивает 2560 байтов, то при типе сокета 1 он получит все 
2560 байтов сразу. При использовании сокета типа 2 ему будут выданы только первые 
512 байтов. Чтобы получить остальные байты, получателю придется выполнить си-
стемный вызов read еще четыре раза. Третий тип сокета предоставляет пользователю 
доступ к «голой» сети. Этот тип сокета особенно полезен для приложений реального 
времени и для тех ситуаций, в которых пользователь хочет реализовать специальную 
схему обработки ошибок. Сеть может терять пакеты или доставлять их в неверном по-
рядке. В отличие от сокетов первых двух типов, сокет типа 3 не предоставляет никаких 
гарантий. Преимущество этого режима заключается в более высокой производительно-
сти, что иногда важнее надежности (например, для доставки мультимедиа, при которой 
скорость ценится существенно выше, чем сохранность данных).

При создании сокета один из параметров указывает используемый для него про-
токол. Для надежных байтовых потоков, как правило, используется протокол TCP   
(Transmission Control Protocol — протокол управления передачей). Для ненадежной 
передачи пакетов обычно применяется протокол UDP   (User Data Protocol — поль-
зовательский протокол данных). Оба они работают поверх протокола IP (Internet 
Protocol). Все эти протоколы были разработаны для сети ARPANET Министерства 
обороны США и теперь составляют основу Интернета. Для надежного потока пакетов 
общепринятого протокола нет.


background image

10.5. Ввод-вывод в системе Linux   

843

Прежде чем сокет может быть использован для работы в сети, с ним должен быть 
связан адрес. Этот адрес может принадлежать к одному из нескольких пространств 
адресов. Наиболее распространенным пространством является пространство адресов 
Интернета, использующее 32-разрядные целые числа (для идентификации конеч-
ных адресатов) в протоколе IP v 4 и 128-разрядные целые числа в протоколе IP v 6 
(5-я версия протокола IP была экспериментальной системой, так и не выпущенной 
в свет).

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

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

10.5.3. Системные вызовы ввода-вывода в Linux

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

Первые четыре перечисленных в табл. 10.6 вызова используются для установки и опре-
деления скорости терминала. Для управления вводом и выводом предоставлены разные 
вызовы, так как некоторые модемы работают в несимметричном режиме. Например, 
старые системы videotex предоставляли пользователям доступ к открытым базам 
данных с короткими запросами (от дома до сервера) на скорости 75 бит/с и ответами, 
посылаемыми со скоростью 1200 бит/с. Этот стандарт был принят в то время, когда 
скорость 1200 бит/с в обоих направлениях была слишком дорогой для домашнего 
использования. Однако времена в сетевом мире изменились. Но асимметрия все еще 
сохраняется на многих линиях связи. Например, некоторые телефонные компании 
предоставляют услуги цифровой связи ADSL  (Asymmetric Digital Subscriber Line — 
асимметричная цифровая абонентская линия) с входящим потоком на скорости 
20 Мбит/с и исходящим потоком на скорости 2 Мбит/с.


background image

844  

 Глава 10. Изучение конкретных примеров: Unix, Linux и Android 

Таблица 10.6. Основные вызовы стандарта POSIX для управления терминалом

Вызов

Описание

s=cfsetospeed(&termios, speed)

Задать исходящую скорость

s=cfsetispeed(&termios, speed)

Задать входящую скорость

s=cfgetospeed(&termios, speed)

Получить исходящую скорость

s=cfgtetispeed(&termios, speed)

Получить входящую скорость

s=tcsetattr(fd, opt, &termios)

Задать атрибуты

s=tcgetattr(fd, &termios)

Получить атрибуты

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

10.5.4. Реализация ввода-вывода в системе Linux

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

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

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


background image

10.5. Ввод-вывод в системе Linux   

845

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

Устройство

Open

Close

Read

Write

Ioctl

Other

Null

null

null

null

null

null

ј

Память

null

null

mem_read

mem_write

null

ј

Клавиатура

k_open

k_close

k_read

error

k_ioctl

ј

Tty

tty_open

tty_close

tty_read

tty_write

tty_ioctl

ј

Принтер

lp_open

lp_close

error

lp_write

lp_ioctl

ј

Каждый драйвер разделен на две части, причем обе они являются частью ядра Linux 
и работают в режиме ядра. Верхняя часть драйвера работает в контексте вызывающей 
стороны и служит интерфейсом к остальной системе Linux. Нижняя часть работает 
в контексте ядра и взаимодействует с устройством. Драйверам разрешается делать вы-
зовы процедур ядра для выделения памяти, управления таймером, управления DMA 
и т. д. Набор функций ядра, которые они могут вызывать, определен в документе под 
названием «Интерфейс драйвер — ядро» (Driver-Kernel Interface) . Создание драйве-
ров для системы Linux подробно описано в работах Cooperstein (2009) и Corbet et al. 
(2009).

Система ввода-вывода разделена на два основных компонента: обработку блочных 
специальных файлов и обработку символьных специальных файлов. Сейчас мы рас-
смотрим эти компоненты по очереди.

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

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

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

Кэш страниц работает не только при чтении, но и при записи. Когда программа пишет 
блок, то этот блок не попадает напрямую на диск, а отправляется в кэш. Демон pdflush 
сбросит блок на диск тогда, когда кэш вырастет свыше установленного значения. Чтобы 
блоки не хранились в кэше слишком долго, принудительный сброс на диск «грязных» 
блоков производится каждые 30 с.