Добавлен: 29.10.2018
Просмотров: 48094
Скачиваний: 190
10.2. Обзор системы Linux
801
Рассмотрим несколько примеров этих утилит, начиная с программ для управления
файлами и каталогами. Команда
cp a b
копирует файл
a
в
b
, не изменяя исходный файл. Команда
mv a b
напротив, копирует файл
a
в
b
, но удаляет исходный файл. В результате она не копи-
рует файл, а перемещает его. Несколько файлов можно сцепить в один при помощи
команды cat, считывающей все входные файлы и копирующей их один за другим
в стандартный выходной поток. Удалить файлы можно командой rm. Команда chmod
позволяет владельцу изменить права доступа к файлу. Каталоги можно создать коман-
дой mkdir и удалить командой rmdir. Список файлов можно увидеть при помощи
команды ls. У этой команды множество флагов, управляющих видом формируемого
ею листинга. При помощи одних флагов можно задать, насколько подробно будет
отображаться каждый файл (размер, владелец, группа, дата создания), другими фла-
гами задается порядок, в котором перечисляются файлы (по алфавиту, по времени
последнего изменения, в обратном порядке), третья группа флагов позволяет задать
расположение списка файлов на экране и т. д.
Мы уже рассматривали несколько фильтров: команда grep извлекает из стандартного
входного потока (или из одного или нескольких файлов) строки, содержащие задан-
ную последовательность символов; команда sort сортирует входной поток и выводит
данные в стандартный выходной поток; команда head извлекает первые несколько
строк; команда tail, напротив, выдает на выход указанное количество последних строк.
Кроме того, стандартом 1003.2 определены такие фильтры, как cut и paste, которые
позволяют вырезать из файлов и вставлять в файлы куски текста, команда od конвер-
тирует (обычно двоичный) вход в ASCII-строку (в восьмеричный, десятичный или
шестнадцатеричный формат), команда tr преобразует символы (например, из нижнего
регистра в верхний), а команда pr форматирует выход для вывода на принтер, позволяя
добавлять заголовки, номера страниц и т. д.
Компиляторы и программные средства включают в себя компилятор gcc с языка C
и программу ar, собирающую библиотечные процедуры в архивные файлы.
Еще одним важным инструментом является команда make, используемая для сборки
больших программ, исходный текст которых состоит из множества файлов. Как пра-
вило, некоторые из этих файлов представляют собой заголовочные файлы (header
files) , содержащие определения типов, переменных, макросов и прочие декларации.
Исходные файлы обычно ссылаются на эти файлы с помощью специальной директивы
include. Таким образом, два и более исходных файла могут совместно использовать одни
и те же декларации. Однако если файл заголовков изменен, то необходимо найти все
исходные файлы, зависящие от него, и перекомпилировать их. Задача команды make
заключается в том, чтобы отслеживать, какой файл от какого заголовочного файла
зависит, и автоматически запускать компилятор для тех файлов, которые требуется
перекомпилировать. Почти все программы в системе Linux, кроме самых маленьких,
компилируются с помощью команды make.
Часть команд POSIX перечислена в табл. 10.1 вместе с кратким описанием. Во всех
версиях операционной системы Linux есть эти программы, а также многие другие.
802
Глава 10. Изучение конкретных примеров: Unix, Linux и Android
Таблица 10.1. Некоторые утилиты Linux, требуемые стандартом POSIX
Программа
Функция
cat
Конкатенация нескольких файлов в стандартный выходной поток
chmod
Изменение режима защиты файла
cp
Копирование файлов
cut
Вырезание колонок текста из файла
grep
Поиск определенного шаблона в файле
head
Извлечение из файла первых строк
ls
Распечатка каталога
make
Компиляция файлов для создания двоичного файла
mkdir
Создание каталога
od
Восьмеричный дамп файла
paste
Вставка колонок текста в файл
pr
Форматирование файла для печати
rm
Удаление файлов
rmdir
Удаление каталога
sort
Сортировка строк файла по алфавиту
tail
Извлечение из файла последних строк
tr
Преобразование символов из одного набора в другой
10.2.5. Структура ядра
На рис. 10.1 была показана общая структура системы Linux . Давайте теперь подробнее
рассмотрим ядро системы (перед тем как начать изучение планирования процессов
и файловой системы).
Ядро работает непосредственно с аппаратным обеспечением и обеспечивает взаимодей-
ствие с устройствами ввода-вывода и блоком управления памятью, а также управляет
доступом процессора к ним. Нижний уровень ядра (рис. 10.2) состоит из обработчиков
прерываний (которые являются основным средством взаимодействия с устройствами)
и механизма диспетчеризации на низком уровне. Диспетчеризация производится при
возникновении прерывания. При этом код низкого уровня останавливает выполнение
работающего процесса, сохраняет его состояние в структурах процессов ядра и запуска-
ет соответствующий драйвер. Диспетчеризация процессов производится также тогда,
когда ядро завершает некую операцию и пора снова запустить процесс пользователя.
Код диспетчеризации написан на ассемблере и представляет собой отдельную от про-
цедуры планирования программу.
Затем мы разделили различные подсистемы ядра на три основных компонента. Ком-
понент ввода-вывода на рис. 10.2 содержит все те части ядра, которые отвечают за
взаимодействие с устройствами, а также выполнение сетевых операций и операций
ввода-вывода на внешние устройства. На самом высоком уровне все операции ввода-
вывода интегрированы в уровень виртуальной файловой системы (Virtual File System
(VFS)). То есть на самом верхнем уровне выполнение операции чтения из файла (будь
он в памяти или на диске) — это то же самое, что и выполнение операции чтения симво-
ла с терминального ввода. На самом низком уровне все операции ввода-вывода прохо-
дят через какой-то драйвер устройства. Все драйверы в Linux классифицируются либо
10.2. Обзор системы Linux
803
Рис. 10.2. Структура ядра операционной системы Linux
как символьные драйверы устройств, либо как блочные драйверы устройств, причем
основная разница состоит в том, что поиск и произвольный доступ разрешены только
для блочных устройств. В техническом смысле сетевые устройства — это символьные
устройства, но работа с ними ведется несколько иначе, так что лучше их выделить (что
и было сделано на рисунке).
Выше уровня драйверов устройств код ядра для каждого типа устройств свой. Сим-
вольные устройства могут использоваться двумя разными способами. Некоторым
программам, таким как экранные редакторы vi и emacs, требуется каждая нажатая
клавиша без какой-либо обработки. Для этого служит необработанный ввод-вывод
с терминала (tty). Другое программное обеспечение (такое, как оболочки) принимает
на входе уже готовую текстовую строку, позволяя пользователю редактировать ее, пока
не будет нажата клавиша
Enter
для отправки строки в программу. В этом случае поток
символов с устройства терминала передается через так называемую дисциплину линии
связи (и применяется соответствующее форматирование).
Сетевое программное обеспечение часто бывает модульным, с поддержкой множества
различных устройств и протоколов. Уровень выше сетевых драйверов выполняет своего
рода функции маршрутизации, обеспечивая отправку правильного пакета к правиль-
ному устройству или блоку обработки протокола. Большинство систем Linux содержат
в своем ядре полнофункциональный эквивалент аппаратного маршрутизатора (однако
его производительность ниже, чем у аппаратного маршрутизатора). Над кодом маршру-
тизации располагается стек протоколов, который всегда включает протоколы IP и TCP,
а также много других дополнительных протоколов. Над сетью располагается интерфейс
сокетов, позволяющий программам создавать сокеты (для сетей и протоколов). Для
последующего использования сокета возвращается дескриптор файла.
804
Глава 10. Изучение конкретных примеров: Unix, Linux и Android
Над дисковыми драйверами располагается планировщик ввода-вывода, который от-
вечает за упорядочивание и выдачу запросов на дисковые операции таким способом,
который экономит излишние перемещения головок (или реализует некую иную си-
стемную стратегию).
На самом верху столбца блочных устройств располагаются файловые системы. Linux
имеет несколько одновременно сосуществующих файловых систем. Для того чтобы
скрыть «страшные» архитектурные отличия аппаратных устройств от реализации фай-
ловой системы, уровень обобщенных блочных устройств обеспечивает используемую
всеми файловыми системами абстракцию.
Справа на рис. 10.2 находятся два других ключевых компонента ядра Linux. Они от-
вечают за задачи управления памятью и процессором. В задачи управления памятью
входят обслуживание отображения виртуальной памяти на физическую, поддержка
кэша страниц, к которым недавно выполнялось обращение (и хорошая стратегия за-
мены страниц), поставка в память (по требованию) новых страниц с кодом и данными.
Основная область ответственности компонента управления процессами — это созда-
ние и завершение процессов. В нем имеется также планировщик процессов, который
выбирает, какой процесс (вернее, поток) будет работать дальше. Как мы увидим в сле-
дующем разделе, ядро Linux работает с процессами и потоками как с исполняемыми
модулями и планирует их на основе глобальной стратегии планирования. Код обра-
ботки сигналов также принадлежит этому компоненту.
Несмотря на то что эти три компонента представлены на рисунке отдельно, они сильно
зависят друг от друга. Файловые системы обычно обращаются к файлам через блоч-
ные устройства. Однако для скрытия большой латентности дискового доступа файлы
копируются в кэш страниц (находящийся в оперативной памяти). Некоторые файлы
(такие, как файлы с информацией об использовании ресурсов времени выполнения)
могут даже создаваться динамически и иметь представление только в оперативной
памяти. Кроме того, система виртуальной памяти может использовать дисковый раз-
дел или область подкачки в файле (для сохранения части оперативной памяти, когда
ей нужно освободить определенные страницы), и поэтому она использует компонент
ввода-вывода. Существует и множество других взаимозависимостей.
Помимо статических компонентов ядра Linux поддерживает и динамически загружае-
мые модули. Эти модули могут использоваться для добавления или замены драйверов
устройств по умолчанию, файловых систем, сетевой работы, а также прочих кодов ядра.
Эти модули на рис. 10.2 не показаны.
Самый верхний уровень — это интерфейс системных вызовов ядра. Все системные
вызовы поступают сюда и вызывают эмулированное прерывание, которое переклю-
чает исполнение из пользовательского режима в защищенный режим ядра и передает
управление одному из описанных ранее компонентов ядра.
10.3. Процессы в системе Linux
В предыдущих разделах мы начали наш обзор системы Linux с точки зрения пользо-
вателя, сидящего за клавиатурой (то есть с того, что видит пользователь в окне
xterm
).
Были приведены примеры часто использующихся команд оболочки и утилит. Этот
краткий обзор был завершен рассмотрением структуры системы. Теперь настало время
10.3. Процессы в системе Linux
805
углубиться в ядро и более пристально рассмотреть основные концепции, поддержи-
ваемые системой Linux, а именно: процессы, память, файловую систему и ввод-вывод.
Эти понятия важны, так как ими управляют системные вызовы — интерфейс самой
операционной системы. Например, существуют системные вызовы для создания про-
цессов и потоков, выделения памяти, открытия файлов и выполнения ввода-вывода.
К сожалению, существует очень много версий системы Linux, и между ними имеются
определенные различия. В данной главе основное внимание будет уделено общим
свойствам всех версий, а не особенностям какой-либо одной версии. Таким образом,
в определенных разделах (особенно в тех, где будет рассматриваться вопрос реали-
зации) может оказаться, что описание не соответствует в равной мере всем версиям.
10.3.1. Фундаментальные концепции
Основными активными сущностями в системе Linux являются процессы. Процессы
Linux очень похожи на классические последовательные процессы, которые мы изу-
чали в главе 2. Каждый процесс выполняет одну программу и изначально получает
один поток управления. Иначе говоря, у процесса есть один счетчик команд, который
отслеживает следующую исполняемую команду. Linux позволяет процессу создавать
дополнительные потоки (после того, как он начинает выполнение).
Linux представляет собой многозадачную систему и несколько независимых процессов
могут работать одновременно. Более того, у каждого пользователя может быть одновре-
менно несколько активных процессов, так что в большой системе могут одновременно
работать сотни и даже тысячи процессов. Фактически на большинстве однопользова-
тельских рабочих станций (даже когда пользователь куда-либо отлучился) работают
десятки фоновых процессов, называемых демонами (daemons). Они запускаются при
загрузке системы из сценария оболочки.
Типичным демоном является cron. Он просыпается раз в минуту, проверяя, не нужно
ли ему что-то сделать. Если у него есть работа, он ее выполняет, а затем отправляется
спать дальше (до следующей проверки).
Этот демон позволяет планировать в системе Linux активность на минуты, часы, дни
и даже месяцы вперед. Например, представьте, что пользователю назначено явиться
к зубному врачу в 15.00 в следующий вторник. Он может создать запись в базе данных
демона cron, чтобы тот просигналил ему, скажем, в 14.30. Когда наступают назначенные
день и время, демон cron видит, что у него есть работа, и в нужное время запускает про-
грамму звукового сигнала (в виде нового процесса).
Демон cron также используется для периодического запуска задач, например ежеднев-
ного резервного копирования диска в 4.00 или напоминания забывчивым пользователям
каждый год 31 октября купить новые «страшненькие» товары для веселого празднования
Хэллоуина. Другие демоны управляют входящей и исходящей электронной почтой,
очередями принтера, проверяют, достаточно ли еще осталось свободных страниц па-
мяти, и т. д. Демоны реализуются в системе Linux довольно просто, так как каждый из
них представляет собой отдельный процесс, не зависимый от всех остальных процессов.
Процессы создаются в операционной системе Linux чрезвычайно просто. Системный
вызов fork создает точную копию исходного процесса, называемого родительским
процессом
(parent process) . Новый процесс называется дочерним процессом (child
process) . У родительского и у дочернего процессов есть собственные (приватные) об-