ГЛАВА 7 УСТРОЙСТВА И ФАЙЛОВЫЕ СИСТЕМЫ ТЕРМИНАЛЬНЫЕ УСТРОЙСТВА РАБОТА С КЛАВИАТУРОЙ ФАЙЛЫ termcap ДИСКОВЫЕ УСТРОЙСТВА ФАЙЛОВЫЕ СИСТЕМЫ ЗАГРУЗОЧНЫЙ ДИСК РАЗМЕРНЫЕ ПАРАМЕТРЫ c Быстрая очистка экрана mntf Монтирование гибкого диска в системном дереве mntlook Поиск всех монтированных файловых систем umntsys Размонтирование всех файловых систем, кроме корневой lrgf Создание файла максимального размера, допустимого в вашей системе УСТРОЙСТВА И ФАЙЛОВЫЕ СИСТЕМЫ ВВЕДЕНИЕ Ниже уровня известной нам области файловых систем находится мир устройств и их драйверов. В данной главе мы исследуем некоторые мето- ды, необходимые для работы с терминалами, дисками и непосредственно файловыми системами. Программное средство 'c' иллюстрирует доступ к терминалу на примере операции быстрой очистки экрана. Следующие три средства - mntf, mntlook и umntsys - имеют дело с монтированием и раз- монтированием файловых систем. Наконец, средство lrgf позволит вам проверить пределы емкости вашей файловой системы. СИСТЕМА UNIX И АППАРАТУРА Перед тем, как углубиться в сущность вопроса, давайте обсудим не- которые элементарные факты, которые мы должны помнить при рассмотрении всех составляющих системы UNIX. Сердцем аппаратуры является централь- ный процессор (CPU), который исполняет инструкции, управляющие маши- ной, и фактически осуществляет всю работу. Операционная система необ- ходима для руководства работой, выполняемой центральным процессором, и для обеспечения интерфейса между ним и ресурсами, требуемыми для того, чтобы сделать что-то полезное: оперативной памятью, внешней памятью и другими периферийными устройствами, такими как терминалы и принтеры. Операционная система, особенно такая высокоразвитая, как UNIX, имеет множество утилит и характерных особенностей, но сейчас речь не об этом. Сердцем операционной системы (в данном случае UNIX) является ядро (kernel). Ядро управляет процессами и руководит выполняемой рабо- той. Оно также является своего рода мостом между аппаратурой и внешним миром. В данной главе мы обратим внимание на основные взаимоотношения между ядром, процессами и аппаратурой. В конечном итоге система должна взаимодействовать с внешними уст- ройствами. Наличие базовых знаний об устройствах весьма важно для пол- ного понимания того, как UNIX общается с внешним миром. При работе с машиной много времени тратится на передачу данных в машину и из нее, а это значит, что необходимо иметь дело со множеством различных типов устройств, каждое из которых имеет свой "характер" и особенности. К нашему счастью, UNIX был разработан так, чтобы облегчить управ- ление данными и устройствами настолько, насколько это возможно. К на- шему несчастью, имеется, по всей видимости, несократимый объем знаний, которыми мы должны овладеть обязательно. На рис. 7-1 показана общая структура операционной системы UNIX. Мы видим, что со стороны ядра об- ращение ко всем внешним периферийным устройствам выполняется как к файлам устройств. Каждый тип устройств имеет свой собственный драйвер и специфическую архитектуру, но обращение к каждому устройству выпол- няется одинаковыми методами. Мы увидим, как использовать различные способы доступа к устройствам и определим, какие способы наиболее эф- фективны. Рис. 7-1 Модель среды системы UNIX -------------------------------------------------------------- +-----------+ | | +------------+ | Магнитная | | Принтер | | лента | +------------+ +-----------+ /dev/lp0 lpn /dev/rmt0 .../dev/rmtn \ | | / \ | | / \ | | / \ \ | | | | / / +-------------------+------- /dev/fd0 fd:1,2 | +-------------+ |--- +-----------+ /dev/tty00----| | ПРОЦЕССОР | | |Гибкий диск| +--------+ -- | | ЯДРО | |--- +-----------+ | | / | +-------------+ |-------- fdn | Экран | +-------------------+ +--------+ / / | | | \ \ +--------+ / | \ |Клавиат.| ttynn /dev/hd01 hdnn +--------+ +----------+ fd:0 | | | Жесткий | | диск | | | +----------+ -------------------------------------------------------------- UNIX обращается к периферийным устройствам через "специальные файлы". Имеется два типа специальных файлов: блочные и символьные. Оба типа имеют свое предназначение и особенности. Блочный (например, /dev/hd0) использует буферизацию и позволяет получить доступ к большим объемам данных на жестком диске. Символьный (например, /dev/tty00 или /dev/rfd0) не использует значительную буферизацию, а выполняет обмен с устройством по одному символу за обращение. Даже несмотря на особые свойства таких файлов, для них поддерживается все тот же механизм за- щиты, что и для всех других файлов в системе. Первая область, которую мы рассмотрим - терминальные устройства и работа с ними. Представленные программы включают в себя средство под названием 'c' для быстрой очистки экрана, а также пример программы, которая считывает значения нажатых клавиш и выполняет опрос нажатия одной клавиши. Мы также рассмотрим пример файла описания терминала (termcap), который обеспечивает доступные определения характеристик терминала. Затем мы рассмотрим дисковые устройства - жесткие и гибкие диски. Мы увидим, что имеются различные способы просмотра разделов диска с использованием файлов устройств. В дополнение к работе с устройствами мы рассмотрим файловые системы на жестком диске. Всем нам известно, что система UNIX сущест- венно ориентирована на диски, поэтому чем больше мы знаем о файловых системах, тем лучше для нас. Для более полного понимания разделов диска и файловых систем мы представим три программных средства. Средство lrgf проверяет граничные значения параметров файловой системы путем создания файла наибольшего возможного размера в вашей системе. Средство mntf обеспечивает удобный способ монтирования и размонтирова- ния гибких дисков. Наконец, средство mntlook выполняет поиск немонти- рованных файловых систем, которые представляют собой потенциальную опасность. ТЕРМИНАЛЬНЫЕ УСТРОЙСТВА Драйверы терминальных устройств являются одними из самых сложных драйверов устройств. Причина этого заключается в том, что существует множество уровней программного обеспечения, которые поддерживают ха- рактеристики интерактивных терминалов. При работе терминала по после- довательной линии связи необходима мощная поддержка для того, чтобы облегчить его использование. Различные установки, которые может иметь терминал, программируются командами stty(1) и ioctl(2). Команда termio(7) также описывает различные аспекты протокола работы термина- ла. ПРОТОКОЛ ОПЕРАЦИЙ ВВОДА/ВЫВОДА ТЕРМИНАЛА Протокол работы терминала представляет собой согласованный набор сигналов, позволяющих системе правильно интерпретировать вводимые с клавиатуры строки. Протокол необходим по четырем причинам. Первой яв- ляется поддержка входной обработки специальных символов, таких как символы удаления и прекращения работы программы. Во-вторых, нам необ- ходимо поддерживать обработку выводимой информации, например, вставку символов задержки или изменение последовательности возврат каретки/пе- ревод строки. Третьей причиной является поддержка режимов необработан- ного и "канонического" ввода. Эти два режима позволяют пользова- тельским программам получать данные или по одному символу, или по од- ной строке. Последняя причина введения терминального протокола - жела- ние сделать так, чтобы пользователь мог сам изменять параметры конфи- гурации терминала. Содержимое терминальной подсистемы показано на рис. 7-2. Рисунок разбит на три части: слева - область пользователя, посредине - область ядра и справа - область устройства. Этот рисунок показывает, как пере- даются данные между терминалами и программами пользователя. Рис. 7-2. Управление протоколом терминала ------------------------------------------------------------------- Область Область ядра Область пользователя устройства Процесс +-------+ : : |Текст | : +--------+ +--------+ +-------+ : dzrint() |-------| : ttread() |канонич.| canon()|необраб.| ttin()|приемн.| : +-----+ |Данные | : /---|очередь |<-------|очередь |<------|буфер |<--|dbuf | | | : / +--------+ +--------+ / +-------+ : | | | +----+| : / структура структура / структура : +-----+ | |ubuf||<---/ clist clist / ccblock : | +----+| : / : | +----+| : +--------- / ttxput() : | |ubuf||---- | : | +----+| : \ +-------+ <--+ +--------+ : +-----+ |-------| : \ |выходн.| |буфер | : |dbuf | |Стек | : \--->|очередь|------->|передачи|------------------->| | | | : +-------+ ttout()+--------+ dzxint() : +-----+ +-------+ : ttwrite() структура структура : clist ccblock /|\ | ------+ Граница драйвера устройства ------------------------------------------------------------------- Когда какой-либо процесс читает символы с устройства, данные на- чинают двигаться от буфера драйвера устройства, который называется dbuf. Из этого буфера данные попадают в приемный буфер, управляемый ядром. Приемный буфер читается подпрограммой ядра с именем ttin(), и данные помещаются в структуру clist, называемую необработанной оче- редью. (Слово "необработанная" означает, что над символами пока что не производилось никакой обработки.) В то же время символы также помеща- ются в выходную очередь, что позволяет системе выполнять эхо-отображе- ние символов по мере их ввода. После этого подпрограмма canon() превращает необработанную оче- редь в каноническую. ("Каноническая" означает применение правил, кото- рые в данный момент установлены системой для выполнения специальных функций над строкой текста, например для обработки символа удаления.) Такая обработка позволяет преобразовать данные перед тем, как их полу- чит процесс пользователя. Последней подпрограммой является ttread(), которая читает символы из канонического буфера в буфер пользова- тельского процесса в области данных процесса. Когда символы записываются из процесса пользователя на терминал, они проделывают почти такой же маршрут в обратном направлении: от об- ласти процесса к области драйвера устройства. Основное отличие в подп- рограмме записи заключается в том, что эти данные проходят на один бу- фер меньше. От процесса пользователя символы передаются в выходную очередь ядра подпрограммой ttwrite(), а затем в буфер передачи с по- мощью подпрограммы ttout(). Из буфера передачи они пересылаются не- посредственно в приемный буфер драйвера устройства с помощью подпрог- раммы dzxint(). ОПРЕДЕЛЕНИЕ ВВОДИМЫХ СИМВОЛОВ Бывает так, что мы хотим увидеть, какие символы вводятся с клави- атуры. Мы могли бы для этого написать программу, но UNIX предоставляет встроенную возможность для этой цели. Это команда od - восьмеричный дамп (octal dump). Такое название осталось еще с тех давних времен, когда восьмеричное исчисление широко применялось при отладке. К счастью, результат работы команды od можно получить в символьном, шестнадцатиричном или десятичном виде. Фокус использования команды od для проверки входных и выходных значений зак- лючается в том, что od читает стандартное устройство ввода по умолча- нию, если не указан файл. Например, вызов $ od -cx test string ^d ^d даст такой результат: ------------------------------- | | 0000000 6574 7473 7320 7274 6e69 0a67 | t e s t s t r i n g \n | 0000014 | $ | Здесь вызов команды od делается без указания имени файла в ко- мандной строке и с применением стандартного вывода в качестве выводно- го устройства. Мы используем опцию -cx для того, чтобы байты интерпре- тировались как символы ASCII, а соответствующие 16-битовые слова отоб- ражались в шестнадцатиричном виде. По мере того, как вы набираете сим- волы, они отображаются на экране, а команда od сохраняет их в своем буфере. В конце строки нажмите возврат каретки, затем CTRL-D. Ввод CTRL-D завершает чтение символов командой od и выдает распечатку, в которой сверху будут шестнадцатиричные значения, а снизу символы в ко- де ASCII. Обратите внимание, что два символа, выводимые для каждого шест- надцатиричного слова, располагаются в обратном порядке по сравнению с двумя байтами, образующими это слово. Например, слово 6574 интерпрети- руется как два символа, t и e, где 65 - код ASCII для символа e, а 74 - ASCII-код для символа t. Для того чтобы выйти из команды od, введите еще один CTRL-D для прекращения блочного чтения. Если вы хотите еще проверять символы, продолжайте их вводить. Команда od работает несколько загадочно. Если вы введете достаточное количество символов, она выдаст на экран информацию по нажатию только лишь возврата карет- ки. Но если вы ввели всего несколько символов, требуется нажатие как возврата каретки, ТАК И CTRL-D для получения результата на экране. Теперь мы можем сделать один фокус - изменить канонический способ обработки при чтении символов командой od. Это позволит нам увидеть эффект от различных установок протокола работы. Для этого проверьте текущие установки вашего терминала. В версии System V используйте ко- манду "stty -a", а в версии Berkeley вам нужно применить команду "stty everything". System V выдает гораздо больше параметров, чем Berkeley. (Наиболее популярные версии UNIX'а разработаны и поддерживаются следу- ющими фирмами: System V - фирмой AT&T Bell Laboratories, которая в настоящее время называется Unix System Laboratories; BSD (Berkeley Software Distribution) - Калифорнийским университетом в Беркли; XENIX - фирмой Microsoft.- Прим. перев.) Ниже приводится пример из XENIX: -------------------------- | | speed 9600 baud; line = 0; | intr = DEL; quit = ^|; erase = ^h; | kill = ^u; eof = ^d; eol = ^` | parenb -parodd cs7 -cstobp hupcl cread -clocal | -ignbrk brkint ignpar -parmrk -inpck istrip -inlcr -igncr icrnl -iuclc | ixon ixany -ixoff | isig icanon -xcase echo echoe echok -echonl -noflsh | opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel ff1 | После того, как мы почешем голову, мы увидим, что текущий флаг для канонической обработки установлен в состояние "icanon". То есть, мы можем видеть, что он включен, поскольку нет префикса в виде знака минус (хотя это не является строгим правилом). Что происходит при канонической обработке? Символ возврата на шаг назад (backspace) является одним из важных вопросов. Когда вы вводите символ CTRL-H, он поступает в необработанную очередь как литеральный символ CTRL-H. Когда программа canon() читает CTRL-H, она понимает это так: "Изменить CTRL-H на символ возврата на шаг назад, записать пробел на место символа, затем сделать еще шаг назад." При эхо -отображении вы получаете удаление символа на экране. Когда каноническая обработка отключена, вы посылаете CTRL-H как обычные символы. Вот пример того, как это выглядит: ---------------------------- | | $ stty -icanon Отключение канонической обработки | $ od -cx | test string^h^h^h^h^h^hcase | ^d... | | 0000000 6574 7473 7320 7274 6e69 0867 0808 0808 | t e s t s t r i n g \b \b \b \b \b | 0000020 6308 7361 0a65 0a04 0a0a 0a0a 0a0a 0a0a | \b c a s e \n 004 \n \n \n \n \n \n \n \n \n | После слова "string" вы видите группу чисел 08, которые в ASCII-коде обозначают CTRL-H. Эти числа 08 показывают вам, что лите- ральные символы CTRL-H действительно присутствуют в их "необработан- ной" форме. Поскольку CTRL-H не является больше специальным символом, команда od рассматривает его подобно любому другому символу. Здесь возникает новая проблема: поскольку специальные символы не распозна- ются, мы потеряли возможность завершить блочное чтение вводом символа конца файла (EOF). Когда вводится CTRL-D, он понимается просто как еще один символ. Поэтому мы должны заполнить буфер команды od, чтобы заставить ее выполнить дамп. В нашем примере CTRL-D - это символ 004 после символов case \n. Кстати, в системе Berkeley используются установки "обработанная" ("cooked") и "необработанная" ("raw") для stty, которые по существу служат для тех же целей, что "canon" и "-canon" в System V. ДИНАМИЧЕСКОЕ ПЕРЕОПРЕДЕЛЕНИЕ СИМВОЛОВ ПРЕРЫВАНИЯ Обратите внимание, что в предыдущей распечатке команды stty -a присутствуют определения символов "intr" и "quit". Это две функции, которые прерывают работу ваших работающих процессов. Строки intr и quit представляют собой особые функции, а не просто нажатие клавиши. Эти функции можно назначить на любую клавишу клавиатуры при помощи ко- манды stty. Если мы изменили значение "intr" на другой символ, то этот новый символ прервет ваши процессы. Вы даже можете установить в качестве клавиши прерывания обычный символ. Вот как это можно сделать: $ stty intr x $ this is a junk stringx $ Когда вы вводите символ x в конце строки, то вся введенная строка обрывается и вы получаете новый символ приглашения. Для того чтобы вернуться к обычному режиму работы, введите в качестве символа преры- вания старый символ. Если старым символом был "delete", вы даете такую команду: $ stty intr DEL Что же в этом хорошего? Это показывает, насколько гибко работает команда stty с терминалом, и может быть использовано в качестве личной меры безопасности, чтобы сбить с толку человека, который захотел бы произвести какой-либо беспорядок с вашего терминала. Когда вам нужно на минуту отойти от терминала, измените ключ прерывания на какой-либо другой и запустите командный файл вроде такого: while : do : done Этот бесконечный цикл будет выполняться постоянно. Если кто-ни- будь попытается прервать этот процесс, используя клавишу DEL, ничего не произойдет. Когда вы вернетесь, нажмите новую клавишу прерывания. Она прервет цикл, и вы получите символ приглашения. Очень важным является также символ "eof". Он соответствует концу файла (end of file), и обычно им является CTRL-D. Это объясняет, поче- му вы выходите из системы UNIX с помощью CTRL-D. Когда вы посылаете CTRL-D вашему регистрационному shell, вы тем самым говорите ему: "Shell, это метка конца файла для данного сеанса работы". Поскольку терминал рассматривается как файл, CTRL-D закрывает файл, а shell, ко- торый работает как цикл вида "читать команды с клавиатуры, пока не встретится EOF", завершается, тем самым посылая сигнал программе init. Программа init порождает команду getty по данной терминальной линии, которая выводит на экран приглашение "login:" для ожидания команды от следующего пользователя. Если вы измените символ конца файла, вы боль- ше не сможете выйти из shell по CTRL-D. Он будет выводиться на экран точно так же, как любой другой символ. Вот пример таких действий: $ stty eof x $ x login: Теперь признаком конца файла является обычный символ x. Когда вы вводите символ x, это равносильно вводу CTRL-D, и вы выходите из системы. Очевидно, такие манипуляции нежелательны, однако это позволя- ет показать ту большую степень гибкости, которую использует UNIX при присвоении различных функций разным символам. ФАЙЛЫ ТЕРМИНАЛЬНЫХ УСТРОЙСТВ Физически адресация терминалов производится посредством файлов устройств в каталоге /dev. Когда вы регистрируетесь в системе, вам присваивается определенный номер терминала, например tty01. Этот номер терминала в действительности является файлом /dev/tty01. Если вы вызо- вете команду tty UNIX, она выведет полное маршрутное имя файла того терминального устройства, за которым вы работаете. Файлы терминальных устройств выглядят так же, как обычные файлы, за исключением того, что команда "ls -l" показывает, как называются старший и младший номера устройства, которые не являются частью обыч- ных файлов. Старший номер является индексом в таблице cdevsw[], кото- рая содержит адрес драйвера устройства, используемого ядром для данно- го типа устройства. Младший номер идентифицирует конкретное физическое устройство. Эти номера появляются в последовательном порядке для всех устройств, использующих один и тот же драйвер. Так выглядит типичный список файлов устройств в системе XENIX: ---------------------------- | | crw--w--w- 1 russ tricks 0, 0 Jun 22 02:34 /dev/console | crw--w--w- 1 russ tricks 0, 1 Jun 22 00:41 /dev/tty02 | crw--w--w- 1 root tricks 0, 2 Jun 21 17:56 /dev/tty03 | crw--w--w- 1 root tricks 0, 3 Jun 21 05:47 /dev/tty04 | crw-rw-rw- 1 root root 0, 4 Feb 18 17:09 /dev/tty05 | crw-rw-rw- 1 root root 0, 5 Feb 18 17:09 /dev/tty06 | crw-rw-rw- 2 root root 5, 0 Jun 21 20:23 /dev/tty11 | crw--w--w- 2 root tricks 5, 8 Jun 22 02:20 /dev/tty12 | crw-rw-rw- 2 root root 5,128 Feb 18 17:09 /dev/tty13 | crw-rw-rw- 2 root root 5,136 Feb 18 17:09 /dev/tty14 | По символу 'c' в первом столбце мы видим, что это символьные уст- ройства, а биты прав доступа показывают, кто имеет доступ к этим фай- лам. Первый столбец чисел (0 или 5) является старшим номером. Младшие номера в следующем столбце обычно следуют в последовательном порядке, но не всегда (как видно в данном примере). В дополнение к использованию абсолютного номера вашего терминала, одно из устройств используется в качестве "логического", или "родово- го" адреса вашего терминала. Оно использует другой драйвер устройства, называется /dev/tty и применяется в случаях, когда стандартный ввод и стандартный вывод переадресовываются в другие файлы, а прикладной программе необходимо читать с клавиатуры или писать на экран. При по- мощи доступа к файлу /dev/tty образуется связь с самим терминалом. Вы- бор использования устройства с именем tty вместо устройства tty01 главным образом зависит от того, какая стоит задача. Если вам необхо- димо иметь независимую от типа терминала программу, используйте /dev/tty. ПРАВА ДОСТУПА К ТЕРМИНАЛУ Поскольку терминальное устройство является файлом, оно имеет ре- жимы прав доступа точно так же, как и все другие файлы. Эти режимы представляют собой защиту доступа к вашему терминалу. Если все пользо- ватели имеют право записи на ваш терминал (это обозначается как rw--w--w-), то они могут записать на ваш экран все, что угодно, и вы никогда не узнаете, кто это сделал. Если вы хотите предупредить это, то выполните команду "chmod 600 `tty`", где символы ударения и обозна- чение tty соответствуют маршрутному имени вашего терминального файла. Более простой в использовании является команда UNIX'а mesg. Команда "mesg n" запрещает запись извне на ваш терминал. Ваши собственные про- цессы по-прежнему имеют доступ на запись. Права доступа к терминалу связаны также с проблемой безопасности, которую мы рассмотрим в главе 9. Пока что отметим, что всякий раз, когда вы открываете файл (то ли для чтения, то ли для записи), вам возвращается дескриптор файла. Затем вы можете использовать этот деск- риптор файла в системном вызове ioctl. Получение этого дескриптора файла подобно получению ключа к терминальному интерфейсу определенного пользователя. Любые изменения, производимые с помощью ioctl с данным дескриптором файла, вызывают немедленный эффект, и нарушитель защиты может читать все, что записывается или считывается с вашего терминала или даже заставить ваш терминал выполнять команды, которые присваивают себе неразрешенные права! Пользователь, который работает с этим терми- налом, может никогда не узнать, что же произошло или кто это сделал. Другим примером подобного рода является команда write(1). Она используется для установки связи по линии в реальном режиме времени, или "болтовни". Она общается с терминалом путем выполнения записи в файл устройства. Измените биты прав доступа, выключив их командой "mesg n", и никто не сможет выполнить команду write с вашим термина- лом. Таким способом вы можете "снять трубку телефона", когда вы хоти- те, чтобы вам не мешали. Вместе с тем кто-нибудь мог бы сделать такое: $ while : > do > clear > /dev/tty00 > done & При этом создается фоновый бесконечный процесс (пока он не будет прек- ращен командой kill или выходом из системы), который посылает пользо- вателю терминала tty00 символы очистки экрана. Как только этот пользо- ватель что-нибудь набирает на экране, он тут же очищается. Большинство пользователей не могут даже понять, что происходит. Если это случится с вами, попытайтесь отключить права доступа к вашему терминалу. Если же тот, кто это делает, является суперпользователем (root), то никакие права доступа не смогут остановить его, поскольку суперпользователь не имеет ограничений по правам доступа к файлам. В этом случае возникает проблема для системного администратора! Еще одна странная вещь наблюдается в случае, когда кто-либо посы- лает что-то на ваш терминал командой write, а вы отключаете права доступа. Команда write по-прежнему имеет доступ к вашему терминалу, пока она не закроет устройство. После закрытия она больше не имеет прав для открытия устройства. Представляется странным факт, что после получения доступа к терминалу последующее запрещение права доступа не оказывает никакого воздействия до тех пор, пока вы не прекратите рабо- ту с терминалом. ОТМЕТКИ О ВРЕМЕНИ ДОСТУПА К ТЕРМИНАЛУ Еще одним атрибутом терминалов, который вызван тем, что терминалы - это файлы, являются даты модификации, доступа и обновления. Каждый файл в системе имеет эти три даты, которые содержатся в его индексном дескрипторе (в секундах) в виде ДЛИННЫХ (long) чисел. Эти даты могут содержать некоторую интересную информацию. Как бы- ло отмечено при описании командного файла activ в предыдущей главе, дата модификации представляет собой последний момент времени, когда пользователь что-то набирал на своей клавиатуре. Другие даты тоже кое-что означают, но они не так часто используются. ОБРАБОТКА ТЕРМИНАЛОМ ВВОДИМОЙ ИНФОРМАЦИИ Как уже обсуждалось ранее, по умолчанию драйвер терминала работа- ет в каноническом режиме, т.е. в режиме построчной обработки. Когда вы вводите символы, драйвер ожидает, пока вы нажмете возврат каретки, после чего передает для обработки всю строку. Если вы работаете не в каноническом режиме, то каждый символ передается для обработки не- посредственно после ввода. Наглядным примером такого режима работы яв- ляется редактор vi. Вы нажимаете по одной клавише для движения курсо- ра, удаления символов и т.д., поэтому редактор vi, очевидно, должен получать каждый символ сразу же, как только нажата клавиша. Каким образом это делается в программе? Прием старый и часто используется в UNIX, хотя и не очень хорошо описан в документации. Та- кого рода информацию можно добыть путем просмотра большого количества текстов программ. Необходимо отметить, что этот прием лучше всего ра- ботает в программах на языке Си. Командные файлы, написанные на языке shell, могут использовать для этой цели команду stty, но результат бу- дет не один и тот же. Следующий фрагмент программы на языке Си отклю- чает каноническую обработку, затем читает символы и выводит их на эк- ран. 1 #include 2 3 struct termio tsav, tchg; 4 5 main (argc, argv) 6 { 7 int c; 8 9 if (ioctl (0, TCGETA, &tsav) == -1) { 10 perror("can't get original settings"); невозможно получить исходные установки 11 exit(1); 12 } 13 14 tchg = tsav; 15 16 tchg.c_lflag &= ~(ICANON | ECHO); 17 tchg.c_cc[VMIN] = 1; 18 tchg.c_cc[VTIME] = 0; 19 20 if (ioctl (0, TCSETA, &tchg) == -1) { 21 perror("can't initiate new settings"); невозможно задать новые установки 22 } 23 24 while (1) 25 { 26 c = getchar(); 27 28 if (c == 'x') 29 break; 30 31 putchar(c); 32 } 33 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 3 4 struct termio tsav, tchg; 5 6 main (argc, argv) 7 { 8 int c; 9 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 } 16 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 } 22 23 tchg = tsav; 24 25 tchg.c_lflag &= ~(ICANON | ECHO); 26 tchg.c_cc[VMIN] = 1; 27 tchg.c_cc[VTIME] = 0; 28 29 if (ioctl (0, TCSETA, &tchg) == -1) { 30 perror(can't initiate new settings"); невозможно задать новые установки 31 } 32 33 while (1) 34 { 35 putchar('.'); 36 c = getchar(); 37 38 if (c == 'x') 39 break; 40 41 putchar(c); 42 } 43 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"; Быстрая очистка экрана 2 3 #define FF "\014" 4 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 Kб кода всего лишь для одного оператора 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 Kб (5 Mб) на жестком диске: # mkfs /dev/hd01 5000 Внутри раздела (размером не менее 5000 Kб) размещается файловая система. Файловая система содержит суперблок, списки свободных блоков и т.п., то есть все, что необходимо для хранения файлов, которые раз- мещаются здесь. Однако, создание файловой системы совсем НЕ означает, что вы сразу же можете получить к ней доступ. Сначала вам необходимо смонтировать файловую систему. Команда для выполнения этой операции может иметь такой вид: # mount /dev/hd01 /mount_pt Файлы могут быть помещены в дисковый раздел командами mv или cp, путем переадресации вывода в каталог с этим именем, например, >/mount_pt/file. Для использования раздела диска в качестве области неструктуриро- ванных данных, а не блочного устройства, применяйте файл с именем сим- вольного устройства, которое начинается с буквы r. Например, для использования того же устройства, что и в предыдущем примере, в ка- честве неструктурированного устройства, укажите имя /dev/rhd01. (Из списка устройств вы видите, что это символьное устройство, так как права доступа в первой колонке начинаются с символов crw, а не brw). Это устройство (и соответствующий раздел) в данный момент не имеет файловой системы и является просто набором байтов. Единственным огра- ничением является то, что вы можете записать в этот раздел не более 5 Mб данных. Вот пример команды, использующей неструктурированное устройство: $ find . -print | cpio -ocBv > /dev/rhd01 ИЗУЧЕНИЕ ДАННЫХ Когда данные находятся на диске, их можно изучить более тщатель- но, чем с помощью команд cat, more и других. Делается это командой od(1), которая выдает дамп файла устройства, как показано в следующем примере: $ od -c /dev/hd01 Если бы вы получали дамп файла НЕСТРУКТУРИРОВАННОГО устройства (/dev/rhd01), то это выглядело бы точно так же. Единственное отличие заключается в том, как драйвер осуществляет доступ к данным. Формат, в котором будут выводиться данные, зависит от того, какой командой про- изводилось копирование: cpio, tar, mkfs или какой-то иной. Некоторые другие способы получения данных с устройства: $ cat /dev/hd01 $ cat < /dev/hd01 $ tail /dev/fd0 Если вы дампируете файл устройства, содержащего файловую систему, то данные будут представлять собой неупорядоченные блоки по 512 байт. В одном месте вы можете увидеть списки каталогов. Другими словами, од- но и то же устройство может рассматриваться двумя совершенно разными способами: как файловая система и как набор неструктурированных битов. Хотя выполнение чтения двумя этими способами может быть поучительным, в большинстве случаев у вас не возникнет желания выполнить ЗАПИСЬ ин- формации на одно и то же устройство двумя способами, поскольку, напри- мер, неструктурированное устройство не будет ничего знать о файловой системе в данном разделе и может затереть данные, относящиеся к файло- вой системе. Теперь, когда вы знаете, как осуществить доступ к диску, мысленно вернемся к главе 2 и программам копирования. Командный файл cpiobr использует для копирования файлов неструктурированное дисковое уст- ройство /dev/rfd0, в то время как autobkp использует файловую систему. Большинство из этих способов работы с устройствами могут пока- заться несколько экзотичными и предназначенными в основном для шутки и обучения. Однако часто шутка помогает продуктивно работать. Ведь пыта- ясь заставить систему сделать то или иное, вы можете открыть для себя новые возможности системы. Ситуация с аппаратурой очень похожа. Появ- ляются новые устройства, и требуются годы для разработчиков программ- ного обеспечения, чтобы обнаружить все возможности машины. Система UNIX существует в том или ином виде уже более десяти лет, но пользова- тели до сих пор открывают ее новые и удивительные способности. Итак, поскольку вы обычно должны выбрать тот или иной метод использования раздела диска, то ничто не мешает вам завести на уст- ройстве все разделы одинакового типа. Обычным подходом является созда- ние файловых систем во всех возможных разделах, чтобы они могли содер- жать файлы. Тем не менее, вы можете сочетать файловую систему с "нест- руктурированными" разделами любым способом, который вам нравится. Од- ной из возможных схем является использование одного раздела (fd01) в качестве неструктурированного устройства для копирования файлов коман- дой "cpio -o". Этот раздел занимает почти весь диск, но какая-то часть отводится для размещения второго раздела с файловой системой (fd02). Распределенное пространство содержит некоторые справочные (help) файлы и текстовый файл с именами файлов, находящихся в неструктурированном разделе. Такое разбиение на разделы использует преимущества обоих способов. Для того чтобы получить данные, скопированные командой cpio, вы вводите команду "cpio -i < /dev/rfd01". Для получения данных из второго раздела, вы вводите команду "mount /dev/fd02 /mnt", а затем используете команды ls, file, grep и другие, которые относятся к фай- ловой системе. В этом случае раздел с файловой системой служит для до- кументирования неструктурированного раздела. ЗАГРУЖАЕМЫЙ ДИСК И АВТОНОМНЫЙ shell (SASH) Инсталляция системы UNIX на жесткий диск обычно выполняется с по- мощью автономного shell (SASH, standalone shell). Иногда эта операция выполняется с магнитной ленты, но легче всего использовать гибкий диск. Возникает вопрос: "Как загрузить UNIX с гибкого диска?" Картина следующая: гибкий диск имеет один раздел или даже может быть разделен на корневой раздел и раздел пользователей. В любом слу- чае гибкий диск имеет файловую систему, созданную другой системой и помещенную на диск. Первый блок файловой системы является загружаемой записью, которая размещается на носителе с помощью команды dd. Команда dd копирует байты, начиная с самого начала устройства. Загрузочная за- пись содержит код, необходимый для запуска системы UNIX с диска. Второй блок - это суперблок, своего рода главный каталог файловой системы. В нем находятcя индексные дескриптооы файлов, содержащие ин- формацию о каждом файле, а также список доступных свободных блоков. Корневая файловая система имеет также вариант ядра для гибкого диска, который загружается и запускает shell точно так же, как это делает его старший брат (ядро системы для жесткого диска) для всей системы в це- лом. Вы даже можете смонтировать инсталляционный диск на жесткий диск с другой системой и выполнять команды копирования. Ограничивающим фак- тором является размер одного инсталляционного диска. Самый большой объем гибкого диска на машинах PC - 1.2 Mб (используется на PC AT), что вполне достаточно. Можно уместить почти всю программу загрузки, которая необходима для запуска многопользовательской системы с гибкого диска. Как только ядро системы с гибкого диска загружено, имеется полная файловая система со всеми файлами устройств. Ядро монтирует раздел жесткого диска (предполагается, что жесткий диск был разбит на разде- лы) и копирует на него файлы в формате файловой системы. Вот как это выглядит: # mount /dev/hd01 /mnt <-вызов с гибкого диска для монтирования первого раздела жесткого диска # copy /unix /mnt <-копирование ядра жесткого диска в раздел жесткого диска ОБНОВЛЕНИЕ ФАЙЛОВОЙ СИСТЕМЫ Мы описали суперблок как запись с ключевой информацией о размере и содержимом файловой системы. Причиной разрушения файловой системы обычно являются проблемы, возникающие в суперблоке. Команда sync(1) выполняет запись образа суперблока на диск, тем самым обновляя его. Иногда эта операция должна выполняться автоматически и постоянно для того, чтобы образы суперблока на диске и в памяти были одинаковы. В настоящее время в System V включена программа update, которая запуска- ется из загрузочного файла /etc/rc. Она живет в системе и исполняет команды sync и sleep. В результате информация о состоянии файловой системы на диске хранится со всеми текущими изменениями, произведенны- ми с самой файловой системой. Если у вас нет такой программы, вы може- те написать командный файл на языке shell, которая работает в цикле, вызывая команду sync через соответствующие интервалы команды sleep. Запустите этот командный файл в фоновом режиме, чтобы поддерживать це- лостность файловой системы. МОНТИРОВАНИЕ ФАЙЛОВЫХ СИСТЕМ Давайте рассмотрим, что происходит, когда файловая система монти- руется в древовидной структуре системы. На рис. 7 -3 показано, как взаимодействуют между собой индексные дескрипторы (inodes) двух файло- вых систем. Рис. 7-3 Монтирование одной файловой системы в другую ------------------------------------------------------------- +------------------------------+ |Раздел| Физическое| Логическое| |диска | имя | имя | |------------------------------| | 1 | /dev/hd01 |/dev/root | | 2 | /dev/hd02 |/dev/usr | +------------------------------+ +----+ | | Раздел 1 | / |inode 2 (ls -lia /) / / | | \ \ \ / / +----+ \ \ \ / / / | \ \ \ \ / / / | \ \ \ \ / / / | \ \ \ \ +---+ +---+ +---+ +---+ +-----+ +---+ +---+ +---+ |bin| |dev| |etc| |lib| |lost+| |mnt| |tmp| |usr| inode 245 | | | | | | | | |found| | | | | | +---+ +---+ +---+ +---+ +---+ +-----+ +---+ +---+ +-| | / | \ / | \ / |\ / | \ / | | \ | | / | \ +---+ /|\ /|\ /|\ /|\ | /----------------+ / КОМАНДА / # /etc/mount /dev/hd02 /usr / +-----+ | | Раздел 2 | / | inode 2 (ls -lia /usr) | | +-----+ \ \ \ / / / | \ \ \ \ \ / / / | \ \ \ \ \ / / / | \ \ \ \ \ / / / | \ \ \ \ \ / / / | \ \ \ \ \ +---+ +---+ +-------+ +---+ +-----+ +-----+ +-----+ +---+ +---+ |adm| |bin| |include| |lib| |lost+| |pre- | |spool| |sys| |tmp| | | | | | | | | |found| |serve| | | | | | | +---+ +---+ +-------+ +---+ +-----+ +-----+ +-----+ +---+ +---+ / | \ / | \ / | | \ / | \ /|\ / || \ / | \ / / | \ / | \ \ / / | \ / | \ \ /|\ /|\ /|\ /|\ / | \ /|\ /|\ /|\ -------------------------------------------------------------------- В примере, показанном на рис. 7-3, файловая система из раздела 2 монтируется в корневой файловой системе (раздел 1) в каталог /usr. Од- нако мы помним, что каждая файловая система имеет свой собственный корневой каталог. В каждой файловой системе нумерация индексных деск- рипторов файла начинается с числа 2, поэтому номера индексных дескрип- торов дублируются в двух файловых системах. Это и является причиной, по которой не могут быть образованы связи между файлами, находящимися в разных файловых си бразованы связи между файлами, находящимися Одним из атрибутов корневого каталога является то, что номер его индексного дескриптора равен 2. Это значение может быть проверено в корневом каталоге командой "ls -lid /". Каталог /usr - это просто еще один файл (а именно каталог) в корневой файловой системе. Этот каталог может содержать файлы и подчиненные каталоги, которые хранятся в раз- деле 1. После выполнения команды "mount /dev/hd02 /usr" корневой ката- лог раздела 2 (индексный дескриптор 2) помещается в каталог /usr (ин- дексный дескриптор 245). Если какие-либо файлы существуют в каталоге /usr в разделе 1, они остаются там, но получить доступ к ним вы не мо- жете. Единственным способом увидеть их является размонтирование файло- вой системы, которая была смонтирована на их место. Хитрость команды mount заключается в том, что она представляет новый раздел как бы при- надлежащим реальному корневому разделу. В сущности, это позволяет иметь безграничную файловую систему. Механизмом, который позволяет производить это, является таблица смонтированных устройств, находящаяся внутри ядра системы. Когда вы- полняется обращение к файлу, его индексный дескриптор определяет марш- рут, по которому находится данный файл. Если в таблице смонтированных устройств имеется запись, то этот маршрут ведет на другой раздел диска или в другую файловую систему. Для того чтобы убедиться, что вновь смонтированная файловая система уникальна, посмотрите индексный деск- риптор каталога /usr сначала из корневого каталога (командой "ls -li /", индексный дескриптор 245), а затем из другой файловой системы (ls -ldi /usr, индексный дескриптор 2). КАК ПОЛУЧИТЬ ПОБОЛЬШЕ ИНФОРМАЦИИ О ФАЙЛОВОЙ СИСТЕМЕ? Как указывалось ранее, файловая система размещается внутри разде- ла на диске. Файловые системы создаются командой mkfs(1), поддержива- ются командой fsck(1), отлаживаются командой fsdb(1), а первый доступ к ним осуществляется командой mount(1). Каталог /usr/include содержит все включаемые файлы для использования в программах на языке Си, реа- лизующих эти команды. Таким образом, этот каталог представляет собой прекрасную возможность для поиска информации о файловой системе, поскольку включаемые файлы содержат глобальные определения, используе- мые подпрограммами файловой системы. В документации Bell Labs (в руко- водстве программиста) также описаны некоторые внутренние таблицы, используемые файловой системой. Теперь мы готовы рассмотреть программные средства для автоматиза- ции рутинной работы с файловой системой. ---------------------------------------------------- ИМЯ: mntf ---------------------------------------------------- mntf Монтирование и размонтирование гибкого диска НАЗНАЧЕНИЕ Монтирует и размонтирует устройство гибкого диска в каталоге как файловую систему с возможностью записи/чтения или только чтения. ФОРМАТ ВЫЗОВА mntf [-d] [-h] [-l] [-r] [-s] Опции: -d размонтирование гибкого диска из корневой файловой системы -h использование устройства с высокой плотностью записи (а не с низкой) -1 использование устройства 1, а не устройства 0 -r монтирование гибкого устройства как файловой системы с возможностью только чтения -s использование имен устройств, принятых в System V По умолчанию выполняется монтирование гибкого диска 0 в каталог /mnt. ПРИМЕР ВЫЗОВА mntf -d -1 Размонтирование гибкого диска на устройстве 1. ТЕКСТ ПРОГРАММЫ 1 : 2 # @(#) mntf v1.0 Mount floppies Author: Russ Sage Монтирование гибких дисков 3 4 CMD="/etc/mount" 5 DIR="/mnt" 6 DRIVE="0" 7 DENSITY="48ds9" 8 SYSTEM="xenix" 9 10 if [ $# -gt 0 ] 11 then for ARG in $* 12 do 13 case $ARG in 14 -d) CMD="/etc/umount" 15 DIR="";; 16 -h) DENSITY="96ds15";; 17 -1) DRIVE="1" 18 if [ -d /mnt1 ] 19 then DIR="/mnt1" 20 else echo "the directory /mnt1 does not exist" >&2 нет каталога /mnt1 21 echo "using the directory /mnt instead" >&2 используется каталог /mnt 22 fi;; 23 -r) DIR="$DIR -r";; 24 -s) SYSTEM="sysv";; 25 *) echo "mntf: invalid argument $ARG" >&2 26 echo "usage: mntf [-d] [-h] [-1] [-r] [-s]" >&2 27 echo " -d dismount" >&2 28 echo " -h high density" >&2 29 echo " -1 use drive 1" >&2 30 echo " -r read only" >&2 31 echo " -s System V device" >&2 32 echo " default: mount XENIX drive 0 48 tpi to " >&2 33 echo " /mnt as a read/write filesystem" >&2 34 exit 1;; 35 esac 36 done 37 fi 38 39 case $SYSTEM in 40 sysv) $CMD /dev/fp${DRIVE}21 $DIR;; 41 xenix) $CMD /dev/fd${DRIVE}${DENSITY} $DIR;; 42 esac ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ CMD Основная команда, подлежащая выполнению DIR Каталог, в котором производится монтирование устройства DENSITY Плотность записи в виде, указанном в имени устройства DRIVE Номер устройства, начиная с 0 SYSTEM Тип имени устройства, принятый в UNIX'е ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ mntf? В машинах с гибким диском это устройство часто используется в повседневных операциях. Оно применяется в качестве источника при инсталляции системы и как обычный носитель для операций копирования. Гибкие диски можно использовать в системе UNIX двумя способами. Первый является неструктурированной последовательностью байтов, что полезно для копирования магнитных лент и хранения архивов. Второй способ - поблочный, ориентирован на поддержку файловой структуры. Для второго способа существует мощная поддержка на уровне файловой систе- мы, но некоторые функции мы должны реализовать самостоятельно. Для того чтобы использовать гибкий диск как файловую систему в UNIX, вам необходимо подготовить диск и смонтировать его как файловую систему. Когда вы закончите работу, вы должны размонтировать гибкий диск. Это отличается от системы DOS, в которой гибкие диски можно вставлять и вынимать когда угодно, если только в этот момент на них не идет запись. Поскольку использование гибких дисков включает в себя взаимосвя- занные шаги монтирования и размонтирования, то было бы вполне естест- венным применять одну команду с соответствующими опциями для выполне- ния монтирования и размонтирования. Однако UNIX так не делает. Наш ко- мандный файл mntf объединяет эти две функции в одной команде для упро- щения работы с гибким диском. Для того чтобы сделать нашу программу более независимой, мы предусмотрели в ней поддержку устройств системы XENIX наравне с устройствами System V. (Системы Berkeley (BSD) не так часто используют гибкие диски, поэтому мы не пытались иметь с ними де- ло.) ЧТО ДЕЛАЕТ mntf? Эта программа обеспечивает поддержку всех возможностей для монти- рования и размонтирования гибких дисков. Она предоставляет все опции, необходимые команде mount, акцентирует внимание на тех аспектах файло- вой системы, которые относятся к гибким дискам, и уменьшает количество нажатий на клавиши, необходимых для выполнения этой работы. Действие программы по умолчанию заключается в монтировании гибко- го диска низкой плотности записи, находящегося в устройстве 0, в ката- лог /mnt. Имеется много опций, чтобы попросить программу mntf сделать то, что вам нужно. Опция -h поддерживает диск высокой плотности (1.2 Mб). В машинах PC AT первое из устройств гибких дисков имеет 96 доро- жек на дюйм, объем 1.2 мегабайта, но может также читать и писать гиб- кие диски с более низкой плотностью. Второй гибкий диск является уст- ройством низкой плотности с 48 дорожками на дюйм и объемом 360 кило- байт. Опция -1 (цифра один, а не буква l) выполняет монтирование гибко- го диска в устройстве 1, а не 0. Опция -r монтирует файловую систему с возможностью ТОЛЬКО ЧТЕНИЯ. Для РАЗМОНТИРОВАНИЯ диска вместо монтиро- вания используется опция -d. Если применяется опция -s, имя устройства изменяется таким образом, чтобы оно соответствовало системе System V, а не XENIX. Это незначительная проблема, поскольку схемы именования не очень отличаются. Данная программа создана для системы XENIX и обеспе- чивает наилучшие возможности именно в ней. Не все опции совместимы друг с другом, но проверка на совмести- мость не выполняется. Например, команда "mntf -d -r" пытается размон- тировать файловую систему с возможностью только чтения, а команда UNIX unmount, которая выполняет эту операцию, отбрасывает ее, выдавая сооб- щение об ошибке. В целях упрощения мы отказались от проверки соот- ветствия опций, а вместо этого предоставили UNIX'у право выдавать сообщения об ошибках для информирования пользователя о возникших проб- лемах. Если вы хотите, чтобы эту программу мог применять относительно неопытный пользователь, вам нужно вставить в нее выполнение таких про- верок. ПРИМЕРЫ 1. $ mntf -s Монтирование гибкого диска как файловой системы с возможностью записи-чтения и с использованием имен устройств, принятых в System V. 2. $ mntf -h -1 -r Монтирование гибкого диска высокой плотности записи на устройстве 1 как файловой системы с возможностью только чтения и с использованием формата имен устройств, принятого в XENIX. Эта команда должна закон- читься неудачей (устройство 1 имеет низкую плотность). 3. $ mntf -d -h Размонтирование файловой системы на устройстве 0 с высокой плот- ностью записи и с использованием имен устройств, принятых в XENIX. ПОЯСНЕНИЯ Для того чтобы максимально упростить программу, все фактически выполняемые команды помещены в текстовые строки. Это позволяет достичь большей гибкости при написании программы. Результатом анализа команд- ной строки является формирование команды, которая выполняется в конце программы mntf. В строках 4-8 инициализируются установки по умолчанию. Переменная CMD содержит команду UNIX, которая в итоге должна быть выполнена, по умолчанию это команда mount. Переменная DIR указывает каталог, в кото- рый должно быть смонтировано устройство, по умолчанию это каталог /mnt. Переменная DRIVE является номером устройства (по умолчанию 0) и используется для формирования корректного имени устройства. Переменная DENSITY по умолчанию установлена для носителя низкой плотности, т.е. 48 дорожек на дюйм, двусторонняя дискета с 9 секторами на дорожку (48ds9). В строке 10 проверяется, указаны ли в командной строке какие-либо аргументы. Если количество аргументов больше нуля, последовательно проверяется каждый аргумент. Если какой-либо из аргументов соот- ветствует образцам в строках 13-35, то он изменяет содержимое команд- ной строки. Строка 14 управляет опцией -d для размонтирования гибкого диска. Переменная CMD изменяется на umount вместо mount. После этого перемен- ной DIR присваивается нулевое значение, поскольку команде umount тре- буется не каталог, а только имя устройства. Переменная DIR должна быть частью строки с командой для того, чтобы мы могли использовать одну и ту же "заготовленную" командную строку для всех вариантов. В данном случае мы устанавливаем эту переменную в нуль, а shell при синтакси- ческом разборе удаляет ее из командной строки. В строке 16 выполняется изменение плотности записи используемого носителя. Обращение к различным типам носителей выполняется по именам файлов устройств. Каждое имя указывает драйвер устройства, который ра- ботает с соответствующей аппаратурой. Устройство высокой плотности мо- жет работать в режимах как высокой, так и низкой плотности записи. Од- нако если вы укажете имя устройства с высокой плотностью записи, а на самом деле оно имеет низкую плотность, то драйвер работать не будет из-за ошибок чтения. ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ ОБ ИМЕНАХ УСТРОЙСТВ Ниже приводится список, полученный командой ls в каталоге dev для машины XT, в которой нет устройств высокой плотности записи. Этот список позволяет проиллюстрировать, каким образом осуществляется обра- щение к именам устройств: ------------------------------ | | 32 brw-rw-rw- 3 bin bin 2, 4 Jun 25 09:25 /dev/fd0 | 32 brw-rw-rw- 3 bin bin 2, 4 Jun 25 09:25 /dev/fd048 | 126 brw-rw-rw- 1 root root 2, 12 Feb 18 17:09 /dev/fd048ds8 | 32 brw-rw-rw- 3 bin bin 2, 4 Jun 25 09:25 /dev/fd048ds9 | 125 brw-rw-rw- 1 root root 2, 8 Feb 18 17:09 /dev/fd048ss8 | 127 brw-rw-rw- 1 root root 2, 0 Feb 18 17:09 /dev/fd048ss9 | 131 brw-rw-rw- 3 root root 2, 5 Feb 18 17:09 /dev/fd1 | 131 brw-rw-rw- 3 root root 2, 5 Feb 18 17:09 /dev/fd148 | 129 brw-rw-rw- 1 root root 2, 13 Feb 18 17:09 /dev/fd148ds8 | 131 brw-rw-rw- 3 root root 2, 5 Feb 18 17:09 /dev/fd148ds9 | 128 brw-rw-rw- 1 root root 2, 9 Feb 18 17:09 /dev/fd148ss8 | 130 brw-rw-rw- 1 root root 2, 1 Feb 18 17:09 /dev/fd148ss9 | Крайнее слева число представляет собой номер индексного дескрип- тора. Мы используем его как ссылку для определения уникального имени файла. Как мы уже отмечали ранее, несколько имен устройств могут от- носиться к одному и тому же файлу, рассматриваемому с различных точек зрения. Например, в данном списке вы видите, что три устройства имеют индексный дескриптор 32. Второе число слева представляет собой коли- чество связей. Когда оно больше единицы, то это также указывает, что несколько устройств являются на самом деле одним файлом, а следова- тельно используют один и тот же индексный дескриптор. Следующие два числа являются старшим и младшим номером. Старший номер относится к драйверу устройства, а младший является уникальным номером одного из устройств, управляемых одним и тем же драйвером. Большинство из этих имен устройств соответствуют определенному шаблону. Они состоят из символов fd (floppy disk - гибкий диск), цифры 0 или 1 (номер устройства), числа 48 (плотность, выраженная в виде ко- личества дорожек на дюйм), символов ss или ds (single-sided - односто- ронняя или double-sided - двусторонняя дискета) и цифры 8 или 9 (число секторов). Мы видим по индексным дескрипторам, что устройство fd0 связано с устройствами fd048 и fd048ds9. Самым информативным именем (и самым трудным при вводе с клавиатуры) является имя fd048ds9. Оно точно выра- жает, к какому устройству и типу носителя мы обращаемся. Для того что- бы упростить указание этого имени, устройство fd048ds9 связывается с более короткими именами. Все три имени файла являются корректными. Следующий список получен на машине AT, в которой имеется уст- ройство высокой плотности: ---------------------------------- | | 102 brw-rw-rw- 3 bin bin 2, 7 Jun 17 14:28 /dev/fd0 | 95 br--r--r-- 2 bin bin 2, 3 Jun 6 09:23 /dev/fd048 | 93 br--r--r-- 1 bin bin 2, 2 Jun 6 09:23 /dev/fd048ds8 | 95 br--r--r-- 2 bin bin 2, 3 Jun 6 09:23 /dev/fd048ds9 | 92 br--r--r-- 1 bin bin 2, 0 Jun 6 09:23 /dev/fd048ss8 | 94 br--r--r-- 1 bin bin 2, 1 Jun 6 09:23 /dev/fd048ss9 | 102 brw-rw-rw- 3 bin bin 2, 7 Jun 17 14:28 /dev/fd096 | 102 brw-rw-rw- 3 bin bin 2, 7 Jun 17 14:28 /dev/fd096ds15 | 99 brw-rw-rw- 3 bin bin 2, 11 Jun 26 19:34 /dev/fd1 | 99 brw-rw-rw- 3 bin bin 2, 11 Jun 26 19:34 /dev/fd148 | 97 br--r--r-- 1 bin bin 2, 10 Jun 6 09:23 /dev/fd148ds8 | 99 brw-rw-rw- 3 bin bin 2, 11 Jun 26 19:34 /dev/fd148ds9 | 96 br--r--r-- 1 bin bin 2, 8 Jun 6 09:23 /dev/fd148ss8 | 98 br--r--r-- 1 bin bin 2, 9 Jun 6 09:23 /dev/fd148ss9 | 103 brw-rw-rw- 2 bin bin 2, 15 Jun 6 09:23 /dev/fd196 | 103 brw-rw-rw- 2 bin bin 2, 15 Jun 6 09:23 /dev/fd196ds15 | Если мы посмотрим на записи с индексным дескриптором 102, начиная с середины списка, то увидим прогрессирующее упрощение имен по мере продвижения к первой записи - устройству 0, которое имеет высокую плотность записи по умолчанию. Для того чтобы обратиться к нему как к устройству с низкой плотностью записи, необходимо использовать имя fd048, а не fd0. Поскольку большинство используемых гибких дисков име- ют низкую плотность записи, то имя fd048ds9 является умолчанием в программе mntf. Строка 17 соответствует опции -1 для указания устройства 1 вместо устройства 0. Строки 18-22 проверяют, сиществует ли каталог для монти- рования второго устройства. Если вы используете два гибких диска од- новременно, то вы не можете монтировать их оба в один и тот же ката- лог. Для разрешения этой проблемы программа mntf использует для монти- рования устройства 1 каталог /mnt1, а не /mnt. Если каталог /mnt1 не существует, по умолчанию используется каталог /mnt, и все хорошо, если вы используете только устройство 1. Однако следует избегать монтирова- ния одного гибкого диска на место второго. Вы можете получить непред- виденные результаты. Если вы собираетесь монтировать два гибких диска, убедитесь, что у вас есть и каталог /mnt, и /mnt1. Строка 23 делает монтируемую файловую систему доступной только для чтения в случае, если была указана опция -r, что выполняется до- бавлением символов -r к имени каталога. Это не является частью имени каталога, но когда shell выполняет обработку команды, пробела между именем каталога и -r достаточно, чтобы распознать -r как опцию. Строка 24 соответствует опции -s и присваивает переменной SYSTEM значение sysv. Это означает, что нужно использовать другие соглашения об именах устройств. Строки 25-34 выполняют проверку на ошибки в командной строке. Лю- бая опция, отличная от уже проверенных, является ошибкой, поэтому все, что соответствует улавливающей ветке оператора case (*), считается не- допустимой опцией. В этом случае выводится синтаксическая подсказка, и программа завершается. В строках 39-42 выполняется вся основная работа. Оператор case действует в соответствии со значением переменной SYSTEM. Если оно рав- но "sysv", выполняется строка 40. В противном случае выполняется стро- ка 41 для системы XENIX. Обратите внимание, что в нашей версии команд- ного файла mntf в строке sysv имеется только переменная с номером уст- ройства. Если вы используете System V, вы можете добавить переменную для указания плотности записи или другие параметры, которые вам нужны. Строка 41 выполняет версию команды, рассчитанную на систему XENIX. Переменная CMD содержит, как мы отмечали, команду монтирования (mount) или размонтирования (umount). Последовательность символов /dev/fd указывает файл устройства для гибкого диска. Переменная DRIVE равна 0 или 1. Переменная DENSITY указывает устройство с высокой или низкой плотностью записи. Если должна быть выполнена команда монтиро- вания, переменная DIR содержит каталог. Если выполняется размонтирова- ние, значение переменной DIR равно нулю. ЗАМЕЧАНИЕ ПО ВОПРОСУ БЕЗОПАСНОСТИ Обычно только суперпользователь (root) может монтировать файловую систему. В больших системах это имеет смысл. Однако на небольших настольных машинах это может быть слишком ограничивающим фактором. Для того чтобы обойти это требование, используйте возможность изменения прав доступа. Чтобы позволить любому пользователю выполнять команды монтирования и размонтирования, примените следующие команды: # chown root /etc/mount <- делает пользователя root владельцем модуля /etc/mount # chmod 4511 /etc/mount и дает возможность выполнять команду mount всем пользователям # chown root /etc/umount <- делает то же самое для команды # chmod 4511 /etc/umount размонтирования Эти команды облегчают всем пользователям работу с гибким диском, но одновременно открывают огромную дыру в защите системы. Если кто-ли- бо уже проложил тропинку прав доступа на гибкий диск (см. главу 9), то монтирование файловой системы продолжит эту тропинку в главную систему и позволит такому пользователю стать суперпользователем всей системы в целом просто с гибкого диска! -------------------------------------------------------------- ИМЯ: mntlook -------------------------------------------------------------- mntlook Поиск файловых систем на устройствах НАЗНАЧЕНИЕ Просмотр всех файлов дисковых устройств и обнаружение всех файло- вых систем, включая немонтированные. ФОРМАТ ВЫЗОВА mntlook ПРИМЕР ВЫЗОВА mntlook /dev/hd* Поиск файловых систем на всех жестких дисках ТЕКСТ ПРОГРАММЫ 1 static char id[] = "@(#) mntlook v1.0 Look for mounts Author: Russ Sage"; Поиск файловых систем 2 3 #include 4 #include 5 #include 6 #include 7 #include 8 9 #define BSIZ 512 10 11 main(argc,argv) 12 int argc; 13 char *argv[]; 14 { 15 struct filsys sb; 16 int d, dev; 17 char buf[BSIZ]; 18 19 for (d = 1; d < argc; d++) 20 { 21 if (argv[d][0] == '-') 22 { 23 printf("mntlook: invalid argument %s\n", argv[d]); 24 printf("usage: mntlook device [device ...]\n"); 25 continue; 26 } 27 if ((dev = open(argv[d],O_RDONLY)) < 0) 28 { 29 sprintf(buf, "cannot open %s",argv[d]); невозможно открыть 30 perror(buf); 31 continue; 32 } 33 34 /* throw away first block */ обойти первый блок 35 if (read(dev, &sb, sizeof(sb)) == -1) 36 { 37 perror("cannot read block 0"); не читается блок 0 38 continue; 39 } 40 41 /* block 1 is the superblock */ блок 1 является суперблоком 42 if (read(dev, &sb, sizeof(sb)) == -1) 43 { 44 perror("cannot read block 1"); не читается блок 1 45 continue; 46 } 47 48 if (sb.s_magic == S_S3MAGIC) 49 { 50 printf("\nDEV: %s --> VALID file system\n",argv[d]); 51 printf("filsys: %s\n",sb.s_fname); 52 printf("pack : %s\n",sb.s_fpack); 53 printf("type : %s byte block\n", 54 (sb.s_type == S_B512) ? "512" : "1024"); 55 printf("magic : %lx\n",sb.s_magic); 56 } 57 58 close(dev); 59 } 60 } ОПИСАНИЕ ЗАЧЕМ НАМ НУЖНА ПРОГРАММА mntlook? Файловые системы являются сердцевиной системы UNIX. Сама система не существует вне файловой системы, а расширенные возможности системы обеспечиваются файловой системой. Даже несмотря на то, что файловые системы настолько важны, UNIX не имеет никаких средств для обнаружения файловых систем как таковых. Не имеется даже средств, которые могли бы сообщить нам, какая информа- ция находится в суперблоках. Нам необходимо универсальное средство для работы с файловыми системами. Оно должно обнаруживать и идентифицировать суперблоки, на- ходящиеся на устройствах. Заметим, что операция открытия устройства обычно требует наличия привилегий суперпользователя. ЧТО ДЕЛАЕТ mntlook? Программа mntlook предназначена для просмотра содержимого файлов устройств и поиска суперблока. (Мы вкратце рассматривали суперблоки ранее). Когда суперблок обнаружен, из него извлекается и выводится на экран имя файловой системы, имя дискового пакета, используемый размер блока и идентификационный "магический номер". Первоначальной целью данной утилиты было обнаружение на внешнем носителе машины таких файловых систем, которые в настоящий момент не смонтированы. Но когда данная программа открывает и читает устройство, ей все равно, было ли устройство смонтировано или нет, поскольку доступ к нему выполняется на более низком уровне, чем уровень файловой системы. В результате обнаруживаются все файловые системы, независимо от того, были они смонтированы или нет. Давайте рассмотрим, каким образом файловые системы связаны с фи- зическим носителем. К каждой машине подсоединено фиксированное число периферийных устройств. Для работы со сменными носителями в UNIX реа- лизована концепция монтированного и немонтированного файлового прост- ранства. Но первым шагом является установка пакета дисков (или гибкого диска) в дисковое устройство. После этого можно выполнять операции чтения и записи на физических носителях, указывая определенные имена устройств. Монтирование представляет собой логическое действие, которое чи- тает суперблок с пакета дисков и записывает его в память, где ядро системы поддерживает свою версию суперблока. Периодически выполняется запись версии, находящейся в памяти, обратно на диск, чтобы эти две версии были по возможности одинаковыми. В System V это делает програм- ма update, которая работает в фоновом режиме с момента загрузки систе- мы. Для обращения непосредственно к физическому носителю используются такие команды, как, например, команда "od -c /dev /rfd0", которая дам- пирует неструктурированный гибкий диск. Одной из команд, которые не- посредственно помещают данные на устройство, является команда "cp file /dev/rfd0". Область данных начинается с самого первого байта на гибком диске. Такие данные несовместимы с командами tar, cpio или файловой системой. Для обращения к файловой системе используется команда "mount /dev/fd0 /mnt". Начиная с этого момента, все обращения к данному уст- ройству выполняются через каталог /mnt. Важно то, что непосредственный доступ к файлу устройства является операцией более низкого уровня, чем операции файловой системы, что позволяет прочитать информацию о су- перблоке непосредственно с устройства. Входной информацией для программы mntlook являются имена файлов устройств. В командной строке нельзя указывать никакие опции. Имена устройств могут быть как блочными, так и символьными. Для операции чтения это не имеет никакого значения, если только у вас имеются права чтения. Затем программа читает второй блок и проверяет "магическое число", определяющее суперблок. Суперблок - это просто структура языка Си, которая предопределена системой и хранится в файле filsys.h, что указано в нашей программе оператором #include. Магическое число представляет собой длинное целое, имеющее заранее определенное значе- ние. Если элемент структуры, которая прочитана с диска, содержит это число, то считается, что остальная часть структуры является корректны- ми данными. Для каждой файловой системы имеется только одна структура суперблока. Если магическое число такое, как надо, выводится остальная инфор- мация о файловой системе. Если это число некорректно, на экран ничего не выводится и программа обрабатывает в цикле следующий файл уст- ройства, указанный в командной строке. Данная программа может служить в качестве функции, обеспечивающей безопасность, поскольку она умеет идентифицировать файловые системы, которые кто-либо оставил на машине несмонтированными. Как отмечается в главе 9, нарушители защиты могут читать данные с немонтированных уст- ройств, поэтому если оставить устройство или файловую систему немонти- рованными, то на самом деле это не предохраняет их от несанкциониро- ванного доступа. ПРИМЕРЫ 1. $ mntlook /dev/hd13 Поиск суперблока на устройстве с именем hd13. Это имя указывает устройство 1, третий раздел. Для просмотра разделов в среде XENIX нуж- но запустить программу fdisk. Для System V нужно воспользоваться ко- мандой iv. 2. $ mntlook /dev/fd0* Поиск файловых систем на гибких дисках с любой плотностью записи, находящихся в устройстве 0. Это снова пример для системы XENIX. 3. $ for DEV in /dev/*[fh]d* > do > echo "checking device: $DEV" > mntlook $DEV > done Данный цикл выполняется по всем именам устройств для гибих и жестких дисков по очереди. Каждое имя выводится на экран. Затем прове- ряется, содержит ли данное устройство файловую систему. ПОЯСНЕНИЯ Строки 3-7 определяют включаемые файлы, которые использует данная программа. Вам необходимо изучить эти файлы, поскольку они не только помогут вам понять работу этой программы, но и покажут вам некоторые важные значения, имеющие отношение к файловым системам. Строка 9 определяет размер используемого буфера. Этот буфер при- меняется только для хранения сообщений об ошибках, поэтому он не дол- жен быть очень большим. Строка 15 определяет структуру суперблока. Он имеет тип filesys (см. включаемый файл sys/types.h). На моей машине суперблок имеет раз- мер 1024 байта. Если вы не знаете точно размер буфера у вас, то вы мо- жете проверить его, вставив в программу следующий оператор: printf ("the size is %d",sizeof(sb)) Строка 16 описывает рабочую переменную d и дескриптор файла dev. Строка 17 объявляет буфер размером BSIZE. Строки 19-59 - это один большой цикл for. Этот цикл представляет собой всю остальную часть программы. Он выполняется столько раз, сколько аргументов указано в командной строке. Счетчик цикла начина- ется с 1, поскольку первым аргументом argv[0] является имя команды. В качестве аргументов должны быть указаны имена файлов, поэтому данный цикл использует каждое имя по очереди. В строках 21-26 проверяется, не начинается ли текущий рассматри- ваемый нами аргумент со знака '-'. Если да, то это опция, что представляет собой ошибку, поэтому выводится сообщение об ошибке и оператор continue вызывает выполнение следующей итерации цикла. Таким образом, данная программа отвергает опции, но работает при обнаружении имен файлов. Считая, что имя файла было найдено, строки 27-32 открывают файл устройства с этим именем только для чтения. Если данный вызов open не- удачен, мы посылаем сообщение в буфер вместе с именем, указанным в ко- мандной строке. Этот буфер передается программе обработки ошибок (системной), которая использует наше сообщение как первую часть своего сообщения об ошибке. Она выводит системное сообщение, которое опреде- ляет данную ошибку. По оператору continue начинается выполнение следу- ющей итерации цикла for. Строки 35-39 читают первый блок файла. Для корневой файловой системы первым блоком является загрузочная запись. Если при этом чте- нии возникает ошибка, выводится сообщение об ошибке и выполнение цикла продолжается. Строки 42-46 читают второй блок, который должен находиться в том месте, где размещается суперблок, если он имеется. По информации, про- читанной и помещенной в структуру, мы можем получить доступ к каждому элементу по его имени. Строка 48 проверяет, равно ли магическое число в структуре маги- ческому числу, определенному в файле заголовка. Если они совпадают, программа mntlook выводит имя файла устройства и сообщение о том, что файловая система корректна, имя файловой системы (если оно имеется), имя пакета дисков, размер используемого блока и магическое число в шестнадцатиричном виде. В строках 53-54 мы имеем подобие кодированной структуры: оператор printf использует структуру типа if-then-else для указания строки, ко- торую нужно выводить. После того как выведена первая строка, выполня- ется проверка и если она прошла успешно, то выводится значение 512. Если в результате проверки получено значение "ложь", выводится значе- ние 1024. Этот метод описан в книге B.W.Kernighan, D.M.Ritchie "The C Programming Language" (Prentice-Hall, 1978). Строка 58 закрывает устройство, и цикл возвращается в начало для принятия следующего имени устройства. РАЗМЕРНЫЕ ПАРАМЕТРЫ Теперь, когда мы рассмотрели взаимоотношения между устройствами и файловыми системами и некоторые параметры, связанные с форматами дисков, давайте обратимся к гайкам и болтикам этих устройств. Хотя основная часть этой информации может показаться экзотичной, она может оказаться важной при определенных обстоятельствах. Например, для уста- новки системы UNIX на новую машину вам нужно разбить диск на сегменты и понимать, каким образом UNIX фактически располагается на диске. Если вы создаете программы, которые выполняют какую-либо операцию низкого уровня с диском, вам, очевидно, необходимо понимать, что вы делаете. Администраторам, поскольку они должны добавлять новые устройства в систему, необходимо уметь определять количество файловых систем (т.е. сколько можно создать разделов на диске), их размеры и знать, каким образом установить файловые системы в эти разделы. Администраторы должны также уметь писать или модифицировать драйверы устройств. Нако- нец, при работе с дисками возникают проблемы, такие как плохие блоки, которые необходимо изолировать и с которыми приходится иметь дело. РАЗМЕРЫ БЛОКОВ System V является последним достижением ветви фирмы AT&T в фа- мильном дереве UNIX. Это означает, что System V содержит последние правки, внесенные в исходную систему UNIX. Эти правки предназначены для того, чтобы сделать UNIX жизнеспособным и стойким коммерческим продуктом. Для повышения устойчивости были внесены изменения, касающи- еся работы с файлами и размеров их блоков. Обычно обмен данными с дисками осуществляется блоками по 512 бай- тов. Дисковая аппаратура имеет дело именно с таким размером. Для учета этого факта UNIX первоначально использовал 512-байтные блоки внутри файловой системы, что, возможно, облегчало написание программ и созда- вало впечатление, что так и нужно. Однако нельзя отрицать, что при этом UNIX может работать медленно! Для ускорения работы внутренние программы в настоящее время используют блоки размером 1024 байта. Сам диск должен выполнить два обращения к 512-байтным блокам, но в системе две эти операции чтения рассматриваются как одна операция чтения блока размером 1024 байта. Единственная проблема заключается в том, что одни утилиты выдают ре- зультаты в 512-байтных блоках, а другие - в 1024-байтных, в зависи- мости от того, когда они были написаны. Когда сильно приближаются пре- делы свободного пространства на диске, вам действительно нужно знать, с каким размером вы имеете дело. Для лучшего понимания проблемы размеров блоков, в табл. 7-3 пока- зано, какие утилиты какой размер блока используют. Эта информация по- лучена в основном из System V на машине VAX, из другого варианта System V и из XENIX. Эти значения могут отличаться на разных машинах, но идея сохраняется. Вы видите, что большинство утилит выдают результат в блоках раз- мером 512 байтов, но утилиты, относящиеся к файловой системе, выдают результат в 1024-байтных блоках. Поскольку UNIX обращается к дисковому пространству поблочно, важно уметь точно вычислять, сколько свободного пространства в файловой системе. Весьма плоха ситуация, когда имеется какой-то большой файл в редакторе vi (который использует файл /tmp для промежуточного редактирования), а на диске недостаточно места для за- писи временного файла редактора vi в реальный файл на диске. На самом деле это может случиться на персональных машинах с ограниченным (ска- жем, 20 Мбайт) объемом жесткого диска. Таблица 7-3 Размеры блоков для различных команд системы UNIX ------------------------------------------------------------- 512 байтов/блок 1024 байта/блок ------------------------------------------------------------- ls -s fdisk (размеры разделов) sum mkfs cpio fsck df du ------------------------------------------------------------- РАСЧЕТЫ, СВЯЗАННЫЕ С БЛОКАМИ Еще одним важным вопросом, имеющим отношение к физическим уст- ройствам и логическим файловым системам, является определение местона- хождения определенного блока на жестком диске. Номер этого блока вы- числяется по номерам цилиндра, дорожки и сектора. Знание номеров блоков становится важным, когда на диске появля- ется дефектное место. Это дефектное место отмечается номерами цилиндра и головки. Вам необходимо вычислить, какие блоки попадают в дефектную область и занести их номера в таблицу дефектных блоков. Обратная задача также важна. Если программа fsck начинает сооб- щать, что где-то появился дефектный блок, то каким образом мы можем узнать номера цилиндра, головки, сектора и т.д. для данного дефектного блока? Такое обратное вычисление сделать очень тяжело, если не невоз- можно. Во-первых, номер блока представляет собой произведение четырех чисел. Трудно узнать, какие именно эти числа. Кроме того, файловые системы могут использовать информацию вида база/смещение, поэтому блок номер 1 в файловой системе в действительности является блоком номер 1382 на диске. Определить, какого вида информация была использована в данном случае, тоже тяжело. Конкретные данные в следующем примере относятся к вполне опреде- ленной машине, но на других машинах используются подобные зависимости. Эти данные относятся к машине с жестким диском объемом 20 Мбайт с сиcтемами XENIX/DOS. Характеристики устройства: 1 диск = 615 цилиндров, или 615 цилиндров/диск 1 цилиндр = 4 головки (дорожки), или 4 головки/цилиндр Промышленный стандарт: 1 дорожка = 17 секторов, или 17 секторов/дорожку 1 сектор = 512 байт, или 512 байт/сектор 1 Кбайт = 1024 байта = 2^10 1 Мбайт = 1024 килобайта = 2^20 = 1 048 576 байт Характеристики устройства различны для разных устройств, но про- мышленный стандарт для числа секторов на дорожку и байтов на сектор остается одинаковым. В табл. 7-4 показаны примеры характеристик раз- личных устройств. Таблица 7-4 Размеры жестких дисков и их конфигурация -------------------------------------------------------------- Число цилиндров Число головок Мегабайты -------------------------------------------------------------- 981 3 25 697 5 30 981 5 42 925 7 55 1024 8 71 --------------------------------------------------------------- Вы видите, что число цилиндров и число дорожек различны для уст- ройств с разным объемом. Определить максимальный объем дисковой памяти можно перемножением всех чисел. В следующем примере вычисляется общий размер в байтах для предыдущих данных. 615 цил 4 дор 17 сек 512 байт ------- * ----- * ------ * -------- = 21 411 840 байт/диск 1 диск 1 цил 1 дор 1 сек 21411840 байт 1 мегабайт ------------- * ------------ = 20.4 мегабайта/диск 1 диск 1048576 байт Отметим, что если вы верно указываете единицы измерения, то они попарно сокращаются, за исключением одной снизу и одной сверху, и в результате получаются нужные единицы измерения в ответе. Таким обра- зом, в первой строке вычислений цилиндры, дорожки и секторы сокраща- ются, и в качестве единиц измерения остаются байты/диск. Поскольку мы имеем дело с таким большим количеством различных единиц измерения, вы- числения такого рода (иногда называемые "размерный анализ") убеждают нас, что мы понимаем, о чем идет речь. Объем доступной дисковой памяти уменьшается после форматирования, локализации дефектных блоков и размещения на диске файловой системы. Однако наш пример показывает, что все размеры согласуются между собой. Важным моментом, на который необходимо обратить внимание, явля- ется использование разных терминов. Иногда применяется число головок на цилиндр, а иногда число дорожек на цилиндр. При использовании каж- дого из этих терминов соответствующим образом изменяются и другие тер- мины. Существует такая взаимосвязь: цилиндр, дорожка, сектор = физический сектор цилиндр, головка, байт = блок Эти две записи выражают в точности одно и то же. Когда вы исполь- зуете запись вида цилиндр/дорожка/сектор, то в результате получаете физический сектор. Используя запись вида цилиндр/головка/байт, вы по- лучаете в результате номер блока. Следует помнить, что ГОЛОВКА - это то же самое, что и ДОРОЖКА. Если вы это запомните, все остальное вста- нет на свое место. Ниже приводятся некоторые общие вычисления, которые часто всплы- вают, когда вы работаете с диском на низком уровне. Эти примеры в большей степени относятся к самому устройству, чем к системе UNIX. Од- нако после того, как вы поймете эту информацию о диске, вам будет лег- че понять, как работает UNIX на этом уровне. 1. Сколько дорожек имеет диск? 615 цил 4 дор Решение: ------- * ----- = 2460 дор/диск 1 диск 1 цил 2. Сколько байт в дорожке? 17 сек 512 байт Решение: ------ * -------- = 8704 байт/дор 1 дор 1 сек 3. Сколько дорожек в одном мегабайте? 2460 дор 1 диск Решение: -------- * ------ = 123 дор/Мб 1 диск 20 Мб 4. Сколько цилиндров в одном мегабайте? 1 цил 2460 дор Решение: ----- * -------- = 30 цил/Мб, 4 дор 20 Мб 615 цил 1 диск 123 дор или ------- * -------- * ------- = 30 цил/Мб 1 диск 2460 дор 1 Мб 5. Дан цилиндр 47, дорожка 2, сектор 4. Какой физический номер секто- ра? Решение: Сначала мы обращаем внимание на то, что вопрос касается секторов. В качестве единиц измерения даны цилиндр, дорожка и сектор. Как пере- вести их в другие единицы? Мы знаем, что головки - это то же самое, что и дорожки, поэтому в вычислениях нужно использовать 4 головки вместо 4 дорожек: 4 дор 17 сек 17 сек 47 цил * ----- * ------ + 2 дор * ------ + 4 сек = 1 цил 1 дор 1 дор = 3196 сек + 34 сек + 4 сек = = сектор 3234 РАЗМЕРЫ ФАЙЛОВ В основном при работе в системе UNIX мы считаем, что ее ресурсы безграничны. Например, мы не заботимся о том, что созданный файл полу- чится "слишком большим", а это не так уж редко в персональных компь- ютерах на гибких дисках. Если же мы занимаемся сопровождением и адми- нистрированием системы UNIX, то мы должны быть готовы иметь дело с си- туациями, когда превышаются различные предельные значения системы. Всегда лучше исследовать эти вопросы заранее в некритичных ситуациях, поэтому давайте рассмотрим пределы размеров файлов и их смысл. Некоторые параметры "зашиты" в ядро системы при ее генерации. Од- ним из таких значений является максимальный размер файла. Он определя- ет наибольшее число блоков, которые может занимать файл. Этот параметр тесно связан с принятым в UNIX методом использования индексных деск- рипторов файла (inodes). Это наборы указателей, среди которых первые десять указывают на блоки данных, следующий указывает на другую табли- цу, следующий - на таблицу, указывающую на таблицу и т.д. Имеется еще одно ограничение размера файла, которое определено для каждого пользователя во время работы в системе - число ulimit (user limit - пользовательский предел). Это значение устанавливается в момент вашей регистрации в системе и представляет собой число блоков по 512 байт, которые вы можете записать в любой заданный файл. В shell'е имеется команда ulimit, которая при ее вызове без аргументов выводит это число. Эта же команда позволяет вам уменьшить ваше значе- ние ulimit. Только суперпользователь (root) может УВЕЛИЧИТЬ значения ulimit. Побочным эффектом уменьшения значения ulimit является то, что вы не можете снова увеличить его до регистрационного значения. Значение ulimit остается таким же на все время работы вашего shell, поэтому для восстановления регистрационного значения вам необходимо выйти из системы, а затем снова зарегистрироваться. Еще одним интересным моментом является то, что если вы установите ваше значение ulimit равным 0, вы не сможете создать никакие файлы! Максимально допустимым размером файла в данном случае является нуле- вой, поэтому никакой файл не может быть создан. Это представляется достаточно резонным, однако существуют такие ситуации, когда файл ну- левого размера МОЖЕТ существовать. Опять же, для восстановления вашего обычного значения ulimit необходимо выйти из системы, а затем снова зарегистрироваться. Как отмечалось ранее, увеличить значение ulimit может только су- перпользователь. Эта процедура довольно проста. Сначала нужно увели- чить значение ulimit командой ulimit, а затем запустить shell. Этот новый shell имеет новое значение ulimit. Если мы хотим, чтобы система загружалась с shell, имеющим большее значение ulimit, мы можем устано- вить программу в inittab (таблице инициализации системы), чтобы эта операция выполнялась автоматически. Ниже приводится пример программы, которая изменяет значение ulimit и запускает shell с этим новым значением. Напомним, что эта программа может быть запущена только суперпользователем. 1 #include 2 #include 3 4 main() 5 { 6 long v1, v2, v3, newlimit = 5120; 7 8 v1 = (long)ulimit(UL_GFILLIM, 0L); 9 v2 = (long)ulimit(UL_SFILLIM,newlimit); 10 v3 = (long)ulimit(UL_GFILLIM, 0L); 11 12 printf("v1: %ld v2: %ld ulim: %ld\n",v1,v2,v3); 13 setuid(getuid()); 14 execl("/bin/sh","ulimit sh", 0); 15 } Значение ulimit является возвращаемым значением системного вызова ulimit. Первый вызов ulimit в строке 8 получает исходное значение по умолчанию. Это значение сохраняется в переменной v1. Вызов в строке 9 устанавливает новое значение ulimit равным значению переменной newlimit. Если этот вызов оканчивается неудачей, переменной v2 присва- ивается возвращаемое значение -1, и мы видим это по распечатке, кото- рую выдает строка 12. Если вызов был успешным, возвращаемым значением является новое значение ulimit, и это мы тоже видим. Затем вызов в строке 10 получает это значение ulimit. Это или новое значение, или старое, в зависимости от того, была ли успешной попытка изменить ulimit. В строке 13 значение идентификатора текущего процесса устанавли- вается равным значению идентификатора пользователя, запустившего дан- ный процесс. Это сработает только в том случае, если пользователь, за- пустивший данный shell, имеет более низкий идентификатор, чем сам про- цесс. Цель заключается в том, чтобы предоставить возможность обычным пользователям запускать данный процесс, давая им временно права супер- пользователя. (Не оставляйте исходный текст этой программы в системе, поскольку кто-то может превратить ее в "лазейку" и перекомпилировать ее - в главе 9 мы увидим такого рода дыры в системе защиты.) Строка 14 запускает shell. Аргументом этого shell является строка "ulimit sh". Эта строка будет выведена на экран, если мы выполним ко- манду "ps -ef". Данный shell имеет новое значение ulimit. Возможность изменить значение ulimit позволяет нам определить на- ибольший возможный размер файла. Создание одного или нескольких таких файлов максимального размера полезно в целях тестирования. Например, полезно выяснить, сколько данных может содержать гибкий диск без пере- полнения или что произойдет, когда система выйдет за пределы свободных блоков. Мы хотим понять, как ведет себя система в таких ситуациях. ------------------------------------------------------------- ИМЯ: umntsys ------------------------------------------------------------- umntsys НАЗНАЧЕНИЕ Размонтирование всех файловых систем, смонтированных в данный мо- мент. ФОРМАТ ВЫЗОВА umntsys ПРИМЕР ВЫЗОВА umntsys Размонтирует все смонтированные файловые системы ТЕКСТ ПРОГРАММЫ 1 : 2 # @(#)umntsys v1.0 Unmount all file systems Author: Russ Sage Размонтирование всех файловых систем 3 4 if [ "$#" -gt 0 ] 5 then echo "umntsys: too many arguments" >&2 6 echo "usage: umntsys" >&2 7 exit 1 8 fi 9 10 /etc/mount | sed -n -e '/^\/ /d' -e 's/^.* on \(.*\) read.*/umount \1/p' | sh - ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ umntsys? Иногда возникают ситуации, когда вы как администратор хотели бы запустить систему в однопользовательском режиме. Например, вы хотите сменить или установить жесткие диски и вам нужно, чтобы никто не имел доступа к этому устройству, пока вы с ним работаете. Вам может также понадобиться запустить систему в минимальной конфигурации с целью ло- кализации какой-то проблемы. Поскольку выполнение операций завершения работы системы и перезагрузки представляет собой довольно длительную процедуру, было бы лучше иметь способ сохранить систему работающей, но переключить в однопользовательский режим, а затем быстро перезапустить многопользовательский режим, чтобы свести к минимуму неудобства поль- зователей. Для того чтобы сделать это, нам нужно понять концепцию "уровней работы системы" и использовать их. Уровень работы (run level) в системе UNIX представляет собой состояние или конфигурацию, в которой может быть машина. Фактически это число, которое определяет, какие возможности системы включены или отключены и находится ли система в одноили многопользовательском режи- ме. Описание того, что происходит на каждом уровне работы системы, со- держится в файле /etc/inittab. Обычно изменение уровня работы системы включает в себя переход от многопользовательского режима (например, уровень 6), к однопользовательскому режиму (уровень S). Одним из побочных эффектов перехода от многопользовательского ре- жима к однопользовательскому является то, что все дополнительные фай- ловые системы размонтируются. Единственной смонтированной файловой системой является корневая (определенная как /dev/root, /dev/hd0a и т.п.). Ее никогда нельзя размонтировать. Когда происходит переход об- ратно к многопользовательскому режиму, файловые системы обычно повтор- но монтируются с помощью файла /etc/rc. Мы можем эмулировать однопользовательский режим путем прекращения выполнения всех процессов в системе командой kill и размонтирования всех файловых систем. Командный файл umntsys предназначен для этой це- ли. ЧТО ДЕЛАЕТ umntsys? Командный файл umntsys представляет собой набор конвейерных про- цессов, которые в конечном итоге выполняют размонтирование всех смон- тированных в данный момент файловых систем. Корневая файловая система распознается как особая, поэтому не делается попытка размонтировать ее. Также исключается попытка размонтировать немонтированные файловые системы. ПОЯСНЕНИЯ Первым делом командный файл umntsys проверяет отсутствие аргумен- тов в командой строке. Поскольку для него не существует опций, команд- ная строка должна быть пустой. Если количество аргументов больше нуля, это ошибка, поэтому на стандартное устройство регистрации ошибок выво- дится сообщение об ошибке, и программа завершается. Вся работа выполняется в строке 10. Этот оператор похож на вол- шебное заклинание. Начинается он с выполнения обычной команды mount без аргументов. По умолчанию команда mount выводит таблицу с информа- цией обо всех каталогах и именах устройств монтированных файловых систем. Эта таблица выглядит примерно так: ----------------------- | | / on /dev/hd0a read/write on Mon Jan 06 09:53:03 1986 | /tmp on /dev/hd01 read/write on Mon Jan 06 09:53:03 1986 | /usr on /dev/hd02 read/write on Mon Jan 06 09:53:03 1986 | /u1 on /dev/hd03 read/write on Mon Jan 06 09:53:03 1986 | /u2 on /dev/hd04 read/write on Mon Jan 06 09:53:03 1986 | /u3 on /dev/hd05 read/write on Mon Jan 06 09:53:03 1986 | /mnt on /dev/fd01 read/write on Mon Jan 06 09:54:41 1986 | Когда файловая система смонтирована, требуются и каталог, и имя устройства. Когда файловая система не смонтирована, используется толь- ко имя устройства. Нам нужно вырезать имена устройств из таблицы мон- тирования и вставить их в команду umount. Это делается с помощью ко- манды sed. Команда sed начинает работать с опцией -n, которая подавляет вы- полняемый по умолчанию вывод на экран, поэтому ничего не выводится, пока мы не попросим. Мы можем использовать это в своих интересах, от- фильтровывая ненужные нам строки. Первой коррекцией таблицы смонтиро- ванных файловых систем является избавление от записи о корневой файло- вой системе, поскольку мы бы не хотели пытаться ее размонтировать. Поскольку корневой файловой системе соответствует каталог "/", мы мо- жем использовать его в качестве ключа. Выражение в операторе sed озна- чает: "Искать с начала строки первый символ наклонной черты (поскольку этот символ имеет специальное значение, он экранирован обратной косой чертой) и пробел за ним. Когда наклонная черта найдена, удалить ее". Данный шаблон поиска соответствует только записи о корневой файловой системе. Следующая операция редактирования выполняется более замысловато. Она использует возможность группирования регулярных выражений и после- дующей ссылки на них по номеру, что вы уже видели в некоторых наших предыдущих командных файлах. Данный синтаксис (регулярное выражение) предназначен для группирования символов и последующей ссылки на них с помощью номера \n. Фокус в том, чтобы выделить только имя устройства и сгруппировать его, что и делает команда подстановки sed'а. Первое вы- ражение означает: "От начала строки распознать любой символ, за кото- рым следует любое количество любых символов, пробел и слово `on'; сгруппировать следующие символы вплоть до пробела, слово `read' и все символы после него". В результате всего этого выделяется имя уст- ройства и помещается во временную переменную, чтобы впоследствии к ней можно было обратиться. Вторая часть подстановки создает новую строку взамен исходной. Эта строка состоит из слова "umount", пробела, затем группового выра- жения номер 1, которое представляет собой временную переменную, содер- жащую имя устройства. В результате всех этих действий таблица смонти- рованных файловых систем (за исключением записи о корневой системе) превращается в набор команд размонтирования с именами устройств в ка- честве аргументов. Полученный результат имеет примерно такой вид: ------------------- | | umount /dev/hd0a | umount /dev/hd01 | umount /dev/hd02 | umount /dev/hd03 | umount /dev/hd04 | umount /dev/hd05 | umount /dev/fd01 | Теперь эти команды по конвейеру передаются другому shell ("sh -"). Символ "-" указывает shell, что свои команды он должен получать со стандартного ввода, а в данном случае это наши команды umount, пе- реданные по конвейеру. Они размонтируют все файловые системы. ------------------------------------------------------------ ИМЯ: lrgf ------------------------------------------------------------ lrgf Создает файл максимально возможного размера НАЗНАЧЕНИЕ Выполняет операции записи в файл до тех пор, пока не обнаружится граница размера файла. ФОРМАТ ВЫЗОВА lrgf ПРИМЕР ВЫЗОВА lrgf Определение границы размера файла ТЕКСТ ПРОГРАММЫ 1 char id[] = "@(#) lrgf v1.0 Create the largest file Author: Russ Sage Создать файл максимального размера 2 3 #include 4 #include 5 #include 6 #include 7 8 #define FSIZ 512 9 #define BSIZ 1024 10 11 long ulimit(); 12 char buf[BSIZ]; 13 14 main() 15 { 16 register int n, fd, bcnt; 17 char file[FSIZ]; 18 19 for (bcnt=0; bcnt do > echo -r "--> Making file $F <--" > ./lrgf <<-! > $F > ! > echo > F=`expr $F + 1` > done Данный цикл запускает программу lrgf бесконечное число раз. Счет- чиком является переменная F. Она должна быть предварительно установле- на в нуль, чтобы shell рассматривал ее как число, а не как символьную строку. Сначала выводится сообщение, содержащее имя создаваемого фай- ла. Первым именем файла является 0. Программа lrgf запускается, используя в качестве входных данных "данный документ" (т.е. сам ко- мандный файл). В качестве ответа на вопрос об имени файла используется значение $F. Значение переменной F увеличивается, и программа lrgf вы- зывается снова. Именами файлов являются 0, 1, 2 и т.д. Это продолжа- ется до тех пор, пока не останется больше свободного места. Вряд ли вы будете пользоваться этим часто, но для тестирования это прекрасное средство заполнить все свободное пространство. Если вы хотите увидеть, что делает ваша система, когда исчерпаны свободные блоки, примените данный командный файл. ПОЯСНЕНИЯ Строки 3-6 включают все необходимые файлы заголовков. Эти файлы содержат определения и метки, необходимые данной программе. Строки 8 и 9 определяют размеры буфера для имен файлов и буфера для записи на диск. Значение BSIZ можно поднастроить, если программа работает слишком медленно. У вас может возникнуть желание увеличить BSIZ до 4096, чтобы производилось не так много операций записи. Строка 11 определяет возвращаемое значение системного вызова ulimit как длинное целое. Строка 12 резервирует буфер, который должен быть записан. Этот буфер находится вне основной части программы из-за ограничений на размер внутри функций. В основном блоке программы наи- большая область автоматической памяти, которую вы можете иметь, равна размеру вашего стека. Вы можете сделать по-другому, объявив данный бу- фер как статическую переменную в функции main. Мы решили вынести его за пределы функции main и не объявлять как статическую переменную. Строка 16 объявляет некоторые рабочие переменные. Заметим, что они помещаются в регистры. Это сделано для ускорения работы программы и получения более компактного объектного кода. Строка 17 резервирует буфер, в который вы вводите имя файла. Строки 19 и 20 заполняют записываемый буфер символами "x", поэто- му после создания файла мы можем их там увидеть. Строка 22 выводит значение ulimit для вашего процесса. Обратите внимание, что вызов ulimit возвращает количество блоков, поэтому мы должны умножить это число на 512. В результате мы получим общее коли- чество байтов, которое может содержать файл. Строки 24-26 запрашивают имя файла, читают его и подготавливают экран для следующего сообщения. Строки 28-32 открывают файл с указан- ным именем для получения дескриптора файла. Файл открывается для за- писи и чтения, создается при необходимости и обрезается, если он уже существует. Если операция открытия файла заканчивается неудачей, выво- дится сообщение об ошибке, и программа завершается. Строки 34-42 выполняют запись. Цикл for бесконечен, поскольку в середине оператора нет проверки значения счетчика. Переменная bcnt постоянно увеличивается, пока выполняется запись. Строка 36 выполняет запись в файл. Если запись неудачна, выво- дится сообщение об ошибке и по оператору break осуществляется выход из цикла. Строка 41 выводит количество выполненных операций записи и ко- личество записанных байтов. Обратите внимание, что данный оператор print содержит возврат каретки (\r), а не перевод строки. Это позволя- ет курсору оставаться в одной итемах. экране поверх старых значений. Экран не скроллируется, что удобно для наблюдения. Выполнение цикла продолжается до тех пор, пока системный вызов write не закончится неудачей и оператор break не прекратит цикл. Когда это происходит, выполнение продолжается со строки 43, которая печатает "end of program". Выполнение команды "ls -l" для записанного файла показывает, сколько байтов имеет файл максимального размера. Это количество должно совпадать с числом, которое сообщила вам программа lrgf. В данной главе представлена лишь небольшая часть возможных ислле- дований внутренней работы файловых систем и устройств в UNIX. Некото- рые из представленных программ могут быть неприменимы в вашей версии системы UNIX или в вашей конфигурации аппаратных средств или могут выглядеть в вашей системе иначе. Однако общие принципы сохраняются, и вы можете использовать рассмотренные средства в качестве основы для ваших собственных исследований.