Организация и распределение памяти, связанные с процессом FreeBSD, показаны на рис. Каждый процесс начинает выполнение с тремя сегментами памяти: кода (text), данных и стека. Сегмент данных делится на инициализированные данные и неинициализированные данные (известные также как BSS). Сегмент text является сегментом только для чтения и обычно разделяется между всеми процессами, выполняющими файл, тогда как в области данных и стека может быть осуществлена запись и они индивидуальны для каждого процесса. Сегмент text и инициализированные данные для процесса считываются из исполняемого файла.
Исполняемый файл отличается тем, что это обычный файл (а не каталог, специальный файл или символическая ссылка) и для него установлены один или более битов с разрешением доступа на исполнение. У каждого исполняемого файла есть заголовок exec, содержащий магическое число, обозначающее тип исполняемого файла. FreeBSD поддерживает несколько исполняемых форматов, включая следующие.
1. Файлы, которые должны читаться интерпретатором.
2. Файлы, которые могут исполняться непосредственно, включая AOUT, ELF и сжатый gzip ELF.
Исполняемый файл вначале анализируется структурой активизации образа (imgact). Заголовок файла, который должен быть выполнен, передается по списку зарегистрированных активаторов образа для обнаружения подходящего формата. Когда подходящий формат найден, соответствующий активатор образа подготавливает файл к выполнению.
У файлов, попадающих в первую категорию, магическое число (занимающее 2 первых байта файла) представляет собой последовательность из двух символов «#!», за которыми следует имя пути к интерпретатору, который должен быть использован. Это имя пути в настоящее время ограничено константой времени компиляции в 128 символов. Например, #!/bin/sh ссылается на оболочку Борна. Активатор образа, который будет выбран, обрабатывает вызов интерпретаторов. Он загрузит и запустит указанный интерпретатор, передав в качестве аргумента имя файла, который должен быть интерпретирован. Для предотвращения зацикливаний FreeBSD допускает лишь один уровень интерпретации, и интерпретатор файла не может сам интерпретироваться.
По соображениям производительности большинство файлов попадают во вторую категорию и являются непосредственно выполняемыми. Информация в заголовке непосредственно выполняемого файла включает архитектуру и операционную систему, для которой исполняемый файл был создан, а также указывает, является ли он статически скомпонованным или использующим разделяемые библиотеки. Выбранный активатор образа может использовать такую информацию, как знание операционной системы, для которой исполняемый файл был откомпилирован, чтобы отконфигурировать ядро для использования соответствующей интерпретации системных вызовов при работе программы. Например, исполняемый файл, построенный для запуска под Linux, может безболезненно работать под FreeBSD с использованием вектора перенаправления системных вызовов, предоставляющим эмуляцию системных вызовов Linux.
В заголовке указаны также размеры сегментов text, инициализированных данных, неинициализированных данных и дополнительные сведения для отладки. Отладочная информация не используется ядром или выполняющейся программой. За заголовком следует образ сегмента text, за которым идет образ инициализированных данных. Неинициализированные данные не содержатся в исполняемом файле, поскольку они могут создаваться по требованию с использованием заполненной нулями памяти.
Чтобы начать выполнение, ядро отображает кодовую часть файла в нижнюю часть адресного пространства, начиная со второй страницы виртуального адресного пространства. Первая страница виртуального адресного пространства помечается как недействительная, чтобы попытки читать или записывать с использованием пустого указателя вызывали ошибку. Часть файла с инициализированными данными отображается в адресное пространство сразу за кодом. После области инициализированных данных создается область с размером неинициализированных данных из заполненной нулями памяти. Стек также создается из заполненной нулями памяти. Хотя стек не обязательно должен быть заполнен нулями, старые системы UNIX делали именно так. В попытке несколько сократить время запуска в 4.2BSD разработчики изменили ядро таким образом, чтобы не обнулять стек, оставляя там предыдущее случайное содержимое страницы. Однако беспокойство по поводу тайных злоупотреблений данными ранее запущенных программ и невоспроизводимые ошибки ранее работавших программ привели к восстановлению обнуления стека ко времени выпуска 4.3BSD.
Копирование в память полных областей кода и инициализированных данных больших программ вызывает большую начальную задержку. FreeBSD избегает этого, используя для программы страничную подкачку по требованию вместо ее предварительной загрузки. При страничной подкачке по требованию программа загружается небольшими частями (страницами) по мере необходимости, а не вся сразу до начала выполнения. Система осуществляет страничную подкачку по требованию, разделяя адресное пространство на области равного размера, называемые страницами. Для каждой страницы ядро записывает смещение в исполняемом файле для соответствующих данных. Первая попытка доступа по адресу на каждой из страниц вызывает в ядре исключение отказа страницы. Обработчик отказа страницы считывает в память процесса нужную страницу исполняемого файла. Таким образом, ядро загружает лишь те части исполняемого файла, которые нужны. В главе 5 объясняются подробности страничной подкачки.
Может показаться более эффективным загружать сразу весь процесс, чем множество небольших частей. Однако большинство процессов используют менее половины своего адресного пространства в течение всей продолжительности своего жизненного цикла. Причина такого незначительного использования в том, что у типичных команд пользователя множество опций, лишь немногие из которых используются при каждом конкретном запуске. Код и структуры данных, поддерживающие неиспользующиеся опции, не нужны. Таким образом, цена загрузки подмножества использующихся страниц ниже, чем цена первоначальной загрузки всего процесса. В дополнение ко времени, сэкономленному за счет избежания загрузки всего процесса, подкачка по требованию снижает также количество физической памяти, необходимой для запуска процесса.
Область неинициализированных данных может быть расширена заполненными нулями страницами с помощью системного вызова sbrk, хотя большинство пользовательских процессов используют библиотечную функцию malloc(), более дружественный для программиста интерфейс к sbrk. Эта выделенная память, которая растет от верхушки первоначального сегмента данных, называется кучей. На PC стек растет от верхушки памяти вниз, тогда как куча растет снизу памяти вверх.
Выше стека пользователя находятся области памяти, которые создаются системой при запуске процесса. Непосредственно над стеком пользователя находится число аргументов (argc), массив указателей (вектор) аргументов (argv) и массив указателей (вектор) переменных окружения процесса (envp), устанавливаемых при выполнении программы. За ними следуют сами аргументы и строки переменных окружения. Далее находится код сигнала, использующийся при доставке системой процессу сигналов. На верхушке находится структура psstrings, использующаяся ps для нахождения argv процесса.
Изначально большинство исполняемых файлов компоновались статически. В статически скомпонованном двоичном файле все библиотечные процедуры и заглушки для системных вызовов загружались в двоичный файл во время компиляции. В настоящее время большинство двоичных файлов используют динамическое связывание. Двоичный файл с динамическим связыванием содержит лишь код откомпилированного приложения и список нужных процедур (библиотечных и заглушек системных вызовов). Когда исполняемый файл запускается, набор разделяемых библиотек, содержащих процедуры, которые нужно использовать, отображается в его адресное пространство в ходе процесса загрузки. При первом вызове процедуры последняя находится в разделяемой библиотеке, и образуется динамическая связь с ней.
Когда динамический загрузчик выполняет системный вызов mmap для выделения памяти для разделяемых библиотек, ядро должно найти место в адресном пространстве процесса, чтобы их разместить. В FreeBSD принято размещать их непосредственно под нижним административным ограничением для стека. Поскольку стек не может расти вниз ниже установленного административного предела, нет опасности перезаписывания разделяемых библиотек. Побочным эффектом такой реализации является то, что после запуска двоичного файла нельзя безопасно изменить ограничение размера стека. В идеале до запуска приложения другим процессом (таким, как оболочка) может быть установлен больший размер стека. Однако приложения, которые на момент запуска знают о том, что им потребуется стек большего размера, могут увеличить размер ограничения своего стека, а затем выполнить для себя системный вызов exec, чтобы перезапуститься с разделяемыми библиотеками, перемещенными ниже нового нижнего предела стека.
В качестве альтернативы можно было бы расположить разделяемые библиотеки непосредственно над верхним пределом кучи. Однако это означало бы, что после запуска двоичного файла нельзя было бы увеличить предельный размер кучи. Поскольку приложениям значительно чаще приходится увеличивать размер своей кучи, чем размер стека, ограничение размера стека было выбрано в качестве подходящего места для размещения разделяемых библиотек.
Процессу требуется использовать некоторые глобальные системные ресурсы. Ядро поддерживает связанный список процессов, в котором имеется элемент для каждого процесса в системе. Помимо других данных в элементах процессов записывается информация о планировании и распределении виртуальной памяти. Поскольку из основной памяти при подкачке на диск может быть сброшено все адресное пространство процесса, включая стек ядра для процесса, в элементе для процесса должно быть записано достаточно информации для обнаружения процесса и закачки его обратно в память. Вдобавок информация, необходимая для сброшенного на диск процесса (т. е. информация о планировании) должна содержаться в элементе списка для процесса, а не в структуре пользователя, чтобы избежать подкачки процесса ядром лишь для выяснения того, что у процесса недостаточно высокий приоритет для запуска.
Другие глобальные ресурсы, связанные с процессом, включают память для записи сведений о дескрипторах и таблицах страниц, в которых содержится информация об использовании физической памяти.
- 31/07/2010 18:35 - Идентификаторы пользователя, группы и другие идентификаторы
- 24/07/2010 07:07 - Интервальное время
- 24/07/2010 02:45 - Корректировка времени
- 22/07/2010 16:24 - Внешнее представление
- 15/07/2010 22:02 - Реальное время
- 04/07/2010 23:01 - Тайм-ауты
- 04/07/2010 21:47 - Статистика и планирование процессов
- 02/07/2010 05:18 - Прерывания от часов
- 26/06/2010 01:52 - Программные прерывания
- 24/06/2010 12:26 - Прерывания от устройств ввода/вывода