Я знаю общую теорию, но не могу вдаваться в детали. Я знаю, что программа находится во вторичной памяти компьютера. Как только программа начинает выполнение, она полностью копируется в ОЗУ. Затем процессор извлекает несколько инструкций (это зависит от размера шины) за раз, помещает их в регистры и выполняет их.
Я также знаю, что компьютерная программа использует два типа памяти: стек и кучу, которые также являются частью первичной памяти компьютера. Стек используется для нединамической памяти, а куча — для динамической памяти (например, все, что связано с new оператор в C ++) Я не могу понять, как эти две вещи связаны. В какой момент стек используется для выполнения инструкций? Инструкции идут из ОЗУ, в стек, в регистры?
задан 01 марта ’11, 22:03
+1 за фундаментальный вопрос! — mkelley33
хм . знаете, об этом пишут книги. Вы действительно хотите изучить эту часть архитектуры ОС с помощью SO? — Andrey
Я добавил пару тегов на основе характера вопроса, связанного с памятью, и ссылки на C ++, хотя я думаю, что хороший ответ также может прийти от кого-то, кто разбирается в Java или C #!) — mkelley33
Ничего не происходит при запуске exe файла на Windows 10
Проголосовали и добавили в избранное. Я всегда боялся спросить . — Maxpm
Термин «помещает их в реестры» не совсем правильный. На большинстве процессоров регистры используются для хранения промежуточных значений, а не исполняемого кода. — user122299
4 ответы
Это действительно зависит от системы, но современные ОС с виртуальная память обычно загружают свои образы процессов и выделяют память примерно так:
+———+ | stack | function-local variables, return addresses, return values, etc. | | often grows downward, commonly accessed via «push» and «pop» (but can be | | accessed randomly, as well; disassemble a program to see) +———+ | shared | mapped shared libraries (C libraries, math libs, etc.) | libs | +———+ | hole | unused memory allocated between the heap and stack «chunks», spans the | | difference between your max and min memory, minus the other totals +———+ | heap | dynamic, random-access storage, allocated with ‘malloc’ and the like. +———+ | bss | Uninitialized global variables; must be in read-write memory area +———+ | data | data segment, for globals and static variables that are initialized | | (can further be split up into read-only and read-write areas, with | | read-only areas being stored elsewhere in ROM on some systems) +———+ | text | program code, this is the actual executable code that is running. +———+
Это общее адресное пространство процесса во многих распространенных системах виртуальной памяти. «Дыра» — это размер вашей общей памяти за вычетом места, занимаемого всеми другими областями; это дает большое пространство для роста кучи.
Это также «виртуальный», то есть соответствует вашему представить память через таблицу трансляции и может фактически храниться в любом месте реальной памяти. Это делается таким образом, чтобы защитить один процесс от доступа к памяти другого процесса и заставить каждый процесс думать, что он работает в полной системе. Обратите внимание, что позиции, например, стека и кучи могут быть в другом порядке в некоторых системах (см. Билли О’Нил ответ ниже для более подробной информации о Win32). Другие системы могут быть очень другой. DOS, например, запускалась в реальный режим, и его распределение памяти при запуске программ выглядело иначе:
Прекращена работа программы при запуске программы или игры
+————+ top of memory | extended | above the high memory area, and up to your total memory; needed drivers to | | be able to access it. +————+ 0x110000 | high | just over 1MB->1MB+64KB, used by 286s and above. +————+ 0x100000 | upper | upper memory area, from 640kb->1MB, had mapped memory for video devices, the | | DOS «transient» area, etc. some was often free, and could be used for drivers +————+ 0xA0000 | USER PROC | user process address space, from the end of DOS up to 640KB +————+ |command.com| DOS command interpreter +————+ | DOS | DOS permanent area, kept as small as possible, provided routines for display, | kernel | *basic* hardware access, etc. +————+ 0x600 | BIOS data | BIOS data area, contained simple hardware descriptions, etc. +————+ 0x400 | interrupt | the interrupt vector table, starting from 0 and going to 1k, contained | vector | the addresses of routines called when interrupts occurred. e.g. | table | interrupt 0x21 checked the address at 0x21*4 and far-jumped to that | | location to service the interrupt. +————+ 0x0
Вы можете видеть, что DOS разрешал прямой доступ к памяти операционной системы без защиты, а это означало, что программы пользовательского пространства обычно могли напрямую обращаться или перезаписывать все, что им нравилось.
Однако в адресном пространстве процесса программы имели тенденцию выглядеть одинаково, только они были описаны как сегмент кода, сегмент данных, куча, сегмент стека и т. Д., И отображались несколько иначе. Но большая часть общих областей все еще была там. После загрузки программы и необходимых общих библиотек в память и распределения частей программы по нужным областям ОС начинает выполнять ваш процесс там, где находится его основной метод, и ваша программа берет на себя ответственность оттуда, делая системные вызовы по мере необходимости, когда они ему нужны. Различные системы (встроенные, любые) могут иметь очень разные архитектуры, такие как системы без стека, системы с архитектурой Гарварда (с кодом и данными, хранящимися в отдельной физической памяти), системы, которые фактически хранят BSS в постоянной памяти (изначально заданной программист) и т. д. Но это общая суть. Ты сказал:
Я также знаю, что компьютерная программа использует два типа памяти: стек и кучу, которые также являются частью первичной памяти компьютера.
«Стек» и «куча» — это просто абстрактные понятия, а не (обязательно) физически различные «виды» памяти. A стек это просто структура данных «последний вошел — первым ушел». В архитектуре x86 к нему можно обращаться случайным образом, используя смещение от конца, но наиболее распространенными функциями являются PUSH и POP для добавления и удаления элементов, соответственно.
Он обычно используется для локальных переменных функции (так называемое «автоматическое хранение»), аргументов функций, адресов возврата и т. Д. (Подробнее ниже) A «куча» это просто псевдоним для фрагмента памяти, который может быть выделен по запросу и адресован случайным образом (то есть вы можете получить доступ к любому месту в нем напрямую). Обычно он используется для структур данных, которые вы выделяете во время выполнения (в C ++, используя new и delete , и malloc и друзья в C и т. д.).
Стек и куча на архитектуре x86 физически находятся в вашей системной памяти (RAM) и отображаются посредством выделения виртуальной памяти в адресное пространство процесса, как описано выше. Наблюдения и советы этой статьи мы подготовили на основании опыта команды регистры (все еще на x86), физически находятся внутри процессора (в отличие от ОЗУ) и загружаются процессором из области ТЕКСТ (а также могут быть загружены из другого места в памяти или других местах в зависимости от инструкций ЦП, которые на самом деле выполнено). По сути, это очень маленькие и очень быстрые ячейки памяти на кристалле, которые используются для различных целей. Макет регистров сильно зависит от архитектуры (фактически, регистры, набор команд и макет / дизайн памяти — это именно то, что подразумевается под «архитектурой»), поэтому я не буду вдаваться в подробности, но рекомендую вам воспользоваться курс ассемблера, чтобы лучше их понять. Ваш вопрос:
В какой момент стек используется для выполнения инструкций? Инструкции идут из ОЗУ, в стек, в регистры?
Стек (в системах / языках, которые их имеют и используют) чаще всего используется следующим образом:
int mul( int x, int y ) < return x * y; // this stores the result of MULtiplying the two variables // from the stack into the return value address previously // allocated, then issues a RET, which resets the stack frame // based on the arg list, and returns to the address set by // the CALLer. >int main() < int x = 2, y = 3; // these variables are stored on the stack mul( x, y ); // this pushes y onto the stack, then x, then a return address, // allocates space on the stack for a return value, // then issues an assembly CALL instruction. >
Напишите такую простую программу, а затем скомпилируйте ее в сборку ( gcc -S foo.c если у вас есть доступ к GCC) и посмотрите. Сборка довольно проста. Вы можете видеть, что стек используется для локальных переменных функции и для вызова функций, хранения их аргументов и возвращаемых значений. Вот почему, когда вы делаете что-то вроде:
f( g( h( i ) ) );
Все они вызываются по очереди. Он буквально создает стек вызовов функций и их аргументов, выполняет их, а затем выталкивает их, когда он возвращается вниз (или вверх;).
Однако, как упоминалось выше, стек (на x86) фактически находится в пространстве памяти вашего процесса (в виртуальной памяти), поэтому им можно управлять напрямую; это не отдельный шаг во время выполнения (или, по крайней мере, ортогонален процессу). К вашему сведению, это Соглашение о вызовах C, также используется C ++.
Другие языки / системы могут помещать аргументы в стек в другом порядке, а некоторые языки / платформы даже не используют стеки и делают это по-разному. Также обратите внимание, что это не фактические строки выполнения кода C. Компилятор преобразовал их в инструкции на машинном языке в вашем исполняемом файле. Затем они (как правило) копируются из области ТЕКСТ в конвейер ЦП, затем в регистры ЦП и выполняются оттуда. [Это было неверно. Видеть Исправление Бена Фойгта ниже.]
Источник: stackovergo.com