실제로 시스템에 따라 다르지만 가상 메모리가있는 최신 OS 는 프로세스 이미지를로드하고 다음과 같은 메모리를 할당하는 경향이 있습니다.
+---------+
| 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에 대한 자세한 내용은 아래 Billy O'Neal의 답변 참조).
다른 시스템은 매우 다를 수 있습니다 . 예를 들어 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에서 보호없이 운영 체제 메모리에 직접 액세스 할 수 있다는 것을 알 수 있습니다. 이는 사용자 공간 프로그램이 일반적으로 원하는 것을 직접 액세스하거나 덮어 쓸 수 있음을 의미합니다.
그러나 프로세스 주소 공간에서 프로그램은 비슷해 보였으며 코드 세그먼트, 데이터 세그먼트, 힙, 스택 세그먼트 등으로 설명되었으며 약간 다르게 매핑되었습니다. 그러나 대부분의 일반 지역은 여전히있었습니다.
프로그램과 필요한 공유 라이브러리를 메모리에로드하고 프로그램의 일부를 올바른 영역에 배포하면 OS는 기본 방법이있는 곳마다 프로세스를 실행하기 시작하고 프로그램이 거기에서 인계하여 필요한 경우 시스템 호출을 수행합니다 그것들이 필요합니다.
임베디드 시스템은 스택리스 시스템, 하버드 아키텍처 시스템 (코드 및 데이터가 별도의 물리적 메모리에 보관 됨), 실제로 BSS를 읽기 전용 메모리에 유지하는 시스템 (처음에는 프로그래머) 등입니다. 그러나 이것은 일반적인 요지입니다.
당신은 말했다 :
또한 컴퓨터 프로그램은 두 가지 종류의 메모리를 사용한다는 것을 알고 있습니다. 스택과 힙은 컴퓨터의 기본 메모리의 일부이기도합니다.
"스택"과 "힙"은 물리적으로 별개의 "종류"의 메모리가 아니라 추상적 인 개념 일뿐입니다.
스택은 단지 후입 선출 데이터 구조이다. x86 아키텍처에서는 실제로 끝에서 오프셋을 사용하여 임의로 주소를 지정할 수 있지만 가장 일반적인 기능은 각각 PUSH 및 POP로 항목을 추가 및 제거하는 것입니다. 함수 로컬 변수 (소위 "자동 스토리지"), 함수 인수, 리턴 주소 등에 일반적으로 사용됩니다 (자세한 내용은 아래 참조).
"힙" 단지 수요에 할당 할 수있는 메모리 청크의 별명이며, (당신이 직접에서 어떤 위치에 액세스 할 수 있습니다 의미) 무작위로 해결된다. 그것은 일반적으로 런타임에 할당하는 것이 데이터 구조에 사용 (C ++에서 사용 new
하고 delete
, 그리고 malloc
와 C의 친구 등).
x86 아키텍처의 스택 및 힙은 물리적으로 시스템 메모리 (RAM)에 상주하며 위에서 설명한대로 가상 메모리 할당을 통해 프로세스 주소 공간으로 매핑됩니다.
레지스터 (계속 86의) CPU의 지시에 따라, 물리적으로 프로세서 (RAM 반대) 안에 상주하고, 텍스트 영역에서, 프로세서에 의해로드 된 (그리고 메모리 또는 다른 장소에서 다른 장소로부터로드 될 수있는 실제로 실행됩니다). 그것들은 본질적으로 매우 작고 매우 빠른 온칩 메모리 위치이며 여러 다른 목적으로 사용됩니다.
레지스터 레이아웃은 아키텍처 (실제로 레지스터, 명령어 세트 및 메모리 레이아웃 / 디자인, 정확히 "아키텍처"가 의미하는 것)에 크게 의존하므로 확장하지는 않지만 권장합니다. 더 잘 이해하기 위해 어셈블리 언어 코스.
당신의 질문 :
명령어 실행에 스택이 사용되는 시점은 언제입니까? RAM에서 스택, 레지스터로 명령이 전달됩니까?
스택 (가지고 있고 사용하는 시스템 / 언어)은 다음과 같이 가장 자주 사용됩니다.
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 코드 실행 행이 아닙니다. 컴파일러가이를 실행 파일에서 기계 언어 명령어로 변환했습니다. 그런 다음 TEXT 영역에서 CPU 파이프 라인으로, 그 다음 CPU 레지스터로 (일반적으로) 복사되어 거기서 실행됩니다. [이것은 틀렸다. 아래의 Ben Voigt의 수정 사항을 참조하십시오.]