Жанр: Учеба

Приемы профессиональной работы в Unix

сторінка №25

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

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


1 #include

3 struct termio tsav, tchg;

5 main (argc, argv)
6 {
7 int c;

9 if (ioctl (0, TCGETA, &tsav) == -1) {
10 perror("can't get original settings");
невозможно получить исходные установки
11 exit(1);
12 }

14 tchg = tsav;

16 tchg.c_lflag &= ~(ICANON | ECHO);
17 tchg.c_cc[VMIN] = 1;
18 tchg.c_cc[VTIME] = 0;

20 if (ioctl (0, TCSETA, &tchg) == -1) {
21 perror("can't initiate new settings");
невозможно задать новые установки
22 }

24 while (1)
25 {
26 c = getchar();

28 if (c == 'x')
29 break;

31 putchar©;
32 }

34 if (ioctl(0, TCSETA, &tsav) == -1) {
35 perror("can't reset original settings");
невозможно вернуть исходные установки
36 exit(3);
37 }
38 }

У нас есть две "терминальные" структуры данных, одна из которых
содержит исходные установки, а другая - установки, которые мы изменяем
и записываем. Первый системный вызов ioctl получает информацию об
установках терминала. Затем мы присваиваем эти значения изменяемой
структуре (строка 14). Модификации терминального интерфейса мы выполняем
в строках 16-18. Строка 16 отключает каноническую обработку и
эхо-отображение символов. Строка 17 устанавливает, что минимальное количество
нажатий на клавиши равно одному. Строка 18 определяет, что
время ожидания для повторного чтения данных равно 0. По существу, это
блочное чтение по одному символу.

Новые значения терминальных характеристик устанавливаются в строке
20. В этот момент режим работы терминала меняется. Цикл while читает,
проверяет и выводит символы. Только при вводе символа x цикл завершается,
терминал устанавливается в первоначальное состояние, и
программа заканчивает работу.


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

Этот вопрос эквивалентен такому вопросу: "Как опросить клавиатуру
в UNIX?". Опрос является весьма важным приемом для некоторых применений.
Опрос работает примерно так: "Посмотреть на клавиатуру. Если введен
символ, получить его и каким-то образом обработать. В противном
случае продолжать делать то, что вы делали. После истечения интервала
времени, определенного программой, проверить клавиатуру снова." Таким
образом, если пользователь не нажимает на клавиши, программа продолжает
работу, а не ожидает, пока что-нибудь будет нажато на клавиатуре.

Для выполнения такой работы нам нужно несколько более подробно
рассмотреть терминальный интерфейс. Как было отмечено ранее, терминал
представляет собой файл. Это значит, что он должен обладать всеми
обычными свойствами файлов - возможностью открытия, закрытия, чтения,
записи и т.д. Мы также видели, что терминалы имеют протокол работы,
который может быть изменен командой stty. Мы видели, что для получения
одного символа с клавиатуры используется протокол работы. Теперь мы
увидим, что для выполнения опроса вы должны использовать технику, которая
относится к файлам, а не ioctl.

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

1 #include
2 #include

4 struct termio tsav, tchg;

6 main (argc, argv)
7 {
8 int c;

10 /* change the terminal based on file primitives */
изменить режим терминала с помощью файловых примитивов
11 close(0);
12 if (open("/dev/tty",O_RDWR|O_NDELAY) == -1) {
13 perror("can't open tty");
невозможно открыть tty
14 exit(1);
15 }

17 /* change the terminal based on line disciplines */
изменить режим терминала с помощью протокола работы
18 if (ioctl (0, TCGETA, &tsav) == -1) {
19 perror("can't get original settings");
невозможно получить исходные установки
20 exit(2);
21 }

23 tchg = tsav;

25 tchg.c_lflag &= ~(ICANON | ECHO);
26 tchg.c_cc[VMIN] = 1;
27 tchg.c_cc[VTIME] = 0;

29 if (ioctl (0, TCSETA, &tchg) == -1) {
30 perror(can't initiate new settings");
невозможно задать новые установки
31 }

33 while (1)
34 {
35 putchar('.');
36 c = getchar();

38 if (c == 'x')
39 break;

41 putchar©;
42 }

44 if (ioctl(0, TCSETA, &tsav) == -1) {
45 perror("can't reset original settings");
невозможно вернуть исходные установки
46 exit(3);
47 }
48 }

Основное изменение производится в строках 11-15. Закрытие файла с
нулевым дескриптором (который обозначает стандартное устройство ввода)
закрывает стандартный ввод. Затем мы снова открываем файл /dev/tty.
Значение дескриптора файла равно нулю, так что мы переназначили стандартный
ввод на новое устройство. Фокус в том, что при открытии файла
используется режим, называемый NODELAY. Это означает, что когда выполняется
чтение по данному дескриптору файла (т.е. чтение стандартного
ввода), вместо ожидания ввода выполняется просмотр, есть ли там
что-нибудь, а затем работа продолжается.

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

ВОЗМОЖНОСТИ ТЕРМИНАЛОВ

Теперь, когда мы имеем понятие о характеристиках терминальных интерфейсов,
давайте перейдем к возможностям терминалов. ВОЗМОЖНОСТИ -
это те функции, которые выполняет аппаратура терминала. Если мы знаем
эту информацию, мы можем создать список возможных функций и использовать
его, например, для работы редактора vi. Это осуществляется при
помощи специального файла данных termcap (terminal capabilities - возможности
терминала), который описывает возможности терминала.

Большинство из существующих типов терминалов уже занесены в файл
termcap. Это файл /etc/termcap. Файл termcap и редактор vi происходят
из системы Berkeley. Такая комбинация оказалась настолько эффективной,
что была перенесена в System V. В более поздней System V Release 3
файл termcap уже не используется, его заменяет файл terminfo фирмы
AT&T. Мы применяли файл terminfo совместно с командным файлом today в
главе 5, но подробное обсуждение terminfo выходит за пределы нашей
книги. В системе Berkeley файл termcap по-прежнему остается стандартом,
и он заслуживает более детального рассмотрения.

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

В качестве примера мы приводим запись файла termcap для компьютера
Apple II. Это описание распространено в различных формах, но наш
пример относится к видеоплате Videx UltraTerm для Apple II+. Заметим,
что возможности, предоставляемые файлом termcap, являются обычно подмножеством
тех возможностей, которые фактически предоставляет аппаратура.
В частности, видеоплата в компьютере Apple выполняет некоторые
функции, которые не умеет делать файл termcap, например комбинации
настроечных битов для изменения видеоатрибутов. Самое большее, что мы
можем сделать с видеоатрибутами посредством файла termcap, это включить
или выключить инверсное отображение.

С другой стороны, некоторые типы аппаратуры не обладают всеми
возможностями, обеспечиваемыми файлом termcap. Например, одной из
функций, которой недостает в Apple, является функция прокрутки
("scroll reverse"). Аппаратура не делает этого, поэтому и в termcap
нет необходимости иметь описание этой функции. Вместо скроллинга
(прокрутки) вниз, отображаемый на экране текст продолжает выводиться в
верхней строке.

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

Приведем пример содержимого файла termcap:

a2|aii|Apple II with UltraTerm :\
:bl=^G:\
:bs:\
:cd=^K:\
:ce=^]:\
:cl=^L:\
:cm=^^%r%+ %+ :\
:co#80:\
:cr=^M:\
:do=^J:\
:ho=^Y:\
:kb=^H:\
:kd=^J:\
:kl=^H:\
:kr=^\\:\
:ku=^_:\
:le=^H:\
:li#24:\
:nd=^\\:\
:nl=^J:\
:se=^O:\
:so=^N:\
:up=^_:

В табл. 7-1 представлен список функций файла termcap с сопоставлением
терминалов Apple и vt52. Если какая-либо функция отсутствует у
одного или другого терминала, это отмечается словом "нет".

Таблица 7-1

Терминальные возможности и их конкретные значения

---------------------------------------------------------------------------

Функция Apple II vt52
---------------------------------------------------------------------------

bl - звуковой сигнал ^G ^G
(bell)
bs - возврат на шаг по коду ^H да да
(can backspace with ^H)
cd - очистка до конца экрана ^K \EJ
(clear to end of display)
ce - очистка до конца строки ^] \EK
(clear to end of line)
cl - очистка всего экрана ^L \EH\EJ
(clear entire screen)
cm - движение курсора ^^%r%+ %+ \EY%+ %+
(cursor motion)
co - число позиций в строке #80 #80
(number of columns in a line)
cr - возврат каретки ^M ^M
(carriage return)
do - сдвиг на строку вниз ^J ^J
(down one line)
ho - курсор в начало экрана
(без команды cm) ^Y \EH
(home cursor)
kb - код клавиши backspace ^H ^H
(sent by backspace key)
kd - код клавиши "стрелка вниз" ^J \EB
(sent by down arrow key)
kl - код клавиши "стрелка влево" ^H \ED
(sent by left arrow key)
kr - код клавиши "стрелка вправо" ^\\ \EC
(sent by right arrow key)
ku - код клавиши "стрелка вверх" ^_ \EA
(sent by up arrow key)
le - курсор влево ^H ^H
(cursor left)
li - число строк экрана #24 #24
(number of lines per screen)
nd - нестирающий пробел ^\\ \EC
(nondestructive space)
nl - символ перевода строки ^J ^J
(newline character)
pt - наличие аппаратной табуляции нет да
(has hardware tabs)
se - обычный экран ^O нет
(end stand out mode (normal))
so - инверсный экран ^N нет
(begin stand out mode (inverse))
sr - прокрутка нет \EI
(scroll reverse)
ta - символ табуляции ^I ^I
(tab)
up - сдвиг вверх на строку нет ^_
(up a line)
---------------------------------------------------------------------------

Самое интересное здесь, наверное, то, что терминалы vt52 и Apple
имеют взаимно обратный порядок указания координат в команде движения
курсора. Терминал vt52 воспринимает значения x и y в порядке YX, что
является умолчанием для файла termcap. Apple воспринимает их в порядке
XY, поэтому в записи файла termcap требуется поменять координаты
местами, что указано обозначением %r в функции cm.

Файл termcap позволяет вам спрятать основную информацию о специфических
характеристиках терминала (за исключением характеристик, которые
могут отсутствовать у терминала, или специальных возможностей,
которые не описаны в termcap). Это значит, что вы можете создавать
терминально-независимые программы. При этом вам нет необходимости изменять
все специфические обращения к терминалу, такие как ESC-последовательности
(символы, указывающие терминалу, что передаваемые после
них символы (символ) должны интерпретироваться как управляющие коды).
Это символы (\E) для терминала vt52 и (^) для Apple.

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

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

---------------------------------------------------------------------------

ИМЯ: c
---------------------------------------------------------------------------

c Быстрая очистка экрана

НАЗНАЧЕНИЕ

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

ФОРМАТ ВЫЗОВА

c

ПРИМЕР ВЫЗОВА

c Очистка экрана

ТЕКСТ ПРОГРАММЫ

1 char id[] = "@(#) c v1.0 Fast clear screen Author: Russ Sage";
Быстрая очистка экрана

3 #define FF "\014"

5 main()
6 {
7 if (write(1, FF, 1) == -1)
8 write(2,"c: write error to stdout\n",25);
ошибка записи в стандартный вывод
9 }

ОПИСАНИЕ

ЗАЧЕМ НАМ НУЖНА ПРОГРАММА c?

В System V уже имеется команда для очистки экрана терминала - это
команда clear. Она работает путем определения типа вашего терминала и
затем вывода на экран символа очистки для данного терминала. Все прекрасно,
но есть один существенный недостаток: она очень МЕДЛЕННАЯ!

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

ЧТО ДЕЛАЕТ c?

Программа 'c' выводит на экран символ очистки настолько быстро,
насколько быстро может выполняться операция ввода-вывода в UNIX. Применяя
прямой системный вызов, мы избавляемся от необходимости запускать
другую программу. Поэтому программа 'c' работает очень быстро.
Мы уверены, что точно такую же функцию можно вызывать как команду
Си-shell (поместить в csh alias), поэтому данная программа наиболее
полезна тем, кто работает в System V.

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

ПОЯСНЕНИЯ

Первым делом мы должны найти в файле termcap код очистки экрана.
Для терминала Apple это код ^L, а для vt52 это \EH\EJ. Как только вы
найдете этот код, вставьте его в оператор define в строке 3 или сразу
в оператор write в строке 7. В приведенном примере в качестве символа
очистки экрана используется ^L.

Наиболее быстрым способом передачи символа в файл является непосредственное
выполнение оператора write. Поскольку терминалы являются
файлами, мы можем выполнять запись непосредственно в них, пользуясь
преимуществом предопределенных дескрипторов файла 0,1 и 2.

Системный вызов write в строке 7 посылает символ очистки в файл с
дескриптором 1, который является стандартным устройством вывода. Если
операция записи неудачна (по ряду причин), то в файл с дескриптором 2,
т.е. на стандартное устройство регистрации ошибок, выводится сообщение
об ошибке. Здесь не проверяется, успешно ли завершилась запись на
стандартное устройство регистрации ошибок. Если ошибка все-таки возникнет,
то мы ее увидим.

Программа не использует НИКАКИХ возможностей стандартного ввода-вывода
(stdio). НИКОГДА нельзя смешивать системные вызовы ввода-вывода
(т.е. вызовы из раздела (2) документации по системным функциям,
например read или write) со стандартными вызовами ввода-вывода (т.е.
вызовами из раздела (3), такими как getchar и printf). Дополнительный
буфер, который создается при выполнении функций stdio, не согласован
во времени с системными вызовами, поэтому все выходные сообщения перемешиваются.


Еще один аспект, о котором мы должны помнить, принимая решение об
использовании системных вызовов, это преимущество получения как можно
более короткого объектного кода. Небольшая программа загружается и работает
быстрее. Для того, чтобы ненужные подпрограммы стандартного
ввода-вывода не включались в наш объектный модуль, в исходном тексте
программы не делается никаких ссылок на подпрограммы stdio. Тем не менее,
ваша система могла их каким-то образом включить. Так поступает
XENIX, а вместе с stdio вызывается malloc и все остальное. Вы можете
просмотреть таблицу символов вашего объектного модуля с помощью nm(1)
или nlist(2). Вы увидите весь мусор, который был добавлен в ваш объектный
модуль. Не так редко мы получаем 6 Кб кода всего лишь для одного
оператора printf! Приучайтесь программировать непосредственно на
ассемблере, чтобы достичь того, что вам нужно.

ИССЛЕДОВАНИЯ

Когда эта программа была написана, возник вопрос: "Каким образом
мы можем проверить неудачу записи на стандартное устройство вывода?".
Раньше такой вопрос не стоял, но показалось, что неплохо было бы это
сделать. Решение было найдено на страницах описания sh(1). Способ, которым
можно вызвать ошибку выполнения записи на стандартное устройство
вывода, заключается в том, что нужно закрыть дескриптор файла стандартного
устройства вывода. Это легко делается с помощью команды exec,
которая является внутренней по отношению к shell:

$ exec "&-

Эта команда переназначает файловый дескриптор 1 стандартного вывода
(обозначение ") на дескриптор файла (&) закрытого устройства (-).
Такой эксперимент может оказаться полезным для более полной отладки
ваших программ.

ДИСКОВЫЕ УСТРОЙСТВА

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

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

РАЗБИЕНИЕ ДИСКОВ НА РАЗДЕЛЫ

Части, или области диска, известны как разделы. Раздел может содержать
файловую систему, которая сгенерирована командой mkfs(1), или
же может содержать неструктурированные данные, доступ к которым выполняется
с помощью команды 'cpio -o'.

В системе XENIX управление разделами осуществляется программой
fdisk, которая концептуально подобна своей тезке в системе MS-DOS. В
других системах UNIX используются другие имена. Например, в системе
AT&T 7300 UNIX PC используется программа iv, что значит "format" (хотите
верьте, хотите нет). Как упоминалось ранее, обычно разделы содержат
одну файловую систему. В настоящее время в системах XENIX и SCO
XENIX у вас есть возможность "разделить раздел" на более мелкие части
для получения большего количества файловых систем. Это сделано по той
причине, что машины с системами DOS и XENIX ограничены четырьмя дисковыми
разделами, а у вас может возникнуть желание иметь больше файловых
систем, чем число доступных разделов. В системе AT&T 7300 UNIX PC управление
разделами диска осуществляется по списку начальных номеров
дорожек. Вы можете создать столько разделов, сколько хотите. Каждый
компьютер имеет свои преимущества и недостатки.

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

---------------------------------------------------------------------------

|
| brw------- 1 sysinfo sysinfo 1, 0 Feb 18 17:07 /dev/hd00
| brw------- 1 sysinfo sysinfo 1, 15 Feb 18 16:59 /dev/hd01
| brw------- 1 sysinfo sysinfo 1, 23 Feb 18 16:59 /dev/hd02
| brw------- 1 sysinfo sysinfo 1, 31 Feb 18 16:59 /dev/hd03
| brw------- 1 sysinfo sysinfo 1, 39 Feb 18 16:59 /dev/hd04
| brw------- 1 sysinfo sysinfo 1, 47 Feb 18 17:07 /dev/hd0a
| brw------- 1 sysinfo sysinfo 1, 55 Feb 18 17:09 /dev/hd0d
| crw------- 1 sysinfo sysinfo 1, 0 Feb 18 16:59 /dev/rhd00
| crw------- 1 sysinfo sysinfo 1, 15 Feb 18 16:59 /dev/rhd01
| crw------- 1 sysinfo sysinfo 1, 23 Feb 18 16:59 /dev/rhd02
| crw------- 1 sysinfo sysinfo 1, 31 Feb 18 16:59 /dev/rhd03
| crw------- 1 sysinfo sysinfo 1, 39 Feb 18 16:59 /dev/rhd04
| crw------- 1 sysinfo sysinfo 1, 47 Feb 18 16:59 /dev/rhd0a
| crw------- 1 sysinfo sysinfo 1, 55 Feb 18 17:09 /dev/rhd0d
|

Имена файлов с префиксом hd указывают блочные устройства, а с
префиксом rhd - "неструктурированные" символьные устройства. Не все
символьные устройства являются неструктурированными блочными устройствами.
Терминалы являются символьными устройствами, как мы уже видели
ранее в данной главе. В табл. 7-2 показаны различные характеристики
этих двух типов устройств.

Таблица 7-2

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

---------------------------------------------------------------------------

Блочное устройство Символьное устройство
---------------------------------------------------------------------------

/dev/hd0, /dev/fd0 /dev/rhd0, /dev/rfd0

буфер управляется ядром буферизация отсутствует,
системы, медленное устройство быстрое устройство

произвольное размещение последовательное размещение
блоков данных блоков данных

доступ через файловую доступ непосредственно
систему на диск

cpio -p cpio -o, -i

mkfs, mount, df, du tar
fsck, fsdb

---------------------------------------------------------------------------

Как видите, существует много способов работы с устройствами.

Давайте рассмотрим устройство /dev/hd01 из приведенного выше
списка. Если вы хотите адресоваться к физическому разделу на диске как
к блочному устройству, вы можете создать на нем файловую систему. Для
этого вам нужно выполнить следующую команду, которая создаст файловую
систему размером 5000 Кб (5 Мб) на жестком диске:

# mkfs /dev/hd01 5000

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

# mount /dev/hd01 /mount_pt

Файлы могут быть помещены в дисковый раздел командами mv или cp,
путем переадресации вывода в каталог с этим именем, например,
"/mount_pt/file.

Для использования раздела диска в качестве области неструктурированных
данных, а не блочного устройства, применяйте файл с именем символьного
устройства, которое начинается с буквы r. Например, для
использования того же устройства, что и в предыдущем примере, в качестве
неструктурированного устройства, укажите имя /dev/rhd01. (Из
списка устройств вы видите, что это символьное устройст

Список сторінок

Поділитися у соцмережах

☏ Замовлення реклами: +3805044-68-872

© Асоціація електронних бібліотек України

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