"자동 스택 확장"이란 무엇입니까?


13

getrlimit (2) 는 매뉴얼 페이지에서 다음 정의를 갖습니다.

RLIMIT_AS 프로세스의 가상 메모리 (주소 공간)의 최대 크기 (바이트). 이 제한은 brk (2), mmap (2) 및 mremap (2) 호출에 영향을 미치며이 제한을 초과하면 오류 ENOMEM과 함께 실패합니다. 또한 자동 스택 확장 이 실패합니다 (sigaltstack (2)를 통해 대체 스택을 사용할 수없는 경우 프로세스를 종료하는 SIGSEGV 생성). 값이 길기 때문에 길이가 32 비트 인 시스템에서는이 제한이 최대 2GiB이거나이 리소스가 무제한입니다.

여기서 "자동 스택 확장"이란 무엇입니까? Linux / UNIX 환경의 스택이 필요에 따라 증가합니까? 그렇다면 정확한 메커니즘은 무엇입니까?

답변:


1

예, 스택이 동적으로 커집니다. 스택은 메모리 상단에 있으며 힙쪽으로 아래쪽으로 자랍니다.

--------------
| Stack      |
--------------
| Free memory|
--------------
| Heap       |
--------------
     .
     .

malloc을 할 때마다 힙이 위쪽으로 커지고 새 함수가 호출 될 때마다 스택이 아래쪽으로 커집니다. 힙은 프로그램의 BSS 섹션 바로 위에 있습니다. 즉, 프로그램 크기와 메모리를 힙으로 할당하는 방식도 해당 프로세스의 최대 스택 크기에 영향을 미칩니다. 일반적으로 스택 크기는 무제한입니다.

이것은 사용자 프로세스에만 해당됩니다. 커널 스택은 항상 고정되어 있습니다 (보통 8KB).


"보통 스택 크기는 무제한입니다."아니요, 일반적으로 8Mb ( ulimit -s)로 제한됩니다 .
Eddy_Em

예, 대부분의 시스템에서 옳습니다. 쉘의 ulimit 명령을 점검 중입니다. 스택 크기에 대한 하드 한계가 있으면 무제한입니다 (ulimit -Hs). 어쨌든 그 요점은 스택과 힙이 반대 방향으로 자라는 것입니다.
Santosh

그렇다면 "자동 확장"은 "요소를 스택에 푸시"와 어떻게 다릅니 까? 당신의 설명에서, 나는 그들이 같은 느낌을 얻습니다. 또한 스택 및 힙의 시작점이 8MB 이상인 것처럼 느껴지므로 스택이 필요한만큼 커질 수 있습니다 (또는 힙에 부딪힘). 그게 사실입니까? 그렇다면 운영 체제는 힙과 스택을 배치 할 위치를 어떻게 결정 했습니까?
loudandclear

그것들은 동일하지만 rlimit를 사용하여 크기를 엄격하게 제한하지 않는 한 스택의 크기는 고정되어 있지 않습니다. 스택은 가상 메모리 영역의 끝에 배치되고 힙은 실행 파일의 데이터 세그먼트에 즉시 영향을줍니다.
Santosh

이해합니다, 고마워요 그러나 "스택의 크기가 고정되어 있지 않습니다"라고 생각하지 않습니다. 이 경우 8Mb 소프트 한계는 얼마입니까?
loudandclear

8

정확한 메커니즘은 리눅스, 여기에 주어진 :에 익명 매핑에 페이지 오류를 처리 하면 그것은 "어른이 할당 않는다"인지 확인 당신이 스택처럼 확대해야한다고는. VM 영역 레코드에 필요한 것으로 표시되면 시작 주소를 조정하여 스택을 확장합니다.

페이지 오류가 발생하면 주소에 따라 스택 확장을 통해 페이지 오류가 처리 될 수 있습니다. 가상 메모리에 대한 이러한 "오류 발생시 아래쪽으로 증가"동작은 임의의 사용자 프로그램이 MAP_GROWSDOWN플래그를 mmapsyscall 에 전달하여 요청할 수 있습니다 .

사용자 프로그램에서도이 메커니즘을 망칠 수 있습니다.

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>

int main() {
        long page_size = sysconf(_SC_PAGE_SIZE);
        void *mem = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_GROWSDOWN|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
        if (MAP_FAILED == mem) {
                perror("failed to create growsdown mapping");
                return EXIT_FAILURE;
        }

        volatile char *tos = (char *) mem + page_size;

        int i;
        for (i = 1; i < 10 * page_size; ++i)
                tos[-i] = 42;

        fprintf(stderr, "inspect mappping for originally page-sized %p in /proc... press any key to continue...\n", mem);
        (void) getchar();

        if (munmap(mem, page_size))
                perror("failed munmap");

        return EXIT_SUCCESS;
}

메시지가 표시되면 (을 통해 ps) 프로그램의 pid를 찾고 /proc/$THAT_PID/maps원래 영역이 어떻게 성장했는지 확인하십시오.


메모리 영역이 MAP_GROWSDOWN을 통해 커져도 원래 mem 및 page_size에 대해 munmap을 호출해도됩니까? 그렇지 않으면 사용하기가 매우 복잡한 API이기 때문에 예라고 생각합니다. 그러나이 문서에서는이 문제에 대해 명시 적으로 언급하지 않습니다
i.petruk

2
MAP_GROWSDOWN은 사용하지 않아야하며 glibc에서 제거되었습니다 ( lwn.net/Articles/294001 참조 ).
Collin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.