10.2 Загрузка и выполнение программ |
Предыдущая Содержание Следующая |
Стандартные приложения Linux связаны (слинкованы) с абсолютными адресами. Другими словами, компилятор и компоновщик собирают приложения с предположением, что каждому приложению доступен весь диапазон виртуальных адресов памяти. Мы напишем небольшую программу для x86, чтобы получить представление о том, как организованы различные сегменты в доступном адресном пространстве. (* Каждое приложение на x86 способно адресовать диапазон из 4 Гб виртуальных адресов. 1 Гб этого пространства отведено для пространства ядра, а остальные 3 Гб доступны для приложений пользовательского пространства.)
int data1=1; // будет располагаться в секции .data int data2=2; // также будет располагаться в секции .data int main(int argc, char *argv[]) // .text { int stack1=1; // программный стек int stack2=2; // программный стек char *heap1 = malloc(0x100); // выделение памяти в "куче" char *heap2 = malloc(0x100); // также выделение памяти в "куче"
printf(“ text %p\n”, main); printf(“ data %p %p\n”, &data1, &data2); printf(“ heap %p %p\n”, heap1, heap2); printf(“ stack %p %p\n”, &stack1, &stack2); }
Вывод программы:
text 0x804835c data 0x80494f0 0x80494f4 heap 0x8049648 0x8049750 stack 0xbfffe514 0xbfffe510
Вывод программы делает ясным, где находится каждый раздел, и в каком направлении растёт каждый сегмент. Стек расположен около верхней части PAGE_OFFSET (0xC000_0000) и растёт вниз. Текст находится ближе к нижней части памяти (библиотеки имеют некоторые области, зарезервированные ещё ниже), следом идёт раздел данных. После окончания данных (то есть инициализированные данные + bss) начинается распределение "кучи", растущее вверх, в направлении растущего стека. Что происходит, когда "куча" и стек встречаются? В этом случае обработчик ошибки страницы посылает в программу сигнал SIGSEGV. Рисунок 10.1 показывает стандартную карту памяти приложения Linux.
Рисунок 10.1 Карта памяти приложения для Linux.
Запустите несколько экземпляров программы и вывод будет в том же диапазоне для всех экземпляров приложения. Это становится возможным из-за MMU, который помогает в предоставлении отдельного виртуального адресного пространства для каждого процесса. Приложение имеет дело только с виртуальным адресом. Программное обеспечение виртуальной памяти в ядре и оборудование MMU связывают виртуальный адрес с реальным физическим адресом. Теперь же из-за отсутствия VM не может быть создано отдельное виртуальное адресное пространство для каждой программы. Поэтому в uClinux приложения вынуждены использовать совместно всё свободное адресное пространство как один большой непрерывный физический кусок. Программы загружаются в доступную свободную часть памяти, в любом произвольном месте памяти. Напомним, что на системах с MMU это произвольное расположение будет связано с нулевым виртуальным адресом в карте памяти процесса. В отличие от стандартного Linux это означает, что начальный адрес программы неизвестен (а не произвольный адрес) и адресация, используемая в инструкциях, не может быть абсолютной. (* Эта проблема называется проблемой неизвестного адреса.) Загрузчики uClinux занимаются этой дополнительной работой по модификации программы при запуске на основе доступного начального адреса. Компиляторам и компоновщикам также необходимо взаимодействовать с загрузчиком для оказания помощи в этой работе. uClinux имеет два различных метода для решения этой проблемы неизвестного адреса.
|
Предыдущая Содержание Следующая |