Це дійсно залежить від системи, але сучасні ОС з віртуальною пам’яттю, як правило, завантажують свої процеси зображення та виділяють пам'ять приблизно так:
+---------+
| 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 в пам'яті, доступній лише для читання (спочатку встановлюється програміст) і т. д. Але це загальна суть.
Ти сказав:
Я також знаю, що комп'ютерна програма використовує два види пам'яті: стек і купа, які також є частиною первинної пам'яті комп'ютера.
"Стек" і "купа" - це лише абстрактні поняття, а не (обов'язково) фізично окремі "види" пам'яті.
Стек є лише останнім у, першої зі структури даних. В архітектурі x86 насправді можна вирішити випадковим чином за допомогою зміщення з кінця, але найпоширенішими функціями є PUSH та POP для додавання та видалення елементів відповідно. Він зазвичай використовується для локальних функціональних змінних (так зване "автоматичне зберігання"), аргументів функції, зворотних адрес тощо (докладніше нижче)
«Купа» це просто прізвисько шматка пам'яті , який може бути виділений на вимогу, і адресована випадковим чином (тобто, ви можете отримати доступ в будь-якому місці в ньому безпосередньо). Він зазвичай використовується для структур даних, які ви виділяєте під час виконання (у C ++, використовуючи new
та delete
, та malloc
та друзів у C тощо).
Стек і купа архітектури x86 обидва фізично перебувають у вашій системній пам'яті (ОЗП) і відображаються за допомогою розподілу віртуальної пам'яті в адресний простір процесу, як описано вище.
У регістрах ( до сих пір на 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) фактично знаходиться у вашому просторі оперативної пам’яті (у віртуальній пам’яті), і тому ним можна безпосередньо керувати; це не окремий крок під час виконання (або, принаймні, є ортогональним для процесу).
FYI, вищезгадана умова C виклику , яка також використовується C ++. Інші мови / системи можуть висунути аргументи на стек в іншому порядку, а деякі мови / платформи навіть не використовують стеки і розглядають це по-різному.
Також зауважте, це не фактичні рядки виконання коду С. Компілятор перетворив їх на інструкції машинної мови у вашому виконуваному файлі. Потім вони (як правило) копіюються з області TEXT в конвеєр процесора, потім в регістри процесора і виконуються звідти. [Це було неправильно. Див . Виправлення Бена Войта нижче.]