메인스트림 OS에서 프로그램의 C/C++ 최대 스택 크기
100 X 100 어레이로 DFS를 실행하고 싶다.(어레이의 요소가 그래프노드를 나타냄) 따라서 최악의 경우 재귀함수 호출의 깊이는 10000까지 올라가 각 호출에 20바이트가 소요될 수 있습니다.그러면 스택 오버플로우 가능성이 있다는 의미일까요?
C/C++에서 스택의 최대 사이즈는 어떻게 됩니까?
다 gcc에 대해 하십시오.
1) Windows의 cygwin
2) Unix
일반적인 제한은 어느 정도입니까?
Visual Studio에서는 기본 스택사이즈는 1MB라고 생각합니다.따라서 재귀깊이가 10,000일 경우 각 스택프레임은 최대 100바이트로 DFS 알고리즘에 충분합니다.
Visual Studio를 포함한 대부분의 컴파일러에서는 스택 크기를 지정할 수 있습니다.일부 Linux에서는 스택 크기가 실행 파일의 일부가 아니라 OS의 환경 변수입니다. 크기를 하실 수 .ulimit -s
, 새로운 합니다.ulimit -s 16384
.
다음은 gcc의 기본 스택 크기를 가진 링크입니다.
DFS(재귀 없음):
std::stack<Node> dfs;
dfs.push(start);
do {
Node top = dfs.top();
if (top is what we are looking for) {
break;
}
dfs.pop();
for (outgoing nodes from top) {
dfs.push(outgoing node);
}
} while (!dfs.empty())
스레드용 스택은 종종 더 작습니다.연결 시 기본값을 변경하거나 실행 시 변경할 수도 있습니다.참고로 다음과 같은 기본값이 있습니다.
- glibc i386, x86_64: 7.4 MB
- Tru64 5.1: 5.2 MB
- Cygwin: 1.8 MB
- Solaris 7..10: 1 MB
- MacOS X 10.5: 460 KB
- AIX 5: 98 KB
- OpenBSD 4.0: 64 KB
- HP-UX 11: 16 KB
플랫폼 의존, 툴 체인 의존, ulimit 의존, 파라미터 의존...이것은 전혀 지정되어 있지 않고, 영향을 줄 수 있는 많은 정적 및 동적 속성이 있습니다.
작업 중인 스택이 얼마 남지 않았습니다.데이터베이스에서 몇 개의 스레드를 실행하고 있었습니다.기본적으로 이전 개발자는 스택에 큰 어레이를 배치했습니다만, 어쨌든 스택은 적었습니다.이 소프트웨어는 Microsoft Visual Studio 2015를 사용하여 컴파일되었습니다.
스레드가 스택을 다 사용했는데도 자동으로 장애가 발생하여 계속 진행되었지만 스택 상의 데이터 내용에 액세스할 때 스택 오버플로우만 발생하였습니다.
가장 좋은 조언은 스택상의 어레이를 선언하지 않는 것입니다.특히 복잡한 애플리케이션이나 스레드에서는 히프를 사용하는 것이 좋습니다.그게 바로 그 이유죠;)
또, 스택을 선언했을 때에 곧바로 장해가 발생하는 것이 아니고, 액세스시에만 장해가 발생하는 것에 주의해 주세요.내 추측으로는 컴파일러는 윈도우에서 스택을 "낙관적으로" 선언할 것입니다. 즉, 스택이 선언되고 사용하기에 충분한 크기가 될 때까지 스택이 존재하지 않는 것을 발견할 것입니다.
운영 체제에 따라 스택 선언 정책이 다를 수 있습니다.어떤 정책인지 아시는 분은 댓글로 남겨주세요.
네, 스택 오버플로우 가능성이 있습니다.C 및 C++ 규격에서는 스택의 깊이 등은 일반적으로 환경적인 문제입니다.
대부분의 적절한 개발 환경 및/또는 운영 체제에서는 링크 시 또는 로드 시 프로세스의 스택 크기를 조정할 수 있습니다.
대상 지원을 위해 사용하는 OS 및 개발 환경을 지정해야 합니다.
예를 들어 Ubuntu Karmic Coala에서 gcc의 기본값은 2M 예약 및 4K 커밋입니다만, 이것은 프로그램을 링크하면 변경할 수 있습니다. 하다를 사용하세요.--stack
" " " "ld
네, 그렇습니다.
(2020년 9월 26일 추가)
2009년 10월 24일 @pixelbeat에서 처음 지적한 바와 같이 Bruno Haible은 여러 시스템에서 다음과 같은 기본 스레드 스택 크기를 실증적으로 발견했습니다.그는 멀티스레드 프로그램에서 "기본 스레드 스택 크기는 다음과 같습니다."라고 말했습니다.
- glibc i386, x86_64 7.4 MB - Tru64 5.1 5.2 MB - Cygwin 1.8 MB - Solaris 7..10 1 MB - MacOS X 10.5 460 KB - AIX 5 98 KB - OpenBSD 4.0 64 KB - HP-UX 11 16 KB
위의 단위는 모두 MiB 및 KiB(기본 1024 번호)가 아닌 MB 및 KB(기본 1000 번호)입니다.저는 7.4MB 사건을 검증함으로써 이것을 스스로 증명했습니다.
또한 다음과 같이 언급하였다.
32KB는 멀티스레드 프로그램에서 스택에 안전하게 할당할 수 있는 용량보다 큽니다.
그리고 그는 말했다:
sigaltstack의 기본 스택사이즈 SIGSTKSZ는
- 일부 플랫폼(IRIX, OSF/1, Haiku)에서는 16KB에 불과합니다.
- glibc, NetBSD, OpenBSD, HP-UX, Solaris 일부 플랫폼에서는 8KB에 불과합니다.
- 일부 플랫폼에서는 4KB만 사용할 수 있습니다. AIX
브루노
그는 위의 값을 경험적으로 결정하기 위해 다음과 같은 간단한 Linux C 프로그램을 작성했습니다.지금 바로 시스템에서 실행함으로써 최대 스레드 스택 크기를 빠르게 확인할 수 있습니다.또한 https://onlinegdb.com/rkO9JnaHD의 GDBOnline에서 온라인으로 실행할 수도 있습니다.
설명:그렇게로, 경우를 대비해 다른에서, 그리고 그것은 thread 반복적으로 스택(NOT승자)에 이 새로운 메모리 블록의 첫번째 바이트기 위해서 어떻게 출력한 0을 리눅스 alloca()전화를 사용하여 기억의 128바이트를 할당하고 있는 스레드 스택 크기 및 NOT은 프로그램 스택 크기 확인하는 것이 단순히, 단순한 새 스레드를 만듭니다. 많은할당되어 있는 tal 바이트.이 프로세스를 반복하여 스택에 128바이트를 할당할 때마다 프로그램이 크래시하여Segmentation fault (core dumped)
오류. 마지막으로 출력된 값은 시스템에서 허용되는 최대 스레드 스택 크기입니다.
주의: 스택에 할당: 이는 힙에 대한 동적 메모리 할당처럼 보이지만malloc()
불러,alloca()
는 힙에 동적으로 할당되지 않습니다.오히려.alloca()
는 정적으로 할당된 메모리인 것처럼 스택에 직접 할당하기 위한 특별한 Linux 함수입니다.사용 및 반환된 스택 메모리alloca()
기능 레벨로 스코프가 설정되어 있기 때문에, 「호출한 함수에 의해서 자동적으로 해방됩니다.alloca()
발신자에게 돌아옵니다.그렇기 때문에 스태틱스코프가 종료되지 않고 메모리가 할당됩니다.alloca()
매번 해방되지 않습니다.for
루프의 반복이 완료되어 루프가 종료됩니다.for
루프 범위에 도달했습니다.자세한 것은, 을 참조해 주세요.다음은 관련 인용문입니다(강조 추가).
묘사
그alloca()
함수는 발신자의 스택프레임 내의 공간 크기 바이트를 할당합니다.이 임시 공간은 이 함수가 호출할 때 자동으로 해방됩니다.alloca()
는 발신자에게 돌아옵니다.★★★★★★
alloca()
함수는 할당된 공간의 선두로 포인터를 반환합니다.할당으로 인해 스택 오버플로가 발생할 경우 프로그램 동작은 정의되지 않습니다.
다음은 2009년 10월 24일 이후 Bruno Haible의 프로그램으로, GNU 메일링 리스트에서 직접 복사한 것입니다.
여기서도 온라인으로 실행할 수 있습니다.
// By Bruno Haible
// 24 Oct. 2009
// Source: https://lists.gnu.org/archive/html/bug-coreutils/2009-10/msg00262.html
// =============== Program for determining the default thread stack size =========
#include <alloca.h>
#include <pthread.h>
#include <stdio.h>
void* threadfunc (void*p) {
int n = 0;
for (;;) {
printf("Allocated %d bytes\n", n);
fflush(stdout);
n += 128;
*((volatile char *) alloca(128)) = 0;
}
}
int main()
{
pthread_t thread;
pthread_create(&thread, NULL, threadfunc, NULL);
for (;;) {}
}
위의 링크를 사용하여 GDBOnline에서 실행하면 매번 C와 C++17 프로그램과 동일한 결과를 얻을 수 있습니다.약 10초 정도 걸립니다.출력의 마지막 몇 행은 다음과 같습니다.
Allocated 7449856 bytes Allocated 7449984 bytes Allocated 7450112 bytes Allocated 7450240 bytes Allocated 7450368 bytes Allocated 7450496 bytes Allocated 7450624 bytes Allocated 7450752 bytes Allocated 7450880 bytes Segmentation fault (core dumped)
따라서 이 시스템의 스레드스택 사이즈는 Bruno가 전술한 바와 같이7.45 MB까지입니다
이 프로그램에 몇 가지 변경을 가했습니다.대부분 명확성뿐만 아니라 효율성 및 학습에 대해서도 약간 개선했습니다.
변경 내용 요약:
BYTES_TO_ALLOCATE_EACH_LOOP
의threadfunc()
인 '사용 연습'을 사용하여 으로 하고 있습니다.void*
C' 수 c- 참고: 콜백 기능에 필요한 기능 프로토타입이기도 합니다.
threadfunc()
로 넘어갑니다.pthread_create()
https://www.man7.org/linux/man-pages/man3/pthread_create.3.html 를 참조해 주세요.
- 참고: 콜백 기능에 필요한 기능 프로토타입이기도 합니다.
[효율성] 헛되이 돌아가는 것이 아니라 메인 실을 sleep으로 만들었습니다.
[ [명확성]하다, 하다, 하다, 하다, 등 좀 더 변수
BYTES_TO_ALLOCATE_EACH_LOOP
★★★★★★★★★★★★★★★★★」bytes_allocated
.[클리어] 이걸 바꿨어요
*((volatile char *) alloca(128)) = 0;
다음과 같이 입력합니다.
volatile uint8_t * byte_buff = (volatile uint8_t *)alloca(BYTES_TO_ALLOCATE_EACH_LOOP); byte_buff[0] = 0;
다음은 Bruno와 완전히 동일한 기능을 수행하며 동일한 결과를 얻을 수 있는 수정된 테스트 프로그램입니다.
여기서 온라인으로 실행하거나 제 리포트에서 다운로드할 수 있습니다.내 repo에서 로컬로 실행하도록 선택한 경우 테스트에 사용한 빌드 및 실행 명령은 다음과 같습니다.
C 프로그램으로 빌드 및 실행:
mkdir -p bin && \ gcc -Wall -Werror -g3 -O3 -std=c11 -pthread -o bin/tmp \ onlinegdb--empirically_determine_max_thread_stack_size_GS_version.c && \ time bin/tmp
C++ 프로그램으로 빌드하여 실행합니다.
mkdir -p bin && \ g++ -Wall -Werror -g3 -O3 -std=c++17 -pthread -o bin/tmp \ onlinegdb--empirically_determine_max_thread_stack_size_GS_version.c && \ time bin/tmp
스레드 스택사이즈가 약 7.4 MB인 고속 컴퓨터에서 로컬로 실행하는데 걸리는 시간은 0.5초 미만입니다.
프로그램은 다음과 같습니다.
// =============== Program for determining the default thread stack size =========
// Modified by Gabriel Staples, 26 Sept. 2020
// Originally by Bruno Haible
// 24 Oct. 2009
// Source: https://lists.gnu.org/archive/html/bug-coreutils/2009-10/msg00262.html
#include <alloca.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h> // sleep
/// Thread function to repeatedly allocate memory within a thread, printing
/// the total memory allocated each time, until the program crashes. The last
/// value printed before the crash indicates how big a thread's stack size is.
///
/// Note: passing in a `uint32_t` as a `void *` type here is for practice,
/// to learn how to pass in ANY type to a func by using a `void *` parameter.
/// This is also the required function prototype, as required by the
/// `pthread_create()` function, for the callback function (this function)
/// passed to `pthread_create()`. See:
/// https://www.man7.org/linux/man-pages/man3/pthread_create.3.html
void* threadfunc(void* bytes_to_allocate_each_loop)
{
const uint32_t BYTES_TO_ALLOCATE_EACH_LOOP =
*(uint32_t*)bytes_to_allocate_each_loop;
uint32_t bytes_allocated = 0;
while (true)
{
printf("bytes_allocated = %u\n", bytes_allocated);
fflush(stdout);
// NB: it appears that you don't necessarily need `volatile` here,
// but you DO definitely need to actually use (ex: write to) the
// memory allocated by `alloca()`, as we do below, or else the
// `alloca()` call does seem to get optimized out on some systems,
// making this whole program just run infinitely forever without
// ever hitting the expected segmentation fault.
volatile uint8_t * byte_buff =
(volatile uint8_t *)alloca(BYTES_TO_ALLOCATE_EACH_LOOP);
byte_buff[0] = 0;
bytes_allocated += BYTES_TO_ALLOCATE_EACH_LOOP;
}
}
int main()
{
const uint32_t BYTES_TO_ALLOCATE_EACH_LOOP = 128;
pthread_t thread;
pthread_create(&thread, NULL, threadfunc,
(void*)(&BYTES_TO_ALLOCATE_EACH_LOOP));
while (true)
{
const unsigned int SLEEP_SEC = 10000;
sleep(SLEEP_SEC);
}
return 0;
}
샘플 출력(Bruno Haible의 원래 프로그램과 동일한 결과):
bytes_allocated = 7450240 bytes_allocated = 7450368 bytes_allocated = 7450496 bytes_allocated = 7450624 bytes_allocated = 7450752 bytes_allocated = 7450880 Segmentation fault (core dumped)
직사각형 배열에 대해 깊이 우선 검색을 수행한다는 것이 무엇을 의미하는지 잘 모르겠습니다만, 무엇을 하고 있는지 알고 있을 것입니다.
스택 제한에 문제가 있는 경우 재귀 솔루션을 반복 솔루션으로 변환하여 중간 값을 힙에서 할당된 스택에 푸시할 수 있어야 합니다.
언급URL : https://stackoverflow.com/questions/1825964/c-c-maximum-stack-size-of-program-on-mainstream-oses
'itsource' 카테고리의 다른 글
농담으로 "이" 객체에 대한 메서드를 조롱하다 (0) | 2022.07.17 |
---|---|
수집되지 않은 참조 오류: 버스가 정의되지 않았습니까?(v.1992) (0) | 2022.07.17 |
Visual Studio에서의 C 프로그래밍 (0) | 2022.07.17 |
Vue 클래스 구성 요소, Vuex 클래스를 사용하는 경우의 ESLint 오류 (0) | 2022.07.17 |
LD_PRELOAD 트릭이란 무엇입니까? (0) | 2022.07.17 |