C로 교육 자료를 준비 중이며 일반적인 스택 모델에 맞는 예제를 원합니다.
Linux, Windows, Mac OSX (PPC 및 x86), Solaris 및 최신 Unix에서 C 스택은 어떤 방향으로 성장합니까?
답변
스택 증가는 일반적으로 운영 체제 자체가 아니라 실행중인 프로세서에 따라 다릅니다. 예를 들어 Solaris는 x86 및 SPARC에서 실행됩니다. 언급했듯이 Mac OSX는 PPC 및 x86에서 실행됩니다. Linux는 직장에서 사용하는 System z부터 작은 손목 시계 에 이르기까지 모든 것을 실행합니다 .
CPU가 모든 종류의 선택을 제공하는 경우 OS에서 사용하는 ABI / 호출 규칙은 코드가 다른 모든 사람의 코드를 호출하도록하려면 어떤 선택을해야하는지 지정합니다.
프로세서와 방향은 다음과 같습니다.
- x86 : 다운.
- SPARC : 선택 가능. 표준 ABI는 다운을 사용합니다.
- PPC : 다운 된 것 같습니다.
- System z : 링크 된 목록에서 나는 당신을 농담합니다 (그러나 적어도 zLinux에서는 여전히 다운 됨).
- ARM : 선택 가능하지만 Thumb2에는 아래로만 압축 인코딩이 있습니다 (LDMIA = 이후 증가, STMDB = 이전 감소).
- 6502 : 다운 (단 256 바이트).
- RCA 1802A : SCRT 구현에 따라 원하는 방식으로.
- PDP11 : 아래로.
- 8051 : 위로.
지난 몇 명에서 내 나이를 보여주는 1802는 초기 셔틀을 제어하는 데 사용 된 칩 (문이 열려 있는지 감지, 처리 능력을 기반으로 생각합니다.)과 두 번째 컴퓨터 인 COMX-35 ( 내 ZX80 다음 ).
PDP11 세부 사항은 여기에서 , 8051 세부 사항은 여기 에서 수집했습니다 .
SPARC 아키텍처는 슬라이딩 윈도우 레지스터 모델을 사용합니다. 구조적으로 볼 수있는 세부 정보에는 유효하고 내부적으로 캐시 된 레지스터 창의 순환 버퍼도 포함되며 오버플로 / 언더 플로시 트랩이 포함됩니다. 자세한 내용은 여기 를 참조 하십시오 . SPARCv8 설명서에서 설명하는 것처럼 SAVE 및 RESTORE 명령어는 ADD 명령어와 레지스터 창 회전과 같습니다. 일반적인 음수 대신 양수 상수를 사용하면 스택이 증가합니다.
앞서 언급 한 SCRT 기술은 또 다른 기술입니다. 1802는 SCRT (표준 호출 및 반환 기술)를 위해 일부 또는 16 비트 레지스터를 사용했습니다. 하나는 프로그램 카운터 였는데, 어떤 레지스터 든 SEP Rn
명령어 와 함께 PC로 사용할 수 있습니다 . 하나는 스택 포인터이고 두 개는 항상 SCRT 코드 주소를 가리 키도록 설정되었습니다. 하나는 호출 용이고 다른 하나는 리턴 용입니다. 특별한 방식으로 처리 된 레지스터는 없습니다 . 이러한 세부 사항은 기억에서 가져온 것이며 완전히 정확하지 않을 수 있습니다.
예를 들어 R3이 PC이고 R4가 SCRT 호출 주소이고 R5가 SCRT 반환 주소이고 R2가 “스택”(소프트웨어에서 구현 된 따옴표)이면 SEP R4
R4를 PC로 설정하고 SCRT 실행을 시작합니다. 통화 코드.
그 다음은 R2 “스택”(나는 R6이 임시 저장을 위해 사용되었다고 생각)에 R3 저장 위치를 조정하거나 아래로, R3 다음과 같은 2 바이트를 잡아, 그들을로드합니다 으로 다음 수행 R3 SEP R3
및 새 주소에서 실행.
반환하려면 SEP R5
R2 스택에서 이전 주소를 가져 오고 여기에 2 개를 추가하고 (호출의 주소 바이트를 건너 뛰기 위해) R3에로드 SEP R3
하고 이전 코드 실행을 시작합니다.
모든 6502 / 6809 / z80 스택 기반 코드 이후 처음에는 머리를 감싸기가 매우 어렵지만 벽에 머리를 대는 방식에서는 여전히 우아합니다. 또한 칩의 가장 큰 판매 기능 중 하나는 16 비트 레지스터의 전체 제품군이었습니다 (SCRT의 경우 5 개, DMA의 경우 2 개, 메모리에서 인터럽트). 아, 현실보다 마케팅의 승리 🙂
System z는 실제로 호출 / 반환에 R14 및 R15 레지스터를 사용하여 매우 유사합니다.
답변
C ++에서 (C에 적용 가능) stack.cc :
static int
find_stack_direction ()
{
static char *addr = 0;
auto char dummy;
if (addr == 0)
{
addr = &dummy;
return find_stack_direction ();
}
else
{
return ((&dummy > addr) ? 1 : -1);
}
}
답변
성장하는 것의 장점은 스택이 일반적으로 메모리의 맨 위에 있었다는 오래된 시스템에서입니다. 프로그램은 일반적으로 맨 아래부터 시작하여 메모리를 채웠으므로 이러한 종류의 메모리 관리로 인해 스택 맨 아래를 적절한 곳에 측정하고 배치 할 필요성이 최소화되었습니다.
답변
스택은 x86에서 감소합니다 (아키텍처에 의해 정의 됨, pop 증가 스택 포인터, 푸시 감소).
답변
MIPS 및 많은 최신 RISC 아키텍처 (예 : PowerPC, RISC-V, SPARC …)에는 push
및 pop
지침 이 없습니다 . 이러한 작업은 스택 포인터를 수동으로 조정 한 다음 조정 된 포인터에 상대적으로 값을로드 / 저장하여 명시 적으로 수행됩니다. 모든 레지스터 (제로 레지스터 제외)는 범용이므로 이론적으로 모든 레지스터 는 스택 포인터가 될 수 있으며 스택 은 프로그래머가 원하는 모든 방향으로 성장할 수 있습니다.
즉, 스택은 일반적으로 대부분의 아키텍처에서 성장하여 스택과 프로그램 데이터 또는 힙 데이터가 증가하여 서로 충돌하는 경우를 방지하기 위해 증가합니다. sh-의 대답에 대해 언급 한 훌륭한 주소 지정 이유도 있습니다 . 몇 가지 예 : MIPS ABI는 아래쪽으로 성장 하고 스택 포인터로 $29
(AKA $sp
)를 사용하고, RISC-V ABI는 아래쪽으로 성장하고 x2를 스택 포인터로 사용합니다.
Intel 8051에서는 메모리 공간이 너무 작아서 (원래 버전에서는 128 바이트) 힙이 없어서 스택이 늘어나고 힙이 늘어나는 것과 분리되도록 스택을 맨 위에 놓을 필요가 없기 때문에 스택이 커집니다. 바닥에서
https://en.wikipedia.org/wiki/Calling_convention 에서 다양한 아키텍처의 스택 사용에 대한 자세한 정보를 찾을 수 있습니다.
또한보십시오
답변
내가 볼 수있는 한이 점을 건드리지 않은 다른 답변에 약간의 추가 사항이 있습니다.
스택이 아래쪽으로 커지면 스택 내의 모든 주소가 스택 포인터에 상대적인 양의 오프셋을 갖게됩니다. 사용되지 않은 스택 공간 만 가리 키기 때문에 음수 오프셋이 필요하지 않습니다. 이는 프로세서가 스택 포인터 관련 주소 지정을 지원할 때 스택 위치에 대한 액세스를 단순화합니다.
많은 프로세서에는 일부 레지스터에 상대적인 양수 전용 오프셋으로 액세스를 허용하는 명령이 있습니다. 여기에는 많은 현대 건축과 오래된 건축이 포함됩니다. 예를 들어 ARM Thumb ABI는 단일 16 비트 명령어 내에서 인코딩 된 양의 오프셋을 사용하여 스택 포인터 상대 액세스를 제공합니다.
스택이 위로 커지면 스택 포인터와 관련된 모든 유용한 오프셋은 음수가되어 덜 직관적이고 덜 편리합니다. 또한 구조체의 필드에 액세스하기위한 레지스터 관련 주소 지정의 다른 응용 프로그램과도 상충됩니다.
답변
대부분의 시스템에서 스택은 성장하고 https://gist.github.com/cpq/8598782의 내 기사 는 왜 성장하는지 설명합니다. 간단합니다. 증가하는 두 개의 메모리 블록 (힙 및 스택)을 고정 된 메모리 청크에 배치하는 방법은 무엇입니까? 가장 좋은 해결책은 그것들을 반대쪽 끝에 놓고 서로를 향해 자라게하는 것입니다.