본문 바로가기

서버 관리/리눅스

리눅스. 스택 크기에 관한 고찰

[1. 개요]

Segmentation fault 와 관련하여 고민한 내용 정리


[2. 상황]

특정 함수 진입 시, c++ 로 작성한 애플리케이션이 segmentation fault 발생과 함께 죽는 현상.

gdb 로 디버깅 시, 함수 진입 만 하였는데, segmentation fault 가 발생.

 

의심이 것 중 하나는 스택 오버플로우..


[3. 예제]

     1  #include <iostream>
     2
     3
     4  void myfunc()
     5  {
     6          char arr[MYSZ * 1024 * 1024];
     7          std::cout << "arr size is " << sizeof(arr) << std::endl;
     8  }
     9
    10  int main()
    11  {
    12          std::cout << "main start" << std::endl;
    13          myfunc();
    14          return 0;
    15  }

 

ulimit -s 가 8192 (8 Mbyte 인 경우)

  1. g++ main.cpp -g -DMYSZ=9
  2. g++ main.cpp -Wl,-z,stack-size=10485760 -g -DMYSZ=9

위 두가지 모두 segmentation fault 가 발생

 

 

ulmit -s 16384 로 수정 (16 Mbyte 인 경우)

  1. g++ main.cpp -g -DMYSZ=9
  2. g++ main.cpp -Wl,-z,stack-size=10485760 -g -DMYSZ=9

위 두가지 모두 정상 종료 확인.

 

즉, 컴파일 시 링커에 명시하는 스택 크기보다, ulimit 값에 더 의존적임.


[3. 스레드 스택]

#include <iostream>
#include <pthread.h>

int main()
{
        std::cout << "main start" << std::endl;

        pthread_attr_t attr;
        void* stack_addr;
        size_t stack_size;

        pthread_getattr_np(pthread_self(), &attr);
        pthread_attr_getstack(&attr, &stack_addr, &stack_size);

        std::cout << "Stack addr " << stack_addr << std::endl;
        std::cout << "Stack size " << stack_size << std::endl;


        return 0;
}

 

위 코드 역시, 스택의 크기는 ulimit 값에 따라 바뀌 었음.


[4. 결론. (chatgpt)]

ulimit -s 전체 프로세스의 일괄 적용 각 스레드 실행 시 기본 스택
-Wl,-z,stack-size 실행 파일 ELF 헤더에 스택 크기 저장
pthread_attr_setstack 프로세스 내 스레드 특정 스레드의 스택 크기 지정

 

즉, pthread_attr_setstack 등으로 스택 크기를 명시하지 않으면

  • ulimit -s 에 의해 명시된 스택 크기를 스레드가 사용하게 된다.
  • 개별 스택 크기는 ulimit -s 값 이하로만 설정 가능.

문제는 실제 사용하는 스택 메모리가 예상 한 것보다 더 많이 사용할 수 있다.

  • 재귀 함수 호출.
  • 큰 지역 변수 사용 (매우 긴 배열..)
  • ...

런타임 중, 스레드의 스택 오버플로우 발생 가능성이 있음.

=> pthread를 사용하면, 리눅스 커널이 자동으로 각 스레드의 스택 끝에 읽기/쓰기가 불가능한 페이지를 추가함. 

=> 이 덕분에, 스택이 넘쳐도 다른 스레드의 영역을 침범하지 않고 바로 프로그램이 크래시됨.

=> pthread_attr_setguardsize(&attr, 0);  // 가드 페이지 제거 한다면?