Приложения

Приложение А

Библиография

A.1. Аннотированная библиография.

Эта аннотированная библиография охватывает книги по теории операционных систем так же, как и по разным видам программирования в среде UNIX. Указанная цена может быть точной, а может и нет, но будет вполне преемлема для правительственной работы. [Если у вас есть книга, которая, по-вашему, подходит для библиографии, напишите ее краткий обзор и пошлите необходимую информацию (заголовок,автор, издательство, ISBN и приблизительная цена) и обзор по адресу johnson@sunsite.unc.edu]. Эта версия постепенно отходит, в то время как появляется настоящая библиография. Заглавие: The Design of the UNIX Operating System Автор: Maurice J. Bach Издательство: Prentice Hall, 1986 ISBN: 0-13-201799-7 Приближенная цена: $65.00

Это одна из книг, которые Linus использовал при разработке Linux. Это описание структур данных, используемых в ядре System V. Множество имен важных функций в исходных текстах Linux пришли из этой книги, и названы по алгоритмам, представленным в ней. Например, если вы не можете догадаться, что делают функции getblk(), brelse(), bread(), breada() и bwrite(), глава 3 об'яснит это очень хорошо.

В то время как большинство алгоритмов схожи или одинаковы, стоит отметить несколько различий:

Есть и другие маленькие отличия, но хорошее понимание этой книги поможет вам разобрать исходные тексты Linux. Заглавие: Advanced Programming in the UNIX Environment Автор: W. Richard Stevens Издательство: Addison Wesley, 1992 ISBN: 0-201-56317-7 Приближенная цена: $50.00

Этот замечательный томик охватывает все тонкости, которые вы действительно должны знать, чтобы писать настоящие UN*X программы. Он включает обсуждение различных стандартов релизации UN*X, включая POSIX, X/Open XPG3и FIPS, и концентрируется на двух реализациях, SVR4 и предварительный выпуск 4.4 BSD, который упоминается в книге, как 4.3 + BSD. Заглавие: Advanced 80386 Programming Techniques Автор: James L. turley Издательство: Osborne McGraw-Hill, 1988 ISBN: 0-07-881342-5 Прибл. Цена: $22.95

Эта книга достаточно хорошо охватывает 80386, не затрагивая других аппаратных средств. В книгу включены примеры кода. Охвачены все главные возможности, также как и множество основных понятий. Книга включает следующие главы: Основы, Сегментация памяти, Уровни привелегий, Замещение страниц, Многозадачность, Связь между задачами, Обработка сбоев и прерываний, Эмуляция 80286, Эмуляция 8086, Отладка, Математический процессор 80387, Сброс и реальный режим, Аппаратное обеспечение, и несколько приложений включая таблицы управления памятью в качестве справочника.

У автора хороший стиль изложения: если у вас технический склад ума, вы найдете захватывающим чтение этой книги. Сильная сторона этой книги в том что автор не обьясняет ни как делать что либо под DOS, ни как обращаться с конкретной аппаратурой. Фактически единственное место где он упоминает DOS и PC-совместимое аппаратное обеспечение это во введении где он обещает больше не упоминать о них. Заглавие: The C programming Language, second edition. Автор: Brian W. Kernighan and Dennis M. Ritchie Издательство: Prentice Hall, 1988 ISBN: 0-13-110362-8 Прибл. Цена: $35.00

Библия по программированию на Си. Включает учебник по Си, справочник по UN*X интерфейсу, справочник по Си и справочник по стандартным библиотекам. Программируете на Си, купите эту книгу. Это просто. Заглавие: Operating Systems: Design and Implementation Автор: Andrew S. Tanenbaum Издательство: Prentice Hall, 1987 ISBN: 0-13-637406-9 Прибл. Цена: $50.00

В то время как эта книга имеет немного упрощенное описание некоторых тем и опускает некоторые важные моменты, она дает достаточно четкое представление о том что надо сделать чтобы написать операционную систему. Пол книги занимает исходный код клона UN*X называемого Minix, который основывается на микроядре, в отличии от Linux, который славится монолитным дизайном. Было сказано что Minix показывает возможность написания UN*X, основанного на микроядре, но не обьясняется в достаточной степени зачем нужно это делать.

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

На ранних порах существования Linux, Эндрю Таненбаум начал жаркую войну с Linus по поводу разработки ОС, которая была интересной, если не поучительной.

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

К сжалению упор делается на основы, в то время как такие вещи как виртуальная память не охвачены вообще. Заглавие: Modern operating systems Автор: Andrew S. Tanenbaum Издательство: Prentice Hall, 1992 ISBN: 0-13-588187-0 Прибл. Цена: $51.75

первая половина книги это перепечатка более ранней Operating systems, но эта книга включает некоторые вещи не раскрытые в ранней, включая такие вещи как виртуальная память. Minix не упоминается, но есть обзор MS-DOS и нескольких других распостраненных систем. Эта книга вероятно более полезна для тех кто зочет углубить свои знания, чем более ранняя книга Таненбаума Operating systems: Design and Implementation. Причину этого можно видеть в заголовке.. Однако что делает DOS в книге по СОВРЕМЕННЫМ операционным системам многие не могут понять. Заглавие: operating Systems Автор: William Stallings Издательство: Macmillan, 1992 (800-548-9939) ISBN: 0-02-415481-4 Прибл. Цена: $??.??

Наиболее полнай текст по операционным системам, эта книга дает более глубокий подход к темам раскрытым в книге Таненбаума, и охватывает больше тем, имеет живой стиль мзложения. Эта книга охватывает все главные темы которые понадобятся вам для написания операционной системы, и делает это очень доступным образом. Автор использует примеры из трех главных систем сравнивая и противопоставляя их: UN*X, OS/2, и MVS. В каждом разделе эти системы используются для разяснения пунктов и приведения примеров реализации.

Темы охваченные в Operating Systems включают Нити (Связи), системы рельного времени, Планировка в Мультипроцессорах, распределенные системы, миграция процессов, и Безопасность, также как и стандартные темы как планировка управление памятью. Раздел по распределенной обработке похоже вполне современен, и я нахожу его очень полезным. Заглавие: UNIX Network programming Автор: W. Richard Stevens Издательство: Prentice Hall, 1990 ISBN: 0-13-949876-1 Прибл. Цена: $48.75

Эта книга охватывает несколько видов работы в сетях под UN*X, и содержит очень полезные справки по формам сетевой обработки которые она непосредственно не охватывает. Она охватывает TCP/IP и XNS особенно полно, и довольно исчерпывающе описывает как работают все вызовы. В ней также есть описание и пример кода использующего TLI System V, и достаточно полное описание IPC System V. Книга содержит много примеров исходного кода и много полезных процедур. Один из примеров это код реализующий используемые семафоры, основанный на частично- фрагментированной реализации которая применяется в System V. Заглавие: Programming in the UNIX environment Автор: Brian W. Kernighan and Robert Pike Издательство: Prentice Hall, 1984 ISBN: 0-13-937699 (hardcover) 0-13-937681-X (paperback) Прибл. Цена: $??.?? Нет Аннотации. Заглавие: Writing UNIX Device drivers Автор: George Pajari Издательство: Addison Wesley, 1992 ISBN: 0-201-52374-4 Прибл. Цена: $32.95

Эта книга написана президентом и основателем Driver Design Labs, компании специалзирующейся на разработке драйверов устройств для UN*X. Эта книга отличное введение в порой суровый мир разработки драйверов устройств. Сначало кратко обсуждаются четыре основные типа драйверов (символьные, блочные, tty, STREAMS). Приведено множество полных примеров драйверов устройств разных типов, начиная с простейших и с растущей сложностью. Все примеры драйверов работают под UN*X на PC-совместимой аппаратуре. Включены следующие главы:

Character Drivers I: A Test Data Generator Character Drivers II:
An A/D Converter Character Drivers III: A Line Printer Block Drivers I:
A Test Data Generator Block Drivers II:
A RAM Disk Driver Block Drivers III: A SCSI Disk Driver Character
Drivers IV: The Raw Disk Driver Terminal Drivers I: The COM1 Port
Character Drivers V: A Tape Drive STREAMS Drivers I:
A Loop-Back Driver STREAMS Drivers II: The COM1 Port (Revisited) Driver
Installation Zen and the Art of Device Driver Writting.

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

Приложение B.

Обзор исходного текста ядра Linux.

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

В качестве отправной точки обзора возьмем загрузку системы. Для осознания этого материала требуются хорошее знание языка Си и практически полное представление о концепциях UN*X и архитектуре ПК.

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

Любой путь, встречающийся в этой главе, относится к основному кодовому дереву каталогов, обычно это /usr/src/linux.

Большинство информации, представленной в главе, взята из Linux 1.0, однако здесь встречаются ссылки на более поздние версии. Любой параграф в этой главе, выделенный так же, как этот, означает описание изменений в ядре по отношению к Linux 1.0. Если в параграфе нет подобных ссылок, это означает, что исходный текст не претерпел изменения в версиях 1.0.9 - 1.1.76. Иногда выделенные места появляются в тексте в качестве ссылок на исходный текст.

B.1. Загрузка системы.

Во время загрузки ПК процессор 80x86 запускается в режиме реального времени и запускает код ROM-BIOS по адресу 0xFFFF0. PS BIOS проводит тестирование системы и инициализирует вектор прерывания на 0-й физический адрес. После этого она загружает сектор загрузочного устройства по адресу 0x7C00 и обращается по этому адресу. Это устройство обычно представляет собой жесткий диск или накопитель в дисководе. Данная выкладка сильно упрощена, однако она дает представление о инициализации ядра.

Основная (первая) часть ядра Linux была написана на ассемблере 8086 (boot/bootsect.s). Во время запуска она помещает себя по абсолютному адресу 0x90000, считывая следующие 2Кб кода с загрузочного устройства по адресу 0x90200 и часть ядра по адресу 0x10000. Во время загрузки системы появляется сообщение "Loading...". Далее контроль передается коду в boot/Setup.S (другой исходник режима реального времени на ассемблере).

Установленная часть определяет остальные компоненты системы и тип карты vga. Если нужно, она может дать право выбора видеорежима. Затем она переносит всю систему с адреса 0x10000 по адресу 0x1000, включает защищенный режим и обращается к остальной части системы (по адресу 0x1000).

Следующим шагом является распаковка ядра. Код по адресу 0x1000 берется из zBoot/head.S, которая устанавливает регистры и вызывает decompress_kernel(), которая создает zBoot/inflate.c, zBoot/unzip.c и zBoot/misc.c. Распакованная информация помещается по адресу 0x1000000 (1Мб), поэтому Linux не может быть запущена на компьютере с ОЗУ, меньшим 1Мб.

Сокрытие ядра в файле gzip делается Makefile и утилитами в каталоге zBoot. Среди них есть занимательные программы. Ядро версии 1.1.75 помещает каталоги boot и zBoot в arch/i386/boot. Это изменение позволяет ядру подстраиваться под разные архитектуры.

Распакованный код запускается по адресу 0x1010000, где делаются все 32- битные установки: загружаются IDT, GDF и LTD, производятся установки процессору и сопроцессору, устанавливаются страницы и вызывается подпрограмма start_kernel. Исходные тексты предыдущих операций находятся в boot/head.S. Это наиболее изощренный код во всем ядре.

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

start_kernel помещается в init/main и никогда не прекращает работу.

Единственное, что до этого момента написано на Си - это управление прерываниями и системный вызов enter/leave (однако и здесь большинство макросов нвписано на ассемблере). В.2

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

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

Процесс init петаетса запустить /exec/init, или /bin/init, или /sbin/init. Если ни один из перечисленных методов не запускается, система запускает "/bin/sh /etc/rc" и ведает основную оболочку на первый терминал. Эта процедура была написана в Linux 0.01, когда ОС состояла из одного ядра и не поддерживала операцию login.

После запуска функцией exec() программы init с одной из стандартных позиций (предположим что мы находимся в одной из них), ядро не контролирует процесс работы программы. Его ролью в этот момент становится поддерживать процессы с помощью системных вызовов и обслуживать асинхронные события, такие как прерывания аппаратного обеспечения. Многозадачность также устанавливается до этого, поэтому управлением доступа задач с помощью fork() и login занимается программа init. Данный обзор рассмотрит обслуживание ядром асинхронные события, также подробно как размещение информации и организацию кода.

В.3 Как ядро рассматривает процесс.

С точки зрения ядра процесс есть ни что иное, как запись в таблице процессов. Таблица процессов - одна из важнейших структур данных внутри системы совместно с таблицей распределения памяти и механизмом кэширования буфера. Особое место в таблице процессов занимает довольно об'емная структура task_structure, определенная в include/linux/sched.h. Внутри структуры task_struct содержится информация как высокого, так и низкого уровня - от некоторых регистров аппаратного обеспечения до inode работающей директории процесса.

Таблица процессов является одновременно массивом и двусвязным списком в виде дерева. Физическое описание представляет собой статический массив указателей, длина которого NR_TASKS, является константой, определенной в include/linux/tasks.h, так что размер структуры может быть переопределен лишь на определенной зарезервированной странице. Структура списка определена двумя указателями next_task и prev_task, а структура дерева общеизвестна и мы не будем на ней здесь останавливаться. Вы можете изменить величину NR_TASKS со 128, как установлено по умолчанию, однако вам придется перекомпилировать все исходные файлы измененные при этом. После загрузки ядро работает от имени какого-либо процесса, используя глобальную переменную current и указатель на структуру task_struct для запуска прцессов. current изменятся только планировщиком в kernel/sched.c.

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

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

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

Системные вызовы внутри ядра существуют как функции на языке Си и их имена начинаются с префикса "sys_". Системный вызов burnout, к примеру, содержится в ядре в качествефункции Sys_burnout().

Механизм обработки системных вызовов описан в главе 3 этой книги. Просмотр for_each_task и SET_LINKS в include/linux/shed.h может помочь вам в понимании структуры списка и дерева в таблице процессов.

B.4. Создание и удаление процесса.

Система unix создает процесс с помощью системного вызова fork(), удаление процесса может осуществляться с помощью exit() или с помощью передачи ядру сигнала. Описание этих функций в Linux расположено в kernel/fork.c и в kernel/exit.c.

Разветвление процессов устроено довольно просто, так как файл fork.c небольшой и хорошо читаемый. его главная задача - заполнение структуры данных нового процесса. Здесь представлены основные шаги процесса заполнения, исключая заполнение полей:

sys_fork() управляет дескрипторами и inode файлов.

В ядре версии 1.0 предлагается весьма несовершенная поддержка наследования и системный вызов fork() хорошо демонстрирует это.

Выход из программы осуществляется в системе изощренным методом, так как каждый родительский процесс получает информацию ото всех своих существующих наследников. Кроме того, процесс может завершиться при kill() (уничтожении) другого процесса (позаимствовано из UN*X). Файл exit.c содержит sys_kill(), различные версии sys_wait() и sys_exit().

Текст exit.c не описывается здесь - он неинтересен. Он оперирует большим количеством инструментов выхода из системы в рабочем состоянии.

Стандарт POSIX управляет сигналами.

B.5. Запуск программы.

После разделения (fork()) запускаются две одинаковые программы. Одна из них обычно запускает (exec()) другую. Системный вызов exec() должен создать двоичный образ запускаемого файла, загрузить и запустить его. Слово "загрузить" не обязательно означает "запись в память двоичный образ", так как Linux поддерживает загрузку по требуемым частям.

Описание вызова exec() в Linux поддерживает разные форматы двоичного кода. Это содержится в структуре linux_binfmt, которая устанавливает два указателя на функции: один - на функцию запускаемого кода, второй - на загрузку библиотеки, каждый двоичный формат может представлять и запускаемые файлы, и библиотеки.

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

Системы UN*X позволяют программисту работать с шестью различными версиями функции exec(). Одна из них может быть описана, как библиотечная функция.

Также ядро Linux подключает отдельно функцию execve(). Она исполняет достаточно простую задачу - чтение заголовка запускаемого файла и попытку запустить его. Если первые два байта "#!", делается грамматический разбор и включается интерпретатор, иначе последовательно применяются другие двоичные форматы.

Родной формат Linux поддерживается прямо внутри fs/exec.c вместе с соответствующими функциями load_aout_binary и load_aout_library.

Что касается двоичных кодов, функция, загружаемая как запускаемый файл a.out, заканчивает работу после mmap() дискового файла, или после вызова read_exec(). Формальный метод, используемый Linux, требует загружаемый механизм для обнаружения ошибок в программных страницах, когда к ним открывается доступ, тогда как более новый метод используется, когда в основной файловой системе не подчеркивается распределение памяти (к примеру, файловая система "msdos").

После версии 1.1 в ядра включались переделанные файловые системы msdos, поддерживающие mmap(). Более того, структура linux_binfmt как список для поддержки новых двоичных форматов в качестве модулей ядра. В итоге структура была расширена для доступа к подпрограммам конвертации форматов.

B.6. Доступные файловые системы.

Всем известно, что файловая система - основной ресурс системы UN*X, настолько основной и общей, что она должна иметь удобное сокращение имени. Далее в тексте будем называть файловую систему "фс".

Я предполагаю, что читатель уже знаком с с основными концепциями фс UNIX - разрешение доступа, inode, superblock, mounting и umounting. Эти концепции об'яснены в других книгах по UNIX, так что я не буду повторяться, а остановлюсь на особых компонентах Linux.

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

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

Весь материал, не зависящий от фс, хранится в файлах fs/*.c. Они выполняют следующие операции:

Интерфейс VFS содержит набор операций высокого уровня, запускаемых независимым от фс кодом, и представляется нужном формате для фс. Наиболее важными структурами являются file_operations и inode_operations, однако, они далеко не единственны. Все они описаны в include/linux/fs.h.

Отправной точкой в ядре в обращении к файловой системе является структура file_system_type. Массив file_system_types помещен в fs/filesystems.c, и во время запуска mount на него происходит ссылка. Затем функция read_super для соответствующего типа фс заполняет элемент структуры struct super_block,который, в свою очередь, заносит информацию в struct super_operations и в struct type_sb_info.

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

Массив типов файловой системы помещен в скомпанованный список для организации новых типов фс как модулей ядра. Функция, делающая это - (un-)register_filesystem, описана в fs/super.c.

B.7. Краткий обзор сущности типа файловой системы.

Роль типа файловой системы состоит в выполнении низкоуровневых задач, используемых для распределения высокоуровневых операций VFS на физических устройствах.

Интерфейс VFS достаточно универсален для поддержки обеих встроенных фс UN*X и более экзотичных, таких, как msdos и umsdos.

Каждая фс создается из следующих компонентов, принадлежащих ее собственным каталогам:

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

Глава про фс proc в этой книге содержит все подробности о низкоуровневом коде и интерфейсе VFS для этого типа фс. Исходный текст в fs/procfs достаточно доступен после прочтения этой главы.

Разберем внутреннее устройство механизма VFS и фс minix в качестве примера. Я выбрал в качестве примера minix, так как она небольшая, но полная, кроме того, все фс linux берут начало от minix. Читателю предлагается разобрать в качестве упражнения тип ext2, встречающийся в инсталляции Linux.

Во время поддержки системой фс minix minix_read_super заполняет структуру super_block информацией, полученной с поддерживаемого устройства. Поле s_op структуры содержит указатель на minix_sops, используемый основным кодом фс для быстрого выполнения операции суперблока.

Соединение новой поддерживаемой фс с системой основывается на изменении следующих компонент (помещение sb в super_block и dir_i в место обращения):

Umount происходит с помощью do_umount, включающим запуск minix_put_super. Когда разрешен доступ к файлу, minix_read_inode заполняет общую системную inode структуру полями из minix_inode. Поле inode->i_op заполняется, исходя из inode->i_mode и отвечает за все будующие операции над файлом.

Описание исходных текстов функций minix вы можете найти в fs/minix/inode.c. Структура inode_operations используется для засылки inode операций в специальные функции ядра; первая запись в структуре - указатель на file_operations, которая информационно эквивалентна i_op. Фс minix позволяет выбрать три образца наборов inode - операций (для каталогов, для файлов и для скомпанованных символов) и два образца установки file_operations.

Операции над каталогами (одна minix_readdir) могут быть найдены в fs/minix/dir.c, операции над файлами - в fs/minix/file.c, и операции над скомпанованными символами - в fs/minix/symlink.c.

Остальная часть каталога minix предназначена для следующих задач:

B.8. Пультовый драйвер.

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

Инициализация управления происходит с помощью функции tty_init() в tty_io.c. Эта функция предназначена для получения основных номеров устройств и вызова инициализации каждого установленного устройства. con_init() - одна из функций, относящихся к управляющему драйверу, инициализирующая его, находится в console.c.

Инициализация управляющего устройства сильно изменилась после выпуска версии 1.1, была убрана из tty_init() и вызывается прямо из../../main.c. Виртуальные пульты на данный момент динамически распределяемы, и в них изменена большая часть исходного текста.

B.8.1. Как файловые операции посылаются пульту.

Этот параграф довольно низкого уровня, и может быть исключена из прочитываемого. Доступ к устройству UN*X осуществляется через файловую систему. Этот параграф описывает все шаги файла устройства к функциям пульта. Кроме того, эта информация взята из исходных текстов версии 1.1.73, и может отличаться от исходников 1.0.

Когда открывается inode устройства, запускается функция chrdev_open() ( или blkdev_open(), мы будем рассматривать символьные устройства). Эта функция полна компонентами структуры def_chr_fops, на которую ссылаются chrdev_inode_operations, используемой всеми типами фс.

chrdev_open заботится о спецификации операций над устройством,помещая собственную таблицу file_operations в текущий flip и вызывая специфицированную open(). Специфицированные таблицы устройства содержатся в массиве chrdevs[], индексированном по основным номерам устройств и заполняемом тем же ../../fs/devices.c.

Если мы рассматриваем tty устройство (нужен ли нам в таком случае пульт ?), мы переходим к драйверам tty, чьи функции находятся в tty_io.c, индексированные tty_fops. tty_open() вызывает init_dev(), которая выделяет любую структуру данных, нужную устройству, базируемую на подномере устройства.

Подномер также используется для поиска фактического драйвера для устройства, который был зарегистрирован через tty_register_driver(). Драйвер в таком случае представляет собой иную структуру, используемую для определения подмодулей, таких, как file_ops; он напямую связан с записью и контролем над устройством. Последняя структура, используемая в управлении tty, это линейная дисциплина, описываемая позже.

Линейная дисциплина для пульта (или любого другого устройства tty) устанавливается с помощью функции initialize_tty_struct(), запускаемой init_dev.

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

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

B.8.2. Передача информации пульту.

Когда происходит запись в пультовое устройство, вызывается функция con_write(). Эта функция управляет всеми контрольными символами и esc-последовательностями, используемыми для поддержки приложений, связанных со всем управлением экрана. Эти esc-последовательности определены в коммуникационной подпрограмме vt102. Это означает, что вы должны установить TERM=vt102, когда вы хотите передать информацию не Linux-овскому host адаптеру, однако для локальных целей лучше устанавливать TEMP=console, так как пульт Linux позволяет оптимально установить vt102.

con_write() на большую часть состоит из вложенных установок переключателей, используемых для посимвольной интерпритации esc-последовательностей. В нормальном режиме, символ выводитсяя на экран, будучи записанным в видео память, используя определенные атрибуты. Внутри console.c, все поля структуры struct vc становятся доступными лишь через макросы, так что любая ссылка на attr (к примеру), на самом деле приходится на поле в структуре vc_cons[currcons], до тех пор пока система не перестает ссылаться на номер данного пульта.

В новых ядрах, vc_cons представляет собой массив указателей, содержание которых распределено в памяти kmalloc(). Использование макросов сильно упростило изменения, так как в переписывании нуждалась лишь небольшая часть кода.

Непосредственное распределение памяти пульта на экран осуществляется функциями set_scrmem() (копирование информации из буфера пульта в видео память% и get_scrmem() (копирование информации обратно в буфер). Личный буфер конкретного пульта физически расположен прямо в видео RAM, для уменьшения количества передач информации. Это означает, что функции get- и set- stream() являются static(статическими) для console.c и вызываются только во время переключения пульта.

B.8.3 Чтение из пульта.

Чтение из пульта устроено через линейную дисциплину. По умолчанию Linux пользуется линейной дисциплиной tty_ldisc_N_TTY. Линейная дисциплина - это метод разбора компонентов передаваемых в строке. Она является еще одной таблицей функций, которая работает при чтении устройства. С помощью флагов termios, линейная дисциплина контролирует ввод из tty в режимах raw,cbreak и cooked, а также работу функциий select(), ioctl() и подобных.

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

Самым низкоуровневым передатчиком информации пульту, является менеджер клавиатуры, описанный в keyboard.c, в функции keyboard_interrupt().

B.8.4 Управление клавиатурой.

Управление клавиатурой реализовано крайне необдуманно. В keyboard.c определены все десятичные значения различных кодов клавиатуры различных производителей.

В keyboard.c истинный хакер не найдет для себя никакой полезной информации.

B.8.5 Переключение пультов.

Текущий пульт переключается через запуск функции change_console(), которая переопределяет размер tty_io.c, осуществляемый либо keyboard.c, либо vt.c (Пользователь переключает их нажатием клавиши, программа вызовом ioctl())

Переключение происходит в два этапа, и функция complete_change_console() отвечает за второй. Разрыв переключения обусловлен окончанием работы задачи, с предварительным сообщением об этом процессу контролируемому покилаемую нами tty. Если пульт не подчиняется контролирующему процессу, функция change_console() вызывает complete_change_console() самостоятельно. Для смены графического пульта на текстовый нужен процесс конверции, при этом отдельный сервер может продолжать работать с графическим пультом.

B.8.6 Механизм выбора пульта.

"selection" - это устройство службы вырезания и копирования для текстовых пультов. Этот механизм поддерживается процессом пользовательского уровня который может быть запущен selection или gpm. Программа пользовательского уровня использует ioctl() в работе с пультом, для сообщения ядру точного места подсветки текста на экране. Затем выбранный текст помещается в буфер. Этот буфер статически определен в console.c. Копирование текста связывается с обычным помещением символов из буфера в входную очередь tty. Весь механизм выбора защищен #ifdef, так что пользователь может запретить его во время сохранения конфигурации ядра в несколько килобайт памяти.

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

B.8.7 Контроль над вводом-выводом устройства (ioctl()).

Системный вызов ioctl(), является отправной точкой пользовательских процессов, контролирующих поведение файла устройства. Управление передачи контроля находится в ../../fs/ioctl.c, где расположен sys_ioctl(). Стандартные запросы на передачу контроля удовлетворяются прямо здесь, иные запросы, связанные с файлами довлетворяются с помощью file_ioctl() (находится в том-же исходнике), до тех пор пока следующий запрос не обратится к особой функции ioctl() устройства.

Информация о контроле над пультовыми устройствами находится в vt.c, так как пультовое устройство удовлетворяет ioctl - запросы функцией vt_ioctl().

Вышеописанная информация взята из версии 1.1.7x. Ядро 1.0 не имела таблицы драйверов, и vt_ioctl() находился прямо втаблице file_operations().

В серии 1.1.7x обозначены следующие вещи: tty_ioctl.c описывает только запросы на линейные дисциплины (за исключением функции n_tty_ioctl(), являющейся единственной функцией n_tty вне n_tty.c), в то время как поле file_operations указывает на tty_ioctl() в tty_io.c. Если номер запроса не не определяется в tty_ioctl(), он передается в tty->driver.ioctl или в случае провала в tty->ldisc.ioctl.

Материал по линейным дисциплинам находится в tty_ioctl.c, в то время как информация о пультовых драйверах в vt.c.

В Ядре 1.0, tty_ioctl() находится в tty_ioctl.c и указывает на него общая file_operations. Нераспознанные запросы проходят через определенный контроль или через код линейной дисциплины похожий на версию 1.1.7x.

Помните что в обоих случаях запрос TIOCLINUX не зависит от устройства. Это говорит о том, что выбор пульта может быть установлен ioctlом любого tty.

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

Назад | Содержание